
In früheren C++Builder/Delphi Versionen hat man innerhalb von FireMonkey ein paralleles Scrollen von ListBoxen (oder anderen, visuellen Komponenten) zumeist über die Position der Scrollbars erreichen können.
Ein kleines Video dazu:
Dazu eine eigene Klasse definiert, um die Werte der horizontalen und vertikalen Scrollbar zu bekommen):
class MyMemo : public TMemo { private: float gHScrollBarValue() {return HScrollBar->Value;} float gVScrollBarValue() {return VScrollBar->Value;} void sHScrollBarValue(float value) {HScrollBar->Value=value;} void sVScrollBarValue(float value) {VScrollBar->Value=value;} public: MyMemo(TForm *form):TMemo(form) {} float getHScrollBarValue() {return gHScrollBarValue();} float getVScrollBarValue() {return gVScrollBarValue();} void setHScrollBarValue(float value) {sHScrollBarValue(value);} void setVScrollBarValue(float value) {sVScrollBarValue(value);} };
Und die passenden Methoden definiert (hier am Beispiel von 2 GUI TMemo Komponenten, die sich gegenseitig scrollen sollen. Berücksichtig werden dabei ScrollBar (Horizontal/Vertikal), MouseWheel Events (H/V), wie auch Tastatur-Events (H/V):
void __fastcall TForm5::MyScrollBarHChangeMemo1(TObject *Sender) { myMemo2->setHScrollBarValue(myMemo1->getHScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MyScrollBarVChangeMemo1(TObject *Sender) { myMemo2->setVScrollBarValue(myMemo1->getVScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MyScrollBarHChangeMemo2(TObject *Sender) { myMemo1->setHScrollBarValue(myMemo2->getHScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MyScrollBarVChangeMemo2(TObject *Sender) { myMemo1->setVScrollBarValue(myMemo2->getVScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MyMouseWheel1(TObject *Sender, TShiftState Shift, int WheelDelta, bool &Handled) { myMemo2->setHScrollBarValue(myMemo1->getHScrollBarValue()); myMemo2->setVScrollBarValue(myMemo1->getVScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MyMouseWheel2(TObject *Sender, TShiftState Shift, int WheelDelta, bool &Handled) { myMemo1->setHScrollBarValue(myMemo2->getHScrollBarValue()); myMemo1->setVScrollBarValue(myMemo2->getVScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MemoKeyDown1(TObject *Sender, WORD &Key, System::WideChar &KeyChar, TShiftState Shift) { myMemo2->setHScrollBarValue(myMemo1->getHScrollBarValue()); myMemo2->setVScrollBarValue(myMemo1->getVScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MemoKeyDown2(TObject *Sender, WORD &Key, System::WideChar &KeyChar, TShiftState Shift) { myMemo1->setHScrollBarValue(myMemo2->getHScrollBarValue()); myMemo1->setVScrollBarValue(myMemo2->getVScrollBarValue()); }
Jetzt gibt es nur ein kleines Problem: In C++Builder 10.1 Berlin hat ein TMemo keine HScrollBar/VScrollBar mehr.
Die gute Nachricht ist, daß man das leicht umstellen kann. Jedes visuelle GUI Element, welches Scrollbars darstellen kann, kennt einen "ViewportPosition", der X- und Y-Positionen zur Verfügung stellt (TPointF)... dadurch wird der Quelltext auch einfacher und übersichtlicher:
NEU:
class MyMemo : public TMemo { private: System::Types::TPointF gScrollBarValue() {return ViewportPosition;} void sScrollBarValue(System::Types::TPointF value) {ViewportPosition=value;} public: MyMemo(TForm *form):TMemo(form) {} System::Types::TPointF getScrollBarValue() {return gScrollBarValue();} void setScrollBarValue(System::Types::TPointF value) {sScrollBarValue(value);} };
Und die Methoden (auch wieder für Tastatur, Maus und Scrollbar-Events):
void __fastcall TForm5::MyScrollBarChangeMemo1(TObject *Sender, const TPointF &OldViewportPosition, const TPointF &NewViewportPosition, const bool ContentSizeChanged) { myMemo2->setScrollBarValue(myMemo1->getScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MyScrollBarChangeMemo2(TObject *Sender, const TPointF &OldViewportPosition, const TPointF &NewViewportPosition, const bool ContentSizeChanged) { myMemo1->setScrollBarValue(myMemo2->getScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MyMouseWheel1(TObject *Sender, TShiftState Shift, int WheelDelta, bool &Handled) { myMemo2->setScrollBarValue(myMemo1->getScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MyMouseWheel2(TObject *Sender, TShiftState Shift, int WheelDelta, bool &Handled) { myMemo1->setScrollBarValue(myMemo2->getScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MemoKeyDown1(TObject *Sender, WORD &Key, System::WideChar &KeyChar, TShiftState Shift) { myMemo2->setScrollBarValue(myMemo1->getScrollBarValue()); } //------------------------------------------------------------------------------ void __fastcall TForm5::MemoKeyDown2(TObject *Sender, WORD &Key, System::WideChar &KeyChar, TShiftState Shift) { myMemo1->setScrollBarValue(myMemo2->getScrollBarValue()); }
Beispielanwendung (Source, C++Builder 10.1 Berlin) hier: