Implementacja zdarzenia OnMouseMove w komponencie tworzonym dynamicznie

0

Implementacja zdarzenia OnMouseMove w komponencie tworzonym dynamicznie niestety nie powiodła się. W wierszu 46 kompilator zgłasza błędy (jak niżej w kodzie). Może ktoś wie w czym tkwi błąd :) ?

unit Unit2;
interface

{...}

var
  Form2: TForm2;
 
 DynamicImage :TImage;
 procedure DynamicImageCreate;
 procedure DynamicImageMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);

implementation

{$R *.dfm}

 procedure DynamicImageMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
 begin
   Form2.Caption:=IntToStr(X)+'  '+InttoStr(Y);
 end;

 procedure DynamicImageCreate;
begin
if Assigned(DynamicImage) then
DynamicImage.Free();
      DynamicImage:=TImage.Create(Form2);
      With DynamicImage do
      begin
 //         DynamicImage.Name:=DImage1;
          Top:=0;
          Left:=0;
          Width:=400;
         parent:=Form2;
          Height:=600;
          visible:=True;
    
    DynamicImage.OnMouseMove:=
    DynamicImageMouseMove(Sender: TObject; Shift: TShiftState; X,  Y: Integer); //Linia 46

> //[DCC Error] Unit2.pas(46): E2003 Undeclared identifier: 'Sender'
> //[DCC Error] Unit2.pas(46): E2014 Statement expected, but expression of type 'class of TObject' found
> //[DCC Error] Unit2.pas(46): E2029 '(' expected but ';' found


end;
end;
end;

{...}

end.
1
DynamicImage.OnMouseMove := DynamicImageMouseMove;

Tak wygląda przypisanie własnej metody do zdarzenia. Tyle tylko, że do zdarzenia bezpośrednio można przypisać wyłącznie metodę, czyli procedurę będącą składową jakiejś klasy (jakiejkolwiek), bo deklaracja typu danego zdarzenia ma końcówkę of object.

Co prawda można to obejść, ale tego zwykle się nie robi. Obejście wygląda w ten sposób:

// zwykła procedura z parametrami zgodnymi z typem zdarzenia "OnMouseMove"
procedure FormMouseMove(ASelf, ASender: TObject; AShift: TShiftState; AX, AY: Integer);
begin
  TForm(ASender).Caption := 'X: %d, Y: %d'.Format([AX, AY]);
end;

// zdarzenie utworzenia formularza
procedure TForm1.FormCreate(ASender: TObject);
var
  Method: TMethod;
begin
  // wypełniamy strukturkę odpowiednimi danymi
  Method.Data := Self;            // wskaźnik przekazywany do naszej procedury jako "Sender"
  Method.Code := @FormMouseMove;  // wskaźnik na procedurę wykonującą kod zdarzenia

  // przypisanie struktury do zdarzenia, wykonując rzutowanie struktury na typ zdarzenia
  OnMouseMove := TMouseMoveEvent(Method);
end;

Działa to zarówno we Free Pascalu, jak i w Delphi (przykład testowany w Lazarusie).

0

Jeśli tak zrobię jak napisałeś to mam zgłoszony taki błąd:

   DynamicImage.OnMouseMove:=DynamicImageMouseMove;//Linia 51
  //[DCC Error] Unit2.pas(51): E2009 Incompatible types: 'method pointer and regular procedure'
0

Tak jak pisałem wyżej — próbujesz przypisać do zdarzenia procedurę, a powinieneś metodę. Jeśli koniecznie musisz przypisać zwykłą procedurę, to skorzystaj z obejścia, jakie podałem w poprzednim swoim poście. A jeśli nie, to zadeklaruj w jakiejkolwiek klasie metodę zgodną parametrami z typem zdarzenia OnMouseMove i ją przypisz.

0

A jakby wyglądało zadeklarowanie metody do której miałbym później utworzyć przypisanie w dynamicznym
komponencie "DynamicImage :TImage;" bo nie wiem :) ?

2

Coś takiego jak robisz nie ma za bardzo sensu, bo robi się bajzel w module — te zmienne i procedury globalne są paskudztwem. Jeśli kontrolka powinna zawierać własnoręcznie zdefiniowane metody, to przecież nic nie stoi na przeszkodzie, aby sobie to wszystko opakować w nową klasę, dziedziczącą z TImage. Dzięki temu wszystko będzie w jednym miejscu, a każda utworzona dynamicznie kontrolka będzie miała wszystko swoje i opakowane, więc niedostępne z zewnątrz. No i — co najważniejsze — odpadnie problem z przypisywaniem metody do zdarzenia.

Wystarczy na górze modułu zadeklarować nową klasę:

type
  TDynamicImage = class(TImage)
  private
    procedure ImageMouseMove(ASender: TObject; AShift: TShiftState; AX, AY: Integer);
  public
    constructor Create(AOwner: TComponent); override;
  end;

i w konstruktorze kontrolki dodać przypisanie tej metody do zdarzenia:

procedure TDynamicImage.ImageMouseMove(ASender: TObject; AShift: TShiftState; AX, AY: Integer);
begin
  // ASender zawiera referencję dynamicznego TImage
end;

constructor TDynamicImage.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  OnMouseMove := @ImageMouseMove;
end;

No i to tyle — teraz wystarczy stworzyć kontrolkę w standardowy sposób, używając własnej klasy:

DynamicImage := TDynamicImage.Create(SomeParent);

i kontrolka już będzie miała przypisaną metodę do zdarzenia OnMouseMove. Niżej masz pełny przykład:

type
  TDynamicImage = class(TImage)
  private
    procedure ImageMouseMove(ASender: TObject; AShift: TShiftState; AX, AY: Integer);
  public
    constructor Create(AOwner: TComponent); override;
  end;

type
  TForm1 = class(TForm)
    procedure FormCreate(ASender: TObject);
  private
    DynamicImage: TDynamicImage;
  end;

{...}

procedure TDynamicImage.ImageMouseMove(ASender: TObject; AShift: TShiftState; AX, AY: Integer);
var
  Image: TImage absolute ASender;
begin
  Image.Canvas.Brush.Color := clWhite;
  Image.Canvas.FillRect(Image.Canvas.ClipRect);

  Image.Canvas.TextOut(8, 8, 'X: %d, Y: %d'.Format([AX, AY]));
end;

constructor TDynamicImage.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  OnMouseMove := @ImageMouseMove;
end;

procedure TForm1.FormCreate(ASender: TObject);
begin
  DynamicImage := TDynamicImage.Create(Self);
  DynamicImage.Parent := Self;
  DynamicImage.Align := alClient;
end;

Rozmiar kontrolki jest ustawiany na całą powierzchnię formularza (do testów) i po najechaniu kursorem zostaje wykonany kod zdefiniowanego zdarzenia. Maluje on po płótnie komponentu, wyświetlając otrzymane w argumentach koordynaty, ale dopiero po pierwszym najechaniu nań kursorem — czyli zgodnie z założeniami:

screenshot-20220226185832.png

0

Dziękuję Teraz sobie już poradzę :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1