Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Nachricht NACH dem Löschen eines ListView.Item erhalten? (https://www.delphipraxis.net/169696-nachricht-nach-dem-loeschen-eines-listview-item-erhalten.html)

PeterPanino 5. Aug 2012 19:57

Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Hallo! Ich möchte eine Nachricht von einem Listview erhalten, NACHDEM ein Item aus der Listview gelöscht wurde. Ich habe folgenden Code probiert:
Delphi-Quellcode:
private
    { Private-Deklarationen }
    OriginalListViewWindowProc: TWndMethod;
    procedure ListViewWindowProcEx(var Message: TMessage);

procedure TForm1.FormCreate(Sender: TObject);
begin
  OriginalListViewWindowProc := MyListView1.WindowProc;
  MyListView1.WindowProc := ListViewWindowProcEx;
end;

procedure TForm1.ListViewWindowProcEx(var Message: TMessage);
begin
  if Message.Msg = CN_NOTIFY then
  begin
    if PNMHdr(Message.LParam)^.Code = LVN_DELETEITEM then
    begin
      // Funktioniert nicht, da das Item gelöscht wird,
      // NACHDEM die Nachricht gesendet wurde:
      Self.Caption := IntToStr(MyListView1.Items.Count);
    end;
  end;
  OriginalListViewWindowProc(Message);
end;
Es sollte die Anzahl der Items NACH dem Löschen angezeigt werden. Mit dem obigen Code wird aber die Anzahl der Items VOR dem Löschen des Items angezeigt!

Wie kann man also eine Nachricht NACH dem Löschen eines Items erhalten?

Bummi 5. Aug 2012 20:09

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Delphi-Quellcode:
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls,CommCtrl, StdCtrls;

type
  TListView=Class(comCtrls.TListView)
        procedure CNNotify(var Message: TWMNotifyLV); message CN_NOTIFY;

  End;
  TForm2 = class(TForm)
    ListView1: TListView;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

{ TListView }

procedure TListView.CNNotify(var Message: TWMNotifyLV);
begin
   inherited;
   if Message.NMHdr.code=LVN_DELETEITEM then
     if Message.Result=1 then Showmessage('Deleted');


end;

PeterPanino 5. Aug 2012 21:16

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Hallo Bummi, vielen Dank für den Code. Aber leider ist das Problem damit nicht gelöst: Die Anzahl der Items ist beim Erhalt der Nachricht gleich groß wie vorher. Sie müsste aber um die Zahl des gelöschten Items vermindert sein, in dieser Reihenfolge:

1. Anzahl der Items vorher ist n

2. 1 Item wird gelöscht

3. Nachricht sagt, Anzahl der Items ist nun n - 1

Es müsste also eine Nachricht sein, die NACH dem Löschen des Items die verminderte Zahl der Items zurückgibt.

Oder, wenn mehrere Items auf einmal gelöscht werden:

1. Anzahl der Items vorher ist n

2. x Items werden gelöscht

3. Nachricht sagt, Anzahl der Items ist nun n - x

haentschman 5. Aug 2012 21:36

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Mache dir doch ein eigenes Event was dir nach dem Löschen Befehl den neuen Count liefert. Was ist mit OnChange, OnDeletion des Listviews. Nach jedem Ändern bzw. Löschen wird es ausgelöst. Kannst du damit was anfangen ?

Irgendwie habe ich den Eindruck daß zu kompliziert gedacht wird. :gruebel: ... oder ich habe die Anforderung nicht richtig verstanden.

PeterPanino 5. Aug 2012 21:48

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von haentschman (Beitrag 1176987)
Mache dir doch ein eigenes Event was dir nach dem Löschen Befehl den neuen Count liefert. Was ist mit OnChange, OnDeletion des Listviews. Nach jedem Ändern bzw. Löschen wird es ausgelöst. Kannst du damit was anfangen ?

Irgendwie habe ich den Eindruck daß zu kompliziert gedacht wird. :gruebel: ... oder ich habe die Anforderung nicht richtig verstanden.

OnDeletion wird genauso VOR der Verminderung der Items ausgelöst. OnChange ist ganz was anderes.

Sir Rufo 5. Aug 2012 21:56

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Wenn du die Anzahl der Items nach dem Löschen ausgeben möchtest, dann ist OnChange genau das Event, was du suchst. Dieses Ereignis wird nach jeder Änderung am ListView aufgerufen.

Du kannst also auch Items hinzufügen, alle Items auf einmal löschen, whatever, das Event wird anschließend abgefeuert.

PeterPanino 5. Aug 2012 22:10

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1176989)
Wenn du die Anzahl der Items nach dem Löschen ausgeben möchtest, dann ist OnChange genau das Event, was du suchst. Dieses Ereignis wird nach jeder Änderung am ListView aufgerufen.

Du kannst also auch Items hinzufügen, alle Items auf einmal löschen, whatever, das Event wird anschließend abgefeuert.

Nein. OnChange wird bei einem Selection-Wechsel aufgerufen. Je nach Anzahl der Items im Listview findet ein Selection-Wechsel nach dem Löschen von Items statt. Nimm aber ein Listview mit nur wenigen Items und lösche das unterste Item (Report-View), sodass kein Selection-Wechsel stattfindet: OnChange wird dann nicht aufgerufen.

PeterPanino 5. Aug 2012 22:23

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
http://docwiki.embarcadero.com/Libra...TLVChangeEvent

Hier steht nichts von gelöschten Items.

jaenicke 5. Aug 2012 22:25

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
1. Nimm einfach eine "richtige" Komponente, nämlich z.B. die TVirtualStringTree:
http://www.delphi-gems.com/index.php...rtual-treeview
Dort gibt es das Event OnFreeNode, in dem die Anzahl bereits die neue ist:
Delphi-Quellcode:
procedure TForm236.VirtualStringTree1FreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
begin
  Caption := IntToStr(Sender.RootNode.ChildCount);
end;
Außerdem ist die Komponente auch deutlich schneller (und einfacher zu benutzen sobald man die GUI ordentlich von der Datenhaltung trennt).

2. Da du an der Stelle weißt, dass das Item gelöscht werden wird, kannst du doch auch einfach eins von der Anzahl abziehen.

PeterPanino 5. Aug 2012 22:33

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von jaenicke (Beitrag 1176993)
2. Da du an der Stelle weißt, dass das Item gelöscht werden wird, kannst du doch auch einfach eins von der Anzahl abziehen.

Das Programm weiß ja nicht, wie viele Items der Benutzer löscht. Das sollte das Programm NACH dem Löschen ja erfahren. Tut es aber nicht. Deswegen meine Fragestellung.

Bummi 5. Aug 2012 22:50

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Items.Count wird auch über eine Message abgerufen, zu einem Zeitpunkt zu dem das Messagehandling in dem Fall noch nicht abgeschlossen ist. "Ausbrechen" könnte man in der Art, zum weiterbasteln...
Delphi-Quellcode:
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls,CommCtrl, StdCtrls;

  const
  WM_MyInfoMessage=WM_USER + 777;
type


  TListView=Class(comCtrls.TListView)
       private
    FOnItemDeleted: TNotifyEvent;
        procedure CNNotify(var Message: TWMNotifyLV); message CN_NOTIFY;
        Procedure MyInfomessage(var Message: TWMNotify);message WM_MyInfoMessage;
       published
        Property OnItemDeleted:TNotifyEvent read FOnItemDeleted write FOnItemDeleted;
  End;
  TForm2 = class(TForm)
    ListView1: TListView;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    procedure OnItemDel(sender: TObject);
    { Private-Deklarationen }

  public
    { Public-Deklarationen }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

{ TListView }



procedure TListView.CNNotify(var Message: TWMNotifyLV);
begin
   inherited;
   if (Message.NMHdr.code = LVN_DELETEITEM) or (Message.NMHdr.code = LVN_DELETEALLITEMS ) then
     if Message.Result=1 then
         begin
            PostMessage(Handle,WM_MyInfoMessage,0,0);
         end;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  Showmessage('Before:'+IntToStr(ListView1.Items.Count));
  ListView1.Items.Delete(0);
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  ListView1.OnItemDeleted := OnItemDel;
end;

Procedure TForm2.OnItemDel(sender:TObject);
begin
   Showmessage('After:' + IntToStr(TListView(Sender).Items.Count));
end;

procedure TListView.MyInfomessage(var Message: TWMNotify);
begin
   if Assigned(FOnItemDeleted) then FOnItemDeleted(Self);


end;

end.

PeterPanino 6. Aug 2012 00:00

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von Bummi (Beitrag 1176996)
Items.Count wird auch über eine Message abgerufen, zu einem Zeitpunkt zu dem das Messagehandling in dem Fall noch nicht abgeschlossen ist. "Ausbrechen" könnte man in der Art, zum weiterbasteln...

Phantastisch, es scheint zu funktionieren!!!

Es ist mir aber noch nicht ganz klar, wieso. Du schickst beim Erhalt der LVN_DELETEITEM message einfach nochmals eine User-Message an das eigene Fenster. Wieso wird die dann erst NACH dem eigentlichen Delete-Ereignis verarbeitet, wenn die LVN_DELETEITEM Message noch VOR dem Delete gesendet wird? Hat das vielleicht etwas mit der Message-Warteschlange zu tun?

Bummi 6. Aug 2012 00:08

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Delphi-Quellcode:
   inherited; // hier ist schön gelöscht (Originalmessagehandling abgeschlossen), aber wir sind noch in der "Schlange"
   if Message.NMHdr.code=LVN_DELETEITEM then
     if Message.Result=1 then // Result wird nach dem Löschen im inherited gesetzt
hat mit der "Wartesschlange" zu tun , deshalb Postmessage statt SendMessage.
BTW ich hatte es nochmals geändert, die Message wird jetzt innerhalb des LV verarbeitet, falls Du das ganze für eine Komponente brauchen solltest.

PeterPanino 6. Aug 2012 00:22

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Weil PostMessage wartet, bis die Nachricht verarbeitet wurde, und deshalb das das Delete-Ereignis dann schon abgearbeitet ist?

Du meinst, dass die User-Message nicht an das Fenster, sondern an die eigene ListView gesendet wird?

VIELEN, VIELEN HERZLICHEN DANK!!! Ich spendiere dir hiermit ein frisches, kühles virtuelles Lager-Pils!

Bummi 6. Aug 2012 00:58

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
SendMessage wartet, Postmessage nicht, es wird einfach in die Schlange eingereiht...

habe nochmals etwas umgebaut ....

jaenicke 6. Aug 2012 06:33

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von PeterPanino (Beitrag 1176994)
Zitat:

Zitat von jaenicke (Beitrag 1176993)
2. Da du an der Stelle weißt, dass das Item gelöscht werden wird, kannst du doch auch einfach eins von der Anzahl abziehen.

Das Programm weiß ja nicht, wie viele Items der Benutzer löscht. Das sollte das Programm NACH dem Löschen ja erfahren. Tut es aber nicht. Deswegen meine Fragestellung.

Naja, du bekommst aber bei jedem gelöschten Item eine solche Message. Das gilt auch z.B. für Clear, wenn du alle löschst...

Wie löschst du denn sonst mehrere auf einmal?

(Nebenbei kann der Benutzer selbst ohnehin nix löschen, sondern nur der Programmierer mit dem passenden Befehl, so dass der Zeitpunkt danach ohnehin feststeht und erreichbar ist...)

PeterPanino 6. Aug 2012 07:34

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von jaenicke (Beitrag 1177018)
Naja, du bekommst aber bei jedem gelöschten Item eine solche Message.

Naja, streng genommen ist es - vom Zeitpunkt der Nachricht her - eine Vorhersage (z.B. bei OnDeletion). Mit der Methode von Bummi wird der Nachrichtenzeitpunkt auf die Zeit NACH der Aktion verschoben. Das ist vom Standpunkt des Benutzers aus überzeugender.

Furtbichler 6. Aug 2012 07:36

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Wie kann ein Anwender Einträge aus einer ListView löschen? Welche Tasten muss man da drücken?

Bummi 6. Aug 2012 07:37

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Leider bei Clear nicht hier wäre noch LVN_DELETEALLITEMS zu behandeln...
Delphi-Quellcode:
   if (Message.NMHdr.code = LVN_DELETEITEM) or (Message.NMHdr.code = LVN_DELETEALLITEMS ) then

PeterPanino 6. Aug 2012 07:44

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
@Bummi: Wenn es nun aber mehrere Instanzen der so modifizierten TListView im Programm gibt: Wie könnte man erfahren, welche Instanz die Message gesendet hat? Wie würdest du diese Information so in PostMessage kodieren, dass sie in MyInfomessage wieder dekodiert werden kann? Und wie kann CNNotify erfahren, von welcher Instanz die Nachricht kommt?

Bummi 6. Aug 2012 07:48

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Ich hatte meinen Beitrag 2 mal geändert, in der letzen Version gibt es ein OnItemDeleted-Event, hier kannst Du über Sender gehen ....

PeterPanino 6. Aug 2012 08:09

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von Bummi (Beitrag 1177024)
Ich hatte meinen Beitrag 2 mal geändert, in der letzen Version gibt es ein OnItemDeleted-Event, hier kannst Du über Sender gehen ....

Ja, du hast Recht, vielen Dank!

PeterPanino 6. Aug 2012 09:22

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von Furtbichler (Beitrag 1177021)
Wie kann ein Anwender Einträge aus einer ListView löschen? Welche Tasten muss man da drücken?

Zitat:

Zitat von Bummi (Beitrag 1176996)
Delphi-Quellcode:
ListView1.Items.Delete(0);

Oder in von ListView abgeleiteten Klassen könnte es andere Möglichkeiten geben, ein Item zu löschen.

TiGü 6. Aug 2012 09:39

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von PeterPanino (Beitrag 1177032)
Zitat:

Zitat von Furtbichler (Beitrag 1177021)
Wie kann ein Anwender Einträge aus einer ListView löschen? Welche Tasten muss man da drücken?

Zitat:

Zitat von Bummi (Beitrag 1176996)
Delphi-Quellcode:
ListView1.Items.Delete(0);

Oder in von ListView abgeleiteten Klassen könnte es andere Möglichkeiten geben, ein Item zu löschen.

Anwender <> Programmierer!

PeterPanino 6. Aug 2012 11:01

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von TiGü (Beitrag 1177035)
Anwender <> Programmierer!

Korrektur: Oder in von ListView abgeleiteten Klassen könnte es für den Programmierer andere Möglichkeiten geben, dem Anwender die Löschung eines Items zu ermöglichen.

jaenicke 6. Aug 2012 12:34

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von PeterPanino (Beitrag 1177073)
Korrektur: Oder in von ListView abgeleiteten Klassen könnte es für den Programmierer andere Möglichkeiten geben, dem Anwender die Löschung eines Items zu ermöglichen.

Sicher, aber das ist genau der Punkt:
Der Befehl kommt aus der Anwendung. Dort wird ja explizit das Löschen aufgerufen.

Deshalb wird dafür eigentlich gar kein Event benötigt, da Events nur gebraucht werden, wenn der Programmierer etwas mitbekommen muss, von dem er sonst nicht mitbekommen würde, dass es passiert. Da die Aktion hier selbst per Quelltext gestartet wird, weiß man das aber schon.

Deshalb ist die Logik schon etwas seltsam, aber letztlich schadet ein solches Event auch nicht. Es ist nur rein logisch überflüssig.

Damit ist die Lösung, die du jetzt benutzt, schon ok, wenn auch nicht optimal.

PeterPanino 6. Aug 2012 12:52

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von jaenicke (Beitrag 1177082)
Sicher, aber das ist genau der Punkt:
Der Befehl kommt aus der Anwendung. Dort wird ja explizit das Löschen aufgerufen.

Deshalb wird dafür eigentlich gar kein Event benötigt, da Events nur gebraucht werden, wenn der Programmierer etwas mitbekommen muss, von dem er sonst nicht mitbekommen würde, dass es passiert. Da die Aktion hier selbst per Quelltext gestartet wird, weiß man das aber schon.

Deshalb ist die Logik schon etwas seltsam, aber letztlich schadet ein solches Event auch nicht. Es ist nur rein logisch überflüssig.

Damit ist die Lösung, die du jetzt benutzt, schon ok, wenn auch nicht optimal.

Wenn der Anwender etwas will, dann heißt das nicht, dass das Programm das auch tut. Deshalb ist es bei einer "Vorhersage" gut möglich, dass die Anzeige vorschnell eine falsche Änderung anzeigt, diese aber aus irgendeinem Grund nicht durchgeführt werden konnte. Das kann dann zu einem Programm-, Anwendungs- oder Daten-Fehler führen. Auch ist denkbar, dass neben dem Anwender und dem Programm eine dritte Ebene (z.B. das Dateisystem oder eine Datenbank) beteiligt ist, welche wiederum in die Ausführung eingreifen kann. Aus all diesen Gründen ist es im allgemeinenn ratsam, nur mit Tatsachen zu arbeiten. Etwa bei dem Listview-Beispiel abzuwarten, bis Items.Count wirklich um die Zahl der gelöschten Items vermindert wurde. Alles andere ist unsauberer Programmierstil. Aus diesem Grund gibt es bei Datenbanken auch das Transaktions-Prinzip, um Inkonsistenzen zwischen verschiedenen Bereichen (etwa Client und Server) zu verhindern.

jaenicke 6. Aug 2012 13:46

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Darum geht es ja gerade gar nicht. Der Punkt ist, dass der Quelltext so aussieht:
Delphi-Quellcode:
procedure A;
begin
  xy.DeleteItem;
end;

procedure OnDeleted;
begin
  ShowMessage('Ha, gelöscht');
end;
Statt ganz einfach und simpel:
Delphi-Quellcode:
procedure DeleteMyItem;
begin
  xy.DeleteItem;
  ShowMessage('Ha, gelöscht');
end;
Aber wenn es dir so lieber ist, ist das ja deine Sache. Genauso die Wahl der Komponente an sich. :wink:

PeterPanino 6. Aug 2012 13:55

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von jaenicke (Beitrag 1177093)
Der Punkt ist, dass der Quelltext so aussieht:

Bei mir sieht der Quelltext ganz anders aus ... ;-)

Und ich verwende ja in meinem Code auch nicht TListView, sondern eine davon abgeleitete Klasse. Dass wir hier bei der Diskussion TListView nennen, ist ja nur eine Abstraktion.

Iwo Asnet 6. Aug 2012 16:08

AW: Nachricht NACH dem Löschen eines ListView.Item erhalten?
 
Zitat:

Zitat von PeterPanino (Beitrag 1177085)
Aus all diesen Gründen ist es im allgemeinenn ratsam, ... bei dem Listview-Beispiel abzuwarten, bis Items.Count wirklich um die Zahl der gelöschten Items vermindert wurde. Alles andere ist unsauberer Programmierstil. Aus diesem Grund gibt es bei Datenbanken auch das Transaktions-Prinzip, um Inkonsistenzen zwischen verschiedenen Bereichen (etwa Client und Server) zu verhindern.

Du wirst zwar etwas durcheinander, aber letztendlich hältst Du Deine Vorgehensweise für guten Programmierstil. Ich nicht, und zwar aus folgendem Grund:

Der ListView-Container ist eine Abbildung/Darstellung einer Liste. Du führst die Löschoperationen jedoch auf der Liste durch, und nicht auf der ListView selbst. Ich würde also der Liste (also der Datenstruktur) das Event "OnDeleted" bzw. "OnItemCountChanged" spendieren bzw. dort ansetzen.

Grundsätzlich ist deine Vorgehensweise aber ok, denn in deiner Sichtweise wird die ListView verändert und soll immer und überall eine verbindliche und korrekte Aussage über die in ihr gespeicherten Daten geben können (hier: Anzahl), und zwar irgendwann auch, wenn die Löschoperation abgeschlossen ist. Derart (korrekt) mitteilsam ist die LV leider nicht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:32 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz