Grafik, Events und anderes bei eigener Komponente



  • Hallo zusammen,

    Ich bau gerade eine Komponente die von TGraphicControl abgeleitet ist. Damit meine entsprechenden Zeichnungen dargestellt werden, habe ich void __fastcall Paint(void) erweitert.

    Problem was ich habe, zur Entwurfszeit wird die Methode immer aufgerufen, aber in der Laufzeit wird sie nur einmal am Anfang aufgerufen und danach nicht mehr.
    Ist das normal? nachdem was ich gelesen habe, soll die Methode doch immer dann automatisch aufgerufen werden wenn die Form neu gezeichnet werden muss!?



  • Hi,

    eine von TGraphicControl abgeleitete Komponente wird auch von TForm, naja vom Parentelement, neu gezeichnet.
    Vorausgesetzt, alles ist registriert etc.pp.

    Nachfolgend mal Code einer Minimal - Ableitung.

    Komponente mygraph.h:

    //---------------------------------------------------------------------------
    #ifndef MyGraphH
    #define MyGraphH
    //---------------------------------------------------------------------------
    #include <SysUtils.hpp>
    #include <Controls.hpp>
    #include <Classes.hpp>
    #include <Forms.hpp>
    //---------------------------------------------------------------------------
    class PACKAGE TMyGraph : public TGraphicControl
    {
    private:
    
    protected:
    
        virtual void __fastcall Paint(void);
    
    public:
    
        __property Canvas ;  // Zugriff auf Canvas erlauben
        __fastcall TMyGraph(TComponent* Owner);
    
    __published:
    
    };
    //---------------------------------------------------------------------------
    #endif
    

    mygraph.cpp

    #include <vcl.h>
    #pragma hdrstop
    
    #include "MyGraph.h"
    #pragma package(smart_init)
    //---------------------------------------------------------------------------
    
    static inline void ValidCtrCheck(TMyGraph *)
    {
        new TMyGraph(NULL);
    }
    //---------------------------------------------------------------------------
    __fastcall TMyGraph::TMyGraph(TComponent* Owner)
        : TGraphicControl(Owner)
    {
        Width  = 120;
        Height = 120;
    }
    //--------------------------------------------------------------------------
    
    void __fastcall TMyGraph::Paint(void)
    {
       Canvas->Brush->Color = clLime;
       Canvas->Brush->Style = bsDiagCross;
       Canvas->Ellipse(0, 0, Width, Height);
    }
    
    namespace Mygraph
    {
       void __fastcall PACKAGE Register()
       {
          TComponentClass classes[1] = {__classid(TMyGraph)};
          RegisterComponents("Beispiele", classes, 0);
       }
    }
    //---------------------------------------------------------------------------
    

    Soll denn die Kompo für die Problematik im Parallelthread sein?

    mfg
    kpeter



  • Ja, habe aber mittlerweise rausgefunden wann on Paint aufgerufen wird.

    Kann mir villeicht jemand sagen warum das nicht funktioniert?

    typedef void __fastcall (__closure *MyClickEvent)(TObject *Sender,TPoint Position);//(1) 
    //---------------------------------------------------------------------------
    class PACKAGE TTest: public TGraphicControl 
    { 
    private: 
      MyClickEvent IVMyOnClick; 
    
    __published: 
      __property MyClickEvent MyOnClick={read=IVMyOnClick,write=IVMyOnClick};//(2) 
    protected: 
    public: 
      void __fastcall Click() //(3) 
        { 
          TPoint point; 
          GetCursorPos(&point); 
          if(IVMyOnClick)IVMyOnClick(this,point); 
          TGraphicControl::Click();//(4) 
        } 
      __fastcall TTest(TComponent*Owner):TGraphicControl (Owner){} 
    };
    

    Da bekomme ich die Fehlermeldung :

    [BCC32 Fehler] GraphicControl1.h(126): E2113 Virtuelle Funktion '_fastcall TTest::Click()' verursacht Konflikte mit der Basisklasse 'TControl'



  • JBOpael schrieb:

    Ja, habe aber mittlerweise rausgefunden wann on Paint aufgerufen wird.

    Sehr aufschlussreich in bezug auf dein erstes Posting.



  • Da hast du wohl recht 🙂

    Paint wird zur Laufzeit nur aufgerufen wenn TForm von etwas anderem verdeckt oder von minimiert zu maximiert wechselt.



  • Nicht ganz.

    Du kannst jederzeit eine SendMessage mit WM_PAINT zur Komponente schicken...



  • Ich kann auch Paint direkt aufrufen.

    Das Problem mit dem Event hat sich auch fast gelöst. hab das DYNAMIC davor vergessen. Ich bekomme jetzt keine fehlermeldung aber auf den click reagiert er auch nicht.



  • Ansich läuft der Click-Code , aus einer Anwendung :

    void __fastcall TForm1::MyGraph1MyOnClick(TObject *Sender, TPoint Position)
    {
       int x = Position.x;
       int y = Position.y;
       ShowMessage("Klick in x= " + IntToStr(x) + " und y = " + IntToStr(y));
       // x, y sind hier noch Formularkoordinaten
    }
    //---------------------------------------------------------------------------
    

    Hast was übersehen?



  • Ich weiß zwar nicht ob man übersehen nennen kann.hab durch zufall raus gefunden wenn der Component Owner von TGraphicControl kommt, Funktioniert es nicht und wenn der Owner von TWinControl kommt dann funktioniert es. Dasversteh ich zwar nicht ganz weil doch beide von TControl abgeleitet sind in der die Ereignisse und Events enthalten sind.



  • Hi,

    Alternativ bietet sich an, die WndProc-Methode zu benutzen.

    *.h

    //---------------------------------------------------------------------------
    #ifndef main_graphicH
    #define main_graphicH
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    
    #include <Buttons.hpp>
    //---------------------------------------------------------------------------
    
    //typedef void __fastcall (__closure *MyClickEvent)(TObject *Sender,TPoint Position);
    
    class TForm1;
    class TMyGraph : public TGraphicControl
    {
    friend class TForm1;
    private:
        POINT points[5];
        //MyClickEvent    FMyOnClick;
    protected:
    
        void __fastcall WndProc(Messages::TMessage &Message);
    
    public:
    
        //void __fastcall MyOnClick(TObject *Sender, TPoint Position);
        /*
        void __fastcall Click()
        { 
            TPoint point;
            GetCursorPos(&point);   // Screen-Koordinaten
            if(FMyOnClick)
                FMyOnClick(this,point);
            TGraphicControl::Click();
        }
        */
        __property Canvas ;  // Zugriff auf Canvas erlauben
    
        __fastcall TMyGraph(TComponent* Owner);
    
    };
    //---------------------------------------------------------------------------
    
    class TForm1 : public TForm
    {
    __published:	// Komponenten, die von der IDE verwaltet werden
        TButton *Button1;
        TButton *Button2;
        TBitBtn *BitBtn1;
        TSpeedButton *SpeedButton1;
        TLabel *Label1;
        TLabel *Label2;
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall Button2Click(TObject *Sender);
    
    private:
    public:
        TMyGraph* MyGraph;
        __fastcall TForm1(TComponent* Owner);
        __fastcall ~TForm1(void);
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TForm1 *Form1;
    //---------------------------------------------------------------------------
    #endif
    

    *.cpp

    //---------------------------------------------------------------------------
    #include <vcl.h>
    #pragma hdrstop
    
    #include "main_graphic.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    
    #pragma resource "*.dfm"
    TForm1 *Form1;
    
    __fastcall TMyGraph::TMyGraph(TComponent* Owner)
        : TGraphicControl(Owner)
    {
        Width  = 120;
        Height = 120;
        Canvas->Brush->Color = clBlack;
        points[0].x =  0;
        points[0].y =  0;
        points[1].x = Width -1;
        points[1].y =  0;
        points[2].x = Width -1;
        points[2].y = Height -1;
        points[3].x =  0;
        points[3].y = Height -1;
        points[4].x =  0;
        points[4].y =  0;
    
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TMyGraph::WndProc(Messages::TMessage &Message)
    {
        switch(Message.Msg) {
            case  WM_PAINT : {
                // Rahmen zeichnen
                Canvas->Pen->Color = clRed;
                Canvas->Polyline(points,4);
            }
            break;
            case WM_LBUTTONDOWN : {
                int X = (LPARAM) Message.LParamLo;
                int Y = (LPARAM) Message.LParamHi;
                // Ausgabe der Koordinaten
                Form1->Label1->Caption = "Klick bei X: " + IntToStr(X);
                Form1->Label2->Caption = "Klick bei Y: " + IntToStr(Y);
            }
            break;
            case WM_MOUSEMOVE : {
                int X = (LPARAM) Message.LParamLo;
                int Y = (LPARAM) Message.LParamHi;
                // Ausgabe der Koordinaten
                Form1->Label1->Caption = "X: " + IntToStr(X);
                Form1->Label2->Caption = "Y: " + IntToStr(Y);
            }
            break;
       }
       TGraphicControl::WndProc(Message);
    }
    //---------------------------------------------------------------------------
    /*
    void __fastcall TMyGraph::MyOnClick(TObject *Sender, TPoint Position)
    {
        int x = Position.x;
        int y = Position.y;
        ShowMessage("Klick in x= " + IntToStr(x) + " und y = " + IntToStr(y));
        TGraphicControl::Click();
    }
    */
    //---------------------------------------------------------------------------
    
    __fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
    {
        MyGraph         = new TMyGraph(this);
        MyGraph->Left   = 10;
        MyGraph->Top    = 10;
        MyGraph->Parent = Form1;
    }
    //---------------------------------------------------------------------------
    
    __fastcall TForm1::~TForm1(void)
    {
        delete MyGraph;
    }
    //---------------------------------------------------------------------------
    

    Die erste Variante Klick-Event ist kommentiert.

    mfg
    kpeter



  • Hmm... damit kann man schon was anfangen 🙂

    Wenn ich jetzt mit Canvas eine Linie Male, kann ich dann auch rausfinden ob die Maus gerade auf der Linie ist, oder muss ich das über X/Y coordinaten selber raus suchen?

    nachdem was ich so in der BCB Hilfe gelesen habe, könnte ich mit dem HDC Handle von Canvas vielleicht etwas machen, aber habe nicht rausfinden können wie das gehen soll.



  • JBOpael schrieb:

    Wenn ich jetzt mit Canvas eine Linie Male, kann ich dann auch rausfinden ob die Maus gerade auf der Linie ist, oder muss ich das über X/Y coordinaten selber raus suchen?

    Eigentlich sollte es anders herum sein. Nach den Mauskoordinaten (oder Stift) wird eine Linie gezeichnet. Win API's LineDDA zb.

    edit:

    Damit hier keine Langeweile aufkommt 😃

    einfügen in *.cpp

    // gleich unterhalb Form-Konstruktor
    
    VOID CALLBACK LineDDACallBack( int X, int Y, LPARAM lParam )
    {
       Form1->MyGraph->Canvas->Pixels[X][Y] = RGB(0, 0, 0);
       // Koordinaten ausgeben ( und irgendwann speichern ...)
       // Gimmick
       Form1->Memo1->Lines->Add("X: " + IntToStr(X) + ", Y: " + IntToStr(Y));
    }
    //---------------------------------------------------------------------------
    
    // in WndProc vervollständigen
    
       case WM_MOUSEMOVE : {
          int X = (LPARAM) Message.LParamLo;
          int Y = (LPARAM) Message.LParamHi;
          LineDDA( oldx, oldy, X, Y, (LINEDDAPROC)LineDDACallBack, NULL );
          oldx = X;
          oldy = Y;
          // Ausgabe der Koordinaten
          Form1->Label1->Caption = "X: " + IntToStr(X);
          Form1->Label2->Caption = "Y: " + IntToStr(Y);
       }
       break;
    

    mfg
    kpeter



  • Eigentlich sollte es anders herum sein. Nach den Mauskoordinaten (oder Stift) wird eine Linie gezeichnet. Win API's LineDDA zb.

    Der Sinn dahinter ist ja das ich nicht nur eine Linie Malen kann, sondern diese Linie auch ändern, verschieben und vor allem zuordnen kann.

    Mit dem was du geschieben hast, habe ich ein Problem. da ich das ja versuche in eine Komponente zu packe stoße ich bei dem Callback auf ein Problem.

    Wenn ich den außerhalb meiner klasse als funktion anlege, funktioniert das ich komm aber dann nicht an mein Canvas dran. und wenn ich den Innerhalb meiner klasse anlege bekomme ich die Fehlermeldung:

    [BCC32 Fehler] GraphicControl1.h(175): E2235 Elementfunktion muß aufgerufen oder ihre Adresse übernommen werden
    

    weder die BCB Hilfe noch google gibt mir antworten darauf 😞



  • Dann anders; anstatt LineDDA geht natürlich auch

    // ersatzweise für CALLBACK LineDDA
       POINT points[2];
       points[0].x = oldx;
       points[0].y = oldy;
       points[1].x = X;
       points[1].y = Y;
       this->Canvas->Polyline(points, 1);
       oldx = X;
       oldy = Y;
    

    kpeter



  • Damit kann ich doch nur eine Linie zeichnen und keine gezeichnete Linie erkennen ?!



  • JBOpael schrieb:

    gezeichnete Linie erkennen

    Wie ist das gemeint?



  • Wenn ich mit Canvas eine Linie Zeichne und ich dann mit der Maus auf die linie gehe dann soll ein ereignis ausgelöst werden.

    genau genommen will ich es hin bekommen das die linie einen Focus bekommen kann. quasi so wie man es von Grafikprogrammen kennt. Man malt eine Linie und wenn die Linie angeklickt wird erhält sie den Fokus und ich kann die Linie per maus noch vergrößern, verschieben etc.. So soll das endprodukt aussehen.



  • Linien zeichnen geht ja nun.

    Verbunden mit WM_LBUTTONDOWN und WM_LBUTTONUP (Startpunkt/Endpunkt) können gerade Linien und geometrische Figuren gezeichnet
    werden. Die Pixelpositionen bekommst du ja geliefert.

    Wo ist da das Problem?



  • Wenn ich jetzt mit Canvas eine Linie Male, kann ich dann auch rausfinden ob die Maus gerade auf der Linie ist, oder muss ich das über X/Y coordinaten selber raus suchen?

    Das ist das was ich hier gefragt hatte. ich muss also die pixel coordinaten mit den Maus coordinaten vergleichen und wenn die die maus auf der linie ist das event auslösen.



  • Das bekommt man hin, Mauskoord. mit Liniekoord. vergleichen. Aber deshalb bekommt diese
    Linie sicher keinen Fokus oder wird gar markiert.

    Das spielt jetzt wieder in den anderen Thread hinein...

    mfg
    kpeter


Anmelden zum Antworten