Autor Beitrag
galagher
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Mo 10.01.22 21:24 
Hallo zusammen!

Ich möchte gerne einen (oder mehrere) TButtons in einem TJvRichEdit so "einfügen", dass er mitscrollt, dass er also seine Position im Text behält. Dabei soll der Button voll funktionsfähig bleiben, und es soll möglichst flüssig ablaufen. Gerne auch TSpeedButton oder TBitBtn!

Man kann ja Grafiken auf diese Weise einbinden, aber das kommt nicht in Frage, weil man nicht abfragen kann, welche Grafik gerade angeklickt wurde.
Ein TJvRichEdit als Parent einer Komponente geht - natürlich - ebenfalls nicht.

Kann man das mit Komponenten so überhaupt machen? Wenn ja, wie?

lg

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 11.01.22 12:29 
Einen direkten (vom System vorgesehenen) Weg gibt es dafür nicht. Als einziger Weg bleibt wirklich nur den Button dem TJvRichEdit als "SubControl" hinzuzufügen und dann die Scroll-Messages (WM_HSCROLL, WM_VSCROLL) abzufangen und die Position neu zu setzen (evtl. mußt du also das EditControl "subclass"en, also davon ableiten und die Messages dort behandeln).

Als Überblick kann ich dir nur den Petzold empfehlen: Building a Better Scroll (Code ist aber natürlich in C).

Für diesen Beitrag haben gedankt: galagher
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Mi 12.01.22 15:39 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Einen direkten (vom System vorgesehenen) Weg gibt es dafür nicht. Als einziger Weg bleibt wirklich nur den Button dem TJvRichEdit als "SubControl" hinzuzufügen
So wird das nichts, einen Button auf das JvRichEdit legen (in Wirklichkeit ja auf die TForm) und dann alles per Hand machen. Da verzichte ich lieber drauf!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 12.01.22 17:27 
Auch wenn evtl. die Delphi-Komponenten das nicht direkt unterstützen, solltest du immer direkt per WinAPI-Funktion SetParent ein beliebiges Windows-Control als Parent des Buttons zuweisen können (und dieser wird dann auch vom Windows-System nur innerhalb des sichtbaren Bereichs des Parent-Controls angezeigt).
Und dann eben nur noch bei den Scroll-Messages (nachdem das eigentliche TJvRichEdit diese verarbeitet hat), die Position des Buttons neu anpassen (so viel Code sollte das dann nicht sein).

In einem kleinen WinForms-Testprojekt (mit C#) habe ich das ansatzweise ausprobiert und es hat funktioniert. Einzig, wenn du sehr viele Buttons hast, kann natürlich die Performance ein bißchen darunter leiden.

Für diesen Beitrag haben gedankt: galagher
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Fr 14.01.22 12:34 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Und dann eben nur noch bei den Scroll-Messages (nachdem das eigentliche TJvRichEdit diese verarbeitet hat), die Position des Buttons neu anpassen (so viel Code sollte das dann nicht sein).
So aus dem Stand habe ich keine Vorstellung davon, wie ich das anpasse. Ich sehe mir das aber an, vielleicht komme ich ja weiter.

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Fr 14.01.22 19:27 
SetParent ist mir klar, nicht aber der Rest.
Es gibt ein Ereignis OnVerticalScroll, das gilt aber für beide vertikale Richtungen. Das ist es aber nicht, was du mit Scroll-Messages meinst.

Ich brauche einen Tipp... :nixweiss:

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Sa 15.01.22 10:00 
Ich meine die allgemeine WndProc.
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: So 16.01.22 20:29 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Ich meine die allgemeine WndProc.

Ok:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.WndProc(var msg : TMessage);
begin
  if msg.Msg = WM_VSCROLL then
    //Button2.Top := //was?
    ;

  inherited WndProc(msg);
end;


Die Prozedur wird ausgeführt, aber wie ermittle ich den Wert für Button2.Top?

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mo 17.01.22 11:08 
Indem du diese Position relativ zur aktuellen Scrollposition des TJvRichEdit berechnest (merke dir dafür die initiale Position des Buttons), s.a. How to get the scrollbar posit(i)on in a RichEdit control (das einfachste ist wohl unter "Solve 2" beschrieben).
Führ die Berechnung aber nach dem Bearbeiten der Message aus (also nach inherited WndProc(msg);).
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Mo 17.01.22 17:47 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Indem du diese Position relativ zur aktuellen Scrollposition des TJvRichEdit berechnest

Ich musste eine Klasse von TJvRichEdit ableiten, also muss ich das Programm noch ensprechend anpassen.

Soweit habe ich das erstmal. Button2 scrollt auf diese Weise aber in die falsche Richtung:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
procedure TJvRichEditX.WndProc(var msg : TMessage);

  function GetVertScrollBarPosition: Integer;
  begin
    // constants: SB_HORZ = 0, SB_VERT = 1,
    // SB_BOTH = 3, SB_THUMBPOSITION = 4
    Result := GetScrollPos(Form1.aJvRichEditX.Handle, SB_VERT);
    // Result is the number of pixels the RichEdit is scrolled
  end;

begin
  inherited WndProc(msg);

  if msg.Msg = WM_VSCROLL then
    Form1.Button2.Top := GetVertScrollBarPosition;
end;

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mo 17.01.22 19:10 
Ich meinte ja auch:
ausblenden Delphi-Quelltext
1:
Button2.Top := InitialPosY + GetVertScrollBarPosition;					
(und InitialPosY initialisierst du bei der ersten Zuweisung von Button2.Top).

Alternativ berechnest du die Differenz der Scrollbewegung (also Werte vor und nach dem Scrollen vergleichen) und addierst diese zur Button-Position.

PS: Es sollte nur
ausblenden Delphi-Quelltext
1:
Result := GetScrollPos(Handle, SB_VERT);					
heißen...

Für diesen Beitrag haben gedankt: galagher
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Mo 17.01.22 20:31 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:

ausblenden Delphi-Quelltext
1:
Button2.Top := InitialPosY + GetVertScrollBarPosition;					

Perfekt! Nur statt + muss da ein - hin:
ausblenden Delphi-Quelltext
1:
Button2.Top := InitialPosY - GetVertScrollBarPosition;					

Das genügt mir völlig! Jetzt kann ich auf diese Art und Weise auch mehrere Buttons oder was auch immer einfügen, und es wirkt tatsächlich so, als wäre die Komponente im Text "engebettet" und scrollt perfekt mit! :dance2:

//Edit:
Nur eines noch: Wie lasse ich den Button sofort mitscrollen, wenn man am Balken der Bildlaufleiste zieht? Er wird da erst nachträglich an die neue Position gesetzt, wenn ich zusätzlich
ausblenden Delphi-Quelltext
1:
2:
3:
...
else
  Result := GetScrollPos(Handle, SB_THUMBPOSITION);
angebe!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 18.01.22 08:43 
So habe ich mir das vorgestellt.

Ja, hast Recht, hier muß subtrahiert werden, da GetScrollPos positive Werte liefert (in .NET hat die AutoScrollPosition immer negative Werte - daher hab ich mich hier vertan). Aber dafür bist du ja Entwickler...

Eigentlich sollte auch während des Bewegens der ScrollBar die WM_VSCROLL-Nachricht verschickt werden (mit SB_THUMBTRACK im wParam) (denn der Text scrollt ja auch währenddessen).
Und laut WinAPI-Doku zu GetScrollPos gibt es (dort) nicht SB_THUMBPOSITION als Parameter (der Kommentar ist also diesbzgl. falsch).

PS: Noch als Hinweis, falls du es noch nicht in der Doku gelesen hast: wenn deine ScrollBar mehr als 65535 (virtuelle) Pixel hat, dann mußt du stattdessen GetScrollInfo benutzen (32bit-Werte anstatt 16bit).
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Di 18.01.22 16:50 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Eigentlich sollte auch während des Bewegens der ScrollBar die WM_VSCROLL-Nachricht verschickt werden (mit SB_THUMBTRACK im wParam) (denn der Text scrollt ja auch währenddessen).
Und laut WinAPI-Doku zu GetScrollPos gibt es (dort) nicht SB_THUMBPOSITION als Parameter (der Kommentar ist also diesbzgl. falsch).
Ja, es funktioniert auch ohne SB_THUMBPOSITION, aber: Wenn man den Balken der Scrolleiste bewegt, wird der Button erst gesetzt, wenn man die linke Maustaste wieder loslässt.

Mit GetScrollInfo habe ich noch Schierigkeiten, ich bin aber dran.

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 18.01.22 19:03 
In der Antwort von Delphi - Get and Set Scrollbar Position of a ListView steht, wie man GetScrollInfo aufruft.
Du mußt dann nur fMask mit SIF_TRACKPOS initialisieren und danach nTrackPos auslesen.
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Mi 19.01.22 17:14 
Das Ganze kann man aber auch einfach in der OnVerticalScroll-Prozedur unterbringen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TForm1.JvRichEdit1VerticalScroll(Sender: TObject);
var
  iYPos: Integer;
  SInfo: TScrollInfo;
begin
  SInfo.cbSize := SizeOf(SInfo);
  SInfo.fMask := SIF_ALL;
  GetScrollInfo(JvRichEdit1.Handle, SB_VERT, SInfo);
  iYPos := SInfo.nPos;

  Button2.Top := InitialPosY - iYPos;
end;

Mit SIF_TRACKPOS und nTrackPos funktioniert es nicht, SIF_ALL und nPos scheinen korrekt zu sein, denn damit klappt es.

Bleibt immer noch der Effekt, dass beim Verschieben des Scrollbalkens die Position erst nach dem Loslassen der Maustaste gesetzt wird, und bei OnMouseWheel passiert gar nichts. Ein Aufruf von JvRichEdit1VerticalScroll im OnMouseWheel setzt die Position nur ungefähr, aber nicht genau.
Aber das Prinzip funktioniert. Dank dir!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 19.01.22 17:29 
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Bleibt immer noch der Effekt, dass beim Verschieben des Scrollbalkens die Position erst nach dem Loslassen der Maustaste gesetzt wird

Und genau dafür ist doch nTrackPos (im Unterschied zu nPos).
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Mi 19.01.22 19:53 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Und genau dafür ist doch nTrackPos (im Unterschied zu nPos).

SIF_ALL und nPos: a) Pfeile der Bildlaufleiste und in die Bildlaufleiste klicken klappt, ziehen am Balken klappt nicht.
SIF_TRACKPOS und nTrackPos: b) Ziehen am Balken klappt, Pfeile der Bildlaufleiste und in die Bildlaufleiste klicken klappt nicht.
SIF_TRACKPOS und nPos: c) Beides klappt nicht.
SIF_ALL und nTrackPos: Wie b).

Mal sehen, ob es da sonst noch etwas gibt.

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Mi 19.01.22 20:17 
Hier meine vorläufige Lösung, jetzt klappt alles:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
procedure TForm1.JvRichEdit1VerticalScroll(Sender: TObject);

  procedure VScroll(Sender: TControl);
  var
    iPos, iTrackPos: Integer;
    SInfo: TScrollInfo;
  begin
    SInfo.cbSize := SizeOf(SInfo);
    SInfo.fMask := SIF_ALL;

    GetScrollInfo(JvRichEdit1.Handle, SB_VERT, SInfo);
    iPos := SInfo.nPos;
    iTrackPos := SInfo.nTrackPos;

    if iTrackPos = 0 then
        TControl(Sender).Top :=
          InitialY - iPos
    else
        TControl(Sender).Top :=
          InitialY - iTrackPos;
  end;

begin  {JvRichEdit1VerticalScroll}
  VScroll(Button2);
end;

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!