StringGrid - "OnScroll" implementieren



  • Moin,
    ich wüsste gerne, wie ich auf das Scrollen im StringGrid reagieren kann?
    Außerdem muss ich wissen, ob hoch- oder runtergescrollt wird?
    Im Prinzip bräuchte ich die Funktionen StringGrid1->OnScrollUp und StringGrid1->OnScrollDown, bedauerlicherweise gibt es diese Funktionen nicht und ich weiß nicht, wie ich Sie mir selber basteln kann!
    Wäre für eine schnelle Hilfe sehr dankbar!

    MfG
    skillloseR

    [ Dieser Beitrag wurde am 02.05.2002 um 22:34 Uhr von Jansen editiert. ]



  • hm,

    zum ersten:

    reagiere auf das Ereignis RowMoved :

    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::StringGrid1RowMoved(TObject *Sender, int FromIndex,
          int ToIndex)
    {
    // vergleiche hier FromIndex und ToIndex
    // so stellst du fest, in welcher richtung gesrollt wurde
    }
    //---------------------------------------------------------------------------
    

    Mit TopRow kannst du dann diese noch beeinflussen.

    ansonsten leite eine neue Klasse von TStringGrid ab und füge entsprechende Ereignisse hinzu.



  • RowMoved hat doch aber nicht wirklich etwas mit dem Scrollen zu tun, oder?

    Hier ist mal ein Ansatz, bei dem die WindowProc überschrieben wird:

    //in .h unter public:
      TWndMethod OldStringGridWindowProc;
      void __fastcall NewStringGridWindowProc(TMessage &Msg);
    
    //in .cpp
    int ScrollPosOld = 0;
    
    __fastcall TForm1::TForm1(TComponent* Owner)
      : TForm(Owner)
    {
      //save the old WindowProc
      OldStringGridWindowProc = StringGrid1->WindowProc;
    
      //assign our new handler to the WindowProc
      StringGrid1->WindowProc = NewStringGridWindowProc;
    }
    //-------------------------------------------------------------------
    //new StringGrid Window procedure
    void __fastcall TForm1::NewStringGridWindowProc(TMessage &Msg)
    {
      //look for scrolling message
      if (Msg.Msg == WM_VSCROLL)
      {
        int ScrollPosNew = GetScrollPos(StringGrid1->Handle, SB_VERT);
    
        if (ScrollPosNew > ScrollPosOld)
          Memo1->Lines->Add("Down");
        else if (ScrollPosNew < ScrollPosOld)
          Memo1->Lines->Add("Up");
    
        ScrollPosOld = ScrollPosNew;
      }
    
      //call the original procedure to implement scrolling
      OldStringGridWindowProc(Msg);
    }
    //-------------------------------------------------------------------
    //finally, restore the original WindowProc
    void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
    {
      StringGrid1->WindowProc = OldStringGridWindowProc;
    }
    


  • Original erstellt von Jansen:
    **RowMoved hat doch aber nicht wirklich etwas mit dem Scrollen zu tun, oder?
    **

    Nö, führt aber auch zum Ziel. Natürlich kann man die WindowProc überschreiben. Aber ein einfacher Vergleich zweier Werte ist doch ein wenig kürzer. images/smiles/icon_smile.gif

    Ich hätte sowieso ne neue Kompo geschrieben. Ist schöner, einfacher und flexibler. Man kann schließlich das StringGrid so anpassen, wie mans haben will. Wäre ansich die ideale Lösung.

    Naja, jeder machts halt anders images/smiles/icon_wink.gif



  • führt aber auch zum Ziel

    Hmm, ich steh voll auf'm Schlauch. Kannst bitte du mal ein Beispiel geben, wie man das (beim Original-StringGrid) anwenden soll? Ich les bei RowMoved nur was von "wenn eine Zeile die Position ändert", aber beim Scrollen ändert sich doch nicht die Position der Zeilen innerhalb des Grids?

    Und wenn es um das Ableiten einer neuen Kompo geht: da würde ich mir doch lieber was fertiges von Torry holen (mit zig nützlichen Zusatzfeatures), als wegen einer Eigenschaft ne eigene Komponente zu schreiben. images/smiles/icon_smile.gif



  • ähm,

    ja, saß gestern wohl zu lange vorm PC images/smiles/icon_rolleyes.gif

    Sorry. Hab es gerade mal nachgeschaut. Muss ich wohl irgendwie verwechselt haben. Du kannst also wieder vom Schlauch runter gehen.



    Und wenn es um das Ableiten einer neuen Kompo geht: da würde ich mir doch lieber was fertiges von Torry holen (mit zig nützlichen Zusatzfeatures), als wegen einer Eigenschaft ne eigene Komponente zu schreiben

    hm,

    gut, da gibt es einige tolle Kompos. Ich ziehe mir aber selten welche. Um eine neue Kompo abzuleiten brauch ich für sowas 10 min.
    Was er braucht, sind OnScroll- Ereignisse. Ich konnte bei Torry sowas nicht finden.

    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::MyStringGrid1ScrollHori(TObject *Sender,
          HWND ScrollBarHandle, int Pos, int ScrollCode)
    {
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm1::MyStringGrid1ScrollVert(TObject *Sender,
          HWND ScrollBarHandle, int Pos, int ScrollCode)
    {
    
    }
    //---------------------------------------------------------------------------
    

    und solch eine Kompo ist ja schnell geschrieben. Das Suchen in Torry.net hat länger gedauert.

    //---------------------------------------------------------------------------
    typedef void __fastcall (__closure *TMyScrollHoriEvent)(System::TObject* Sender,HWND ScrollBarHandle,int Pos, int ScrollCode);
    typedef void __fastcall (__closure *TMyScrollVertEvent)(System::TObject* Sender,HWND ScrollBarHandle,int Pos, int ScrollCode);
    
    class PACKAGE TMyStringGrid : public TStringGrid
    {
    private:
    TMyScrollHoriEvent IVOnScrollHori;
    TMyScrollVertEvent IVOnScrollVert;
    protected:
    void __fastcall DoScrollVert(HWND Handle,int Pos, int ScrollCode){if(IVOnScrollVert) IVOnScrollVert(this,Handle,Pos,ScrollCode);}
    void __fastcall DoScrollHori(HWND Handle,int Pos, int ScrollCode){if(IVOnScrollHori) IVOnScrollHori(this,Handle,Pos,ScrollCode);}
    __published:
    __property TMyScrollHoriEvent OnScrollHori={read=IVOnScrollHori,write=IVOnScrollHori};
     __property TMyScrollVertEvent OnScrollVert={read=IVOnScrollVert,write=IVOnScrollVert};
    
    public:
    void __fastcall WndProc(TMessage &Message)
        {
        Dispatch(&Message);
        if(Message.Msg==WM_VSCROLL)DoScrollVert((HWND)Message.LParam,HIWORD(Message.WParam),LOWORD(Message.WParam));
        if(Message.Msg==WM_HSCROLL)DoScrollHori((HWND)Message.LParam,HIWORD(Message.WParam),LOWORD(Message.WParam));
        }
    
        __fastcall TMyStringGrid(TComponent* Owner): TStringGrid(Owner){}
    __published:
    };
    //---------------------------------------------------------------------------
    

    Naja, da gehen unsere Meinungen wohl auseinander. Dat macht aber nichts images/smiles/icon_wink.gif

    Edit: Quelltext geändert !!!

    [ Dieser Beitrag wurde am 13.03.2002 um 11:34 Uhr von AndreasW editiert. ]



  • @AndreasW
    Ist der Quelltext, den du gepostet hast, ein fertig Komponente bei der auf das Hoch- und Runterscrollen reagiert werden kann?
    Wenn ja, wo müsste ich den Code einbinden, damit ich diese Eigenschaft auch in meinem StringGrid habe?



  • Hi,

    ja ist es. Das ist eine Klassen einer fertigen Komponente.

    Diese kannst du genauso verwenden wie die Kompo TStringGrid selbst.

    Die ganze Komponente sieht so aus:

    Die Headerdatei:

    //---------------------------------------------------------------------------
    #ifndef MyStringGridH
    #define MyStringGridH
    //---------------------------------------------------------------------------
    #include <SysUtils.hpp>
    #include <Controls.hpp>
    #include <Classes.hpp>
    #include <Forms.hpp>
    #include <Grids.hpp>
    //---------------------------------------------------------------------------
    typedef void __fastcall (__closure *TMyScrollHoriEvent)(System::TObject* Sender,HWND ScrollBarHandle,int Pos, int ScrollCode);
    typedef void __fastcall (__closure *TMyScrollVertEvent)(System::TObject* Sender,HWND ScrollBarHandle,int Pos, int ScrollCode);
    
    class PACKAGE TMyStringGrid : public TStringGrid
    {
    private:
    TMyScrollHoriEvent IVOnScrollHori;
    TMyScrollVertEvent IVOnScrollVert;
    protected:
    void __fastcall DoScrollVert(HWND Handle,int Pos, int ScrollCode){if(IVOnScrollVert) IVOnScrollVert(this,Handle,Pos,ScrollCode);}
    void __fastcall DoScrollHori(HWND Handle,int Pos, int ScrollCode){if(IVOnScrollHori) IVOnScrollHori(this,Handle,Pos,ScrollCode);}
    __published:
    __property TMyScrollHoriEvent OnScrollHori={read=IVOnScrollHori,write=IVOnScrollHori};
     __property TMyScrollVertEvent OnScrollVert={read=IVOnScrollVert,write=IVOnScrollVert};
    
    public:
    void __fastcall WndProc(TMessage &Message)
        {
        Dispatch(&Message);
        if(Message.Msg==WM_VSCROLL)DoScrollVert((HWND)Message.LParam,HIWORD(Message.WParam),LOWORD(Message.WParam));
        if(Message.Msg==WM_HSCROLL)DoScrollHori((HWND)Message.LParam,HIWORD(Message.WParam),LOWORD(Message.WParam));
        }
    
        __fastcall TMyStringGrid(TComponent* Owner): TStringGrid(Owner){}
    __published:
    };
    //---------------------------------------------------------------------------
    #endif
    

    und die cpp- Datei:

    //---------------------------------------------------------------------------
    #include <vcl.h>
    #pragma hdrstop
    #include "MyStringGrid.h"
    #pragma package(smart_init)
    //------------------------------------------------------------------------
    static inline void ValidCtrCheck(TMyStringGrid *){new TMyStringGrid(NULL);}
    //-------------------------------------------------------------------------
    namespace Mystringgrid
    {
        void __fastcall PACKAGE Register()
        {
            TComponentClass classes[1] = {__classid(TMyStringGrid)};
            RegisterComponents("Eigene", classes, 0);
        }
    }
    //---------------------------------------------------------------------------
    

    Die Zeile

    RegisterComponents("Eigene", classes, 0);
    

    Ist für die Zuordnung in dem Register vom BCB verantwortlich, wo auch die ********komponenten abgelegt sind. Gibst du einen Namen an, den es noch nicht gibt, wird auch eine neue Registerkarte angelegt. Hier wird also eine neue Registerkarte mit den Namen "Eigene" angelegt. Bei bedarf würde ich diesen Namen noch deinen Vorstellungen anpassen.

    Nun kopiere diese und speichere diese Dateien in ein Ordner. Ich hab zum Beispiel unter "C:\\Meine Komps" eine Ordner erstellt, in den alle meine Komponenten gespeichert sind.

    Klick dann auf Komponente installieren...
    Dann auf "In neues Package", da du wahrscheinlich noch keins hast

    Auswählen:

    Name der Unit: Die cpp-Datei, die du erstellt hast
    Name des Package: Durchsuchen klicken und dein Verzeichnis auswählen. Als Name für das Package zum Beispiel="Eigene Komps";

    Eventuell noch ne Beschreibung angeben und auf "OK klicken.

    fertig.

    Erstelle nun ein neues Projekt. Geh oben im BCB auf die Registerkarte, welche hinzugefügt wurde ("Eigene" in meinem Beispiel).
    Dort findest du jetzt die Komponente mit den neuen Ereignissen.

    Später kannst du ohne Probleme weitere Kompos zu diesem Package hinzufügen.

    Ich möchte noch auf die Ausführungen in der Hilfe zum Thema "Komponentenentwicklung" hinweisen.

    Edit: Quelltext korrigiert !!!

    Auch ja: zwei Ereignisschablonen wären eigentlich nicht notwendig gewesen. Hab einfach vor mir hingeschrieben. Spielt aber keine Rolle

    [ Dieser Beitrag wurde am 13.03.2002 um 11:36 Uhr von AndreasW editiert. ]



  • Ach ja,

    er wird ide Headerdatei nicht finden.

    Deshalb geh auf:

    Projekt/Optionen

    Dann auf Verzeichnisse/Bedingungen

    rechts vom eintrag des " Include Path" ( "..." ) doppelklicken und dein Verzeichniss hinzufügen.

    nun haste deine eigene Kompo-Bibliothek images/smiles/icon_wink.gif

    [ Dieser Beitrag wurde am 13.03.2002 um 11:29 Uhr von AndreasW editiert. ]



  • Gibt es auch eine Möglichkeit diese Funktionen einem von mir bereits verwendeten StringGrid hinzuzufügen?



  • hm,
    eingeschränkes Ja.
    Dafür ist Jansens Methode sehr gut geeignet. Ein Ereignis ist nichts anderes als ein Methodenaufruf. Naja, vielleicht etwas einfach ausgedrückt.

    Die Methode

    void __fastcall WndProc(TMessage &Message)
    

    welche ich überschrieben habe, wird von der WindowProc aufgerufen, die Jansen in seinen Beispiel überschrieben hat.

    Sie ist also nahezu identisch mit Jansens Methode:

    void __fastcall TForm1::NewStringGridWindowProc(TMessage &Msg)
    {
      //look for scrolling message
      if (Msg.Msg == WM_VSCROLL)
      {
        int ScrollPosNew = GetScrollPos(StringGrid1->Handle, SB_VERT);
    
        if (ScrollPosNew > ScrollPosOld)
          Memo1->Lines->Add("Down");
        else if (ScrollPosNew < ScrollPosOld)
          Memo1->Lines->Add("Up");
    
        ScrollPosOld = ScrollPosNew;
      }
    
      //call the original procedure to implement scrolling
      OldStringGridWindowProc(Msg);
    }
    //-------------------------------------------------------------------
    

    Er hat hier, wie ich auch, überprüft, ob die Nachricht WM_VSCROLL gesendet wurde. Ich hab zusätzlich noch das Vertikale Scrollen mit reingenommen.
    Jansen wertet in diese Methode gleich die Message aus und setzt diese um. Ich hab die Umsetztung auf den Anwender der beschriebenen Komponente abgewälzt indem ich daraus ein Ergeignis erzeugt habe, die alle relevanten Werte mit in die Methodenparameter übergibt.

    Vom Effekt her wäre folgendes mit meinen Beispiel identisch:

    // Jansens Methode etwas umgeschrieben:
    void __fastcall TForm1::NewStringGridWindowProc(TMessage &Msg)
    {
      //look for scrolling message
      if (Msg.Msg == WM_VSCROLL)DoScrollVert((HWND)Message.LParam,HIWORD(Message.WParam),LOWORD(Message.WParam));
    
      //call the original procedure to implement scrolling
      OldStringGridWindowProc(Msg);
    }
    //-------------------------------------------------------------------
    
    // eine Weitere Methode in der Klasse TForm:
    void __fastcall TForm1: :DoScrollVert(HWND Handle,int Pos, int ScrollCode)
    {
    int ScrollPosNew = GetScrollPos(StringGrid1->Handle, SB_VERT);
    
        if (ScrollPosNew > ScrollPosOld)
          Memo1->Lines->Add("Down");
        else if (ScrollPosNew < ScrollPosOld)
          Memo1->Lines->Add("Up");
    
        ScrollPosOld = ScrollPosNew;
    
    }
    

    mit den einzigen Unterschied, dass die Geschichte nicht im Objektinspector angezeigt wird. Funktioniert aber genaus.

    Du kannst nun die Methode

    DoScrollVert(HWND Handle,int Pos, int ScrollCode)
    

    als Ereignis verwenden, so wie ichs mit Jansens Beispiel gemacht hab.

    Das Handle ist das handle von der Scrollbar selbst. Du kannst also über das Handle auf die Scrollleiste des Stringgrids zugreifen. Du kannst Werte Abfragen und die Scrollbar beeinflussen. Siehe hierzu dei MSDN.
    Der Wert Pos ist die Positon des Scrollbalkens, während er gezogen wird. Relativ zum Maximum der Scrolleiste.
    Der Wert ScrollCode gibt weiter Infos über Scrollmodies.
    Siehe hierzu in der Hilfe zum Thema " WM_VSCROLL".

    [EDIT] Quellcode korrigiert [/EDIT]



  • Beachte aber die notwendigen Deklarationen, welche Jansen beschrieben hat.


Anmelden zum Antworten