Discussion:
[Delphi 10] Fehlerhafte Behandlung von MouseUp-Ereignissen in TListView
(zu alt für eine Antwort)
Michael Landenberger
2017-11-26 08:30:10 UTC
Permalink
Hallo,

eine von TListView abgeleitete Komponente im vsReport-Stil reagiert nur sehr
unzuverlässig auf MouseUp-Ereignisse. Die Ursache des Problems scheint tiefer
zu liegen, denn die Komponente empfängt sporadisch auch keine WM_LBUTTONUP-
und WM_RBUTTONUP-Botschaften (das hat ein testweiser Einbau einer
Behandlungsroutine für diese Botschaften ergeben). Schlimmer noch: selbst die
mit Delphi mitgelieferte, originale TListView-Komponente ist davon betroffen
:-/ Der Fehler tritt in Delphi 10.1 (Berlin) und 10.2 auf, allerdings
eigenartigerweise nur dann, wenn die Eigenschaft MultiSelect der Komponente
auf True gesetzt ist. Bei Multiselect = False werden MouseUp-Ereignisse
zuverlässig verarbeitet.

Im Netz habe ich keine brauchbaren Infos zu diesem Problem gefunden. Was
könnte die Ursache für diesen Fehler sein? An einer Lösung wäre ich sehr
interessiert, weil es der Fehler quasi unmöglich macht, Kontextmenü- und
Drag&Drop-Funktionalitäten zu realisieren.

Gruß

Michael
Michael Landenberger
2017-11-26 16:00:09 UTC
Permalink
Post by Michael Landenberger
eine von TListView abgeleitete Komponente im vsReport-Stil reagiert nur sehr
unzuverlässig auf MouseUp-Ereignisse.
Das Problem nervt :-( Ich habe eben ein simples Testprojekt mit einem Formular
angelegt, auf das ich ein TListView mit ViewStyle = vsReport gesetzt habe.
Unter Windows 10 (1709) lässt sich das Problem reproduzieren: wenn die
Eigenschaft MultiSelect des Listviews auf TRUE gesetzt wird, wird ca. die
Hälfte aller MouseDown- und MouseUp-Events einfach ignoriert. Bei Multiselect
= FALSE verhält sich das Listview dagegen ganz normal, alle Mausaktionen
werden ordnungsgemäß ausgewertet.

Um der Sache auf den Grund zu gehen, habe ich in meiner Komponente den Handler
für WM_LBUTTONDOWN-Botschaften überschrieben. Testweise habe ich in den
überschriebenen Handler vor dem Aufruf des vererbten Handlers einen Befehl zum
Simulieren des Loslassens der linken Maustaste (mittels SendInput ())
eingebaut. Dabei stellte sich heraus, dass bei diesem simulierten Loslassen
*keine* WM_LBUTTONUP-Botschaft an die TListView-Komponente geschickt wird. Das
ist unnormal (normalerweise generieren auch simulierte Benutzeraktionen die
entsprechenden Botschaften). Auf das anschließende, tatsächliche Loslassen der
linken Maustaste hat das Programm dann aber ganz normal reagiert.

Jetzt habe ich folgenden Workaround programmiert, der zwar funktioniert, bei
dem mir aber nicht sonderlich wohl ist:

1. Zunächst habe ich meiner Komponente zwei private-Eigenschaften verpasst:
eine Boolean-Eigenschaft "FButtonDown" und eine Integer-Eigenschaft
"FApplyMouseFix". Letztere wird beim Erstellen der Komponente auf 0
initialisiert.

2. Ich habe die Botschafts-Handler WMLButtonDown und WMLButtonUp in meiner
Komponente überschrieben. In WMLButtonDown wird FButtonDown auf TRUE gesetzt,
in WMLButtonUp wieder auf FALSE. Danach wird der geerbte Handler aufgerufen,
um das normale Verhalten der Komponente sicherzustellen.

3. In WMLButtonDown wird zusätzlich FApplyMouseFix abgefragt. Die Eigenschaft
kann 3 gültige Werte annehmen: 0 bedeutet, dass das Vorhandensein des Fehlers
geprüft werden soll. Tritt er *nicht* auf, wird FApplyMouseFix auf 1 gesetzt.
Dieser Wert bedeutet, dass keine erneute Prüfung stattfinden und der
Workaround nicht angewendet werden soll. Ist der Fehler jedoch vorhanden, wird
FApplyMouseFix auf 2 gesetzt. Bei diesem Wert erfolgt ebenfalls keine erneute
Prüfung, allerdings wird dann aber bei jedem Drücken der linken Maustaste der
Workaround angewandt.

4. Die Prüfung auf Vorhandensein des Fehlers habe ich so realisiert:
Beim Drücken der linken Maustaste wird innerhalb des Botschafts-Handlers
WMLButtonDown mittels SendInput () ein Loslassen der linken Maustaste
simuliert. Normalerweise generiert das eine WM_LBUTTONUP-Botschaft, welche in
WMLButtonUp ausgewertet wird. Dort wird, wie geschrieben, die Eigenschaft
FButtonDown auf FALSE gesetzt. Ist die Eigenschaft nach dem simulierten
Loslassen aber immer noch TRUE, dann weiß meine Komponente, dass
Mausbotschaften nicht korrekt verarbeitet werden, d. h. dass der Fehler
vorhanden ist. In diesem Fall wird FApplyMouseFix auf 2 gesetzt, mit den unter
3. beschriebenen Konsequenzen. Ist FButtonDown dagegen FALSE, nachdem das
Loslassen simuliert wurde, ist alles ok. In diesem Fall setzt das Programm
FApplyMouseFix auf 1. Zusätzlich wird einmalig ein weiterer Druck auf die
linke Maustaste simuliert, denn ursprünglich wurde diese ja vom Benutzer
gedrückt und die zum Testen erfolgte Simulation des Loslassens soll das nicht
unterlaufen.

5. Bei jedem weiteren Drücken der linken Maustaste wird dann in Abhängigkeit
des Wertes in FApplyMouseFix entweder ein Loslassen der Taste simuliert (als
Workaround) oder nicht.

Entsprechendes habe ich auch für die mittlere und rechte Maustaste
programmiert, denn auch die sind von dem Problem betroffen.

Lange Rede, kurzer Sinn: jetzt macht die Komponente, was sie soll. Allerdings
kann ich nicht glauben, dass man dazu auf einen solchen Hack angewiesen ist.
Es muss noch eine andere Lösung des Problems geben. Was mich auch erstaunt:
man liest im Netz nichts über dieses Problem. Ich kann mir aber nicht
vorstellen, dass es nur auf meinem Rechner und in meiner Konstellation
(Windows 10 1709 mit Delphi 10.1/10.2) auftritt.

Gruß

Michael
Alfred Gemsa
2017-11-26 20:41:38 UTC
Permalink
Post by Michael Landenberger
Lange Rede, kurzer Sinn: jetzt macht die Komponente, was sie soll. Allerdings
kann ich nicht glauben, dass man dazu auf einen solchen Hack angewiesen ist.
man liest im Netz nichts über dieses Problem. Ich kann mir aber nicht
vorstellen, dass es nur auf meinem Rechner und in meiner Konstellation
(Windows 10 1709 mit Delphi 10.1/10.2) auftritt.
Ich habe es kurz überprüft: auch D7 verhält sich so.
Außerdem gibt's einige Artikel im Netz, die dasselbe bemäkeln. Dazu
gibt's auch sporadisch Vorschläge zu Workaraounds, ob die was taugen,
weiß ich nicht:

https://www.codenewsfast.com/cnf/thread/0/permalink.thr-ng1942q4838
http://www.delphigroups.info/2/10/310307.html

usw.

Gruß, Alfred.
Hans-Peter Diettrich
2017-11-28 09:29:31 UTC
Permalink
Post by Alfred Gemsa
Post by Michael Landenberger
man liest im Netz nichts über dieses Problem. Ich kann mir aber nicht
vorstellen, dass es nur auf meinem Rechner und in meiner Konstellation
(Windows 10 1709 mit Delphi 10.1/10.2) auftritt.
Ich habe es kurz überprüft: auch D7 verhält sich so.
Vermutlich liegt das am Verhalten des Windows-Controls, das mit Win10
ausgeliefert wird, das betrifft dann alle Delphi Versionen.

DoDi
Michael Landenberger
2017-11-28 10:15:06 UTC
Permalink
Post by Hans-Peter Diettrich
Vermutlich liegt das am Verhalten des Windows-Controls, das mit Win10
ausgeliefert wird, das betrifft dann alle Delphi Versionen.
Eine ListView-Komponente in einem anderen Delphi-Programm, welche nicht von
TListView, sondern direkt von TWinControl abgeleitet wurde, funktioniert auch
unter Windows 10 einwandfrei. Allerdings wurde dieses Programm nicht mit
Delphi 10, sondern mit Delphi 4 erstellt.

Gruß

Michael
Matthias Eißing
2017-11-28 12:53:35 UTC
Permalink
Post by Michael Landenberger
Im Netz habe ich keine brauchbaren Infos zu diesem Problem gefunden. Was
könnte die Ursache für diesen Fehler sein? An einer Lösung wäre ich sehr
interessiert, weil es der Fehler quasi unmöglich macht, Kontextmenü- und
Drag&Drop-Funktionalitäten zu realisieren.
Kann ich nachvollziehen.... ist aber kein Delphi-Problem:
https://stackoverflow.com/questions/30204761/wm-lbuttonup-is-not-received-in-my-subclass-procedure

Nach Microsoft ist das normal:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb774734.aspx
(Unter WM_LBUTTONDOWN)

Mit den Stichworten
WM_LBUTTONUP listview
findet man da einiges in der allwissenden Müllhalde.
--
cu://Matthias.Eißing.de
Loading...