Entwickler-Ecke
Grafische Benutzeroberflächen (VCL & FireMonkey) - Listview scrollen bei Ziehen von Items
hRb - Fr 15.06.18 23:06
Titel: Listview scrollen bei Ziehen von Items
Hallo zusammen,
Unter der Frage "ImageList - schnelle Anzeige" habe ich zum Abschluss ein lauffähiges Beispiel beigefügt. Hier können Dateien durch Ziehen in der Listview-Ansicht verschoben werden. Dies funktioniert, allerdings nur mit Items im sichtbaren Bereich. Bei größerer Distanz muss ich von Hand den Scrollbalken betätigen und in Etappen verschieben.
Ich hätte gerne, dass die Listview-Ansicht automatisch weiter scrollt, wenn das Item an die Grenze gezogen wird. Gibt es hierfür eine "einfache" Lösung?
Danke und Gruß hRb
Delete - Mo 18.06.18 08:43
- Nachträglich durch die Entwickler-Ecke gelöscht -
hRb - Mo 18.06.18 23:43
Hallo Frühlingsrrolle,
Dein Ansatz ist gut. Ich hatte das DragOver Ereignis ohnehin schon im Programm und nun erweitert (siehe unten). SB_TOP bzw SB_BOTTOM scheiden natürlich aus (Anfang/Ende), aber LINEUP/LINEDOWN erschien mir nach Microsoft-Library geeignet.
Meine Überlegung: DragOver liefert als Y-Parameter die Mausposition. Ich habe mein Listview auf ein Panel gelegt und frage nun die Y-Position ab. Kommt der Wert 50 Pixel an die Grenze wird geschoben. Das funktioniert auch im Prinzig.
Das Problem: das DragOver-Ereignis löst permanent aus und so scrollen die Bilder in Windeseile zum Anfang oder Ende. Ein schön gleichmäßiger ruhiges Scrollen kommt damit nicht zustande. Man müsste also einen Timer einschalten, der das Ereignis (oder die Applikation) 1-2 Sekunden anhält, damit der User die Maus aus der 50 Pixelzone herausbewegen kann - oder etwas ähnliches. Weiterhin wird bei MS erwähnt, dass gleichzeitig der SetScrollPos-Aufruf abgesetzt werden muss, um den Scrollbalken anzupassen! Ein neues Problem, da dies ein 16- oder 32-Bit-Wert ist und nicht mit der Pixelgröße meiner Icons synchrongeht. Ich werde das weiter verfolgen. Vielleicht auch ergänzender Tipp?
Gruß hRb
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| procedure TForm1.ListView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin Accept := (Sender = Source) ; if y<=50 then SendMessage(ListView1.Handle, WM_VSCROLL, SB_LINEUP, 0) else if y>=Panel4.Height -50 then SendMessage(ListView1.Handle, WM_VSCROLL, SB_LINEDOWN, 0); end; |
Delete - Di 19.06.18 02:57
- Nachträglich durch die Entwickler-Ecke gelöscht -
hRb - Mi 20.06.18 19:00
Hallo Frühlingsrolle
Zitat: |
Da ich aus dem Ganzen nicht herauslesen kann, was genau wohin genau verschoben werden soll, kann ich dir auch keine ausreichende Hilfestellung anbieten. |
Ich will es noch mal versuchen. Habe einen Bekannten, der seine Bilder mit Picasa vorführt. Dieses Programm zeigt die Dateien eines Ordners mit wechselnder Überblendtechnik in alphabetischer Reihenfolge. Nicht immer stimmt Aufnahmereihenfolge mit gewünschter Reihenfolge beim Vorführen überein, d.h. die Bilder sollen mit meinem Programm manuel sortiert und anschließend neu numeriert (umbenannt) werden.
Man gehe hier im Forum zur Frage "ImageList - schnelle Anzeige" und lade das von mir zuletzt eingestellte Programm (zip-Datei). Nach Start und Anwahl eines Ordners mit Bildern können diese in die gewünschte Reihenfolge gebracht werden.
1. in der Stringgridtabelle mit Bild auf/ab oder
2. in der Listview-Ansicht mit der Maus verschieben
Dort nun das Problem, dass das Verschieben nur unter den unmittelbar angezeigten Icons möglich ist. Will man über den angezeigten Bereich (nach oben oder nach unten) verschieben, dann geht dies in dieser Version nur, in dem man das verschobene Bildin z.B. in der untersten Zeile ablegt und dann mit der Maus den Schrollbalken so bewegt, dass das Bild oben erscheint. Nun kann es erneut nach unten verschoben werden. Prinzip klar?
Ich möchte jedoch, dass wenn Bild über den unteren Rand hinaus gezogen wird, dass dass der Anzeigebereich zu scrollen beginnt, bis man es an der gewünschten Stelle ablegt (Ziehen mit der Maus sollte ja bekannt sein).
Mit der von mir ausgedachten Methode rollt allerdings alles viel zu schnell. Zwischenzeitlich habe ich bemerkt, dass der im Beispiel genannte Wert von 50 Pixel zu hoch ist. Ein Wert von 1 oder 2 bringt den Scorllvorgang früher "zum Stillstand". Aber sicherlich kennt jeder von Profiprogrammen das sanfte scrollen einer Vorlage.
Ich denke, spätestens wenn man mein Beispiel einmal ausprobiert (bei mehr als 20-30 Dateien in einem Ordner), wird die Schwäche beim Verschieben in der Listview-Ansicht sichtbar.
Gruß hRb
Sinspin - Do 21.06.18 09:16
Hey,
dein Ansatz ist schon ganz richtig. In den Mouse und Drag Events hohlst Du dir die Daten zusammen. In einen Timer der einmal die Sekunde oder alle halbe Sekunde feuert kommt dann die eigentliche Verarbeitung.
hRb - Mo 23.07.18 22:44
Urlaub vorbei,
Zitat: |
In einen Timer der einmal die Sekunde oder alle halbe Sekunde feuert kommt dann die eigentliche Verarbeitung. |
Die Ermunterung von Sinspin hörte sich gut an. Lösung hat aber einige Zeit gedauert. Meine Probleme waren:
1. zu erkennen, wann ein Item aus der Ansicht herausgezogen wird und
2. wenn Timer einmal gestartet, diesen wieder beenden und zwar nicht nur beim Loslassen der Maus, sondern auch wenn Scrollanfang/ende erreicht wird. Da hatte ich beim Timer längere Zeit "Dauerfeuer". Hier meine Lösung. Alle 50ms wird um 5 Picel gescrollt.
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: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58:
| private var ScrollWert:longint; ScrollEnd :boolean; procedure TForm1.ListView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); const vTime=50; var ScrollMinPos, ScrollMaxPos, Pos :LongInt; begin Accept := (Sender = Source) ;
Pos:=GetScrollPos(ListView1.Handle,SB_VERT); GetScrollRange(ListView1.Handle,SB_VERT,ScrollMinPos, ScrollMaxPos); if ((y<=50) and (Pos > ScrollMinPos)) then begin ScrollWert:=-5; Timer2.Interval:=vTime; Timer2.Enabled:=true; ScrollEnd:=false; end else if ((y>=Listview1.Height-50) and (Pos+5 <ScrollMaxPos)) then begin ScrollWert:=5; Timer2.Interval:=vTime; Timer2.Enabled:=true; ScrollEnd:=false; end else begin ScrollEnd:=true; Timer2.Enabled:=false; end; end;
procedure TForm1.ListView1EndDrag(Sender, Target: TObject; X, Y: Integer); begin ScrollEnd:=true; end;
procedure TForm1.Timer2Timer(Sender: TObject); var ScrollMinPos, ScrollMaxPos, Pos :LongInt; begin Pos:=GetScrollPos(ListView1.Handle,SB_VERT); GetScrollRange(ListView1.Handle,SB_VERT,ScrollMinPos, ScrollMaxPos); if (((Scrollwert>=0) and (Pos+5 <= ScrollMaxPos)) or ((Scrollwert<=0) and (Pos-5 >= ScrollMinPos))) and (ScrollEnd=false) then begin ListView1.Scroll(0,ScrollWert); Pos:=GetScrollPos(ListView1.Handle,SB_VERT); Listview1.Repaint; if ((pos+5 <=ScrollMinPos) or (pos-5 >=ScrollMaxPos) or (ScrollEnd=true)) then Timer2.Enabled:=false; end else Timer2.Enabled:=false; end; |
Hinweis1: Die beiden Proceduren muss man eingebettet sehen in meine Anwendung, die ich früher bereit gestellt habe.
Hinweis2 zu den Funktionen GetScrollPos und GetScrollRange: Aus mir nicht nachvollziehbaren Gründen liefert der Wert Pos -wenn der Scrollbalken am Ende steht- nicht den Wert ScrollMaxPos. Hierdurch kann es zu unnötigen Repaint-Aufrufen kommen.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!