AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Delphi Tile-Modus und Gruppierung von List-View-Items (WinXP)
Tutorial durchsuchen
Ansicht
Themen-Optionen

Tile-Modus und Gruppierung von List-View-Items (WinXP)

Ein Tutorial von MathiasSimmack · begonnen am 2. Mär 2003 · letzter Beitrag vom 29. Jun 2007
Antwort Antwort
MathiasSimmack
Quasi als DP-Exklusivveröffentlichung (wenn ihr so wollt) gibt´s hier die VCL-Variante der o.g. neuen XP-Gimmicks. Meine Ausführungen beziehen sich aber hauptsächlich auf Delphi vor Version 7. Ich nehme an bzw. hoffe, dass D7 diese Neuheiten bereits eingebaut hat. Also zeige ich hier, wie Besitzer von Delphi 5 (wie in meinem Fall) diese neuen Sachen nutzen können.

Für alle, deren Delphi nicht aktuell genug ist, liegt im Anhang meine Unit "CommCtrl_Fragment.pas", die die notwendigen Deklarationen usw. enthält. Sie ist kein Ersatz für die CommCtrl von Borland; sie ist also zusätzlich anzugeben!


Achtung!
Für alle drei Sachen, die nachfolgend besprochen werden, ist die Manifestdatei erforderlich! Das gilt auch, wenn man WinXP im Klassik-Look laufen lässt. Sagt hinterher nicht, ich hätte es nicht erwähnt. Das Manifest, ob nun beiliegend (Dateiname.exe.manifest) oder in den Ressourcen, sorgt dafür, dass die Common Controls 6.0 benutzt werden. Und nur damit funktioniert alles!
Angehängte Dateien
Dateityp: pas commctrl_fragment.pas (26,0 KB, 230x aufgerufen)
 
MathiasSimmack
 
#2
  Alt 2. Mär 2003, 18:57
Um z.B. den neuen Tile-Modus nutzen zu können, solltet ihr unter Win XP nach Möglichkeit die neue Nachricht "LVM_SETVIEW" bzw. die Funktion "ListView_SetView" benutzen, für die es die folgenden Werte gibt:
Code:
LV_VIEW_ICON
LV_VIEW_SMALLICON
LV_VIEW_LIST
LV_VIEW_DETAILS
LV_VIEW_TILE
Also, z.B.
ListView_SetView(lv1.Handle,LV_VIEW_TILE); Ich sag´s lieber noch einmal: A) diese Anweisung funktioniert nur unter Windows XP, und B) auch nur, wenn ein Manifest für die Common Controls 6.0 vorhanden ist. Soll eure Anwendung also unter allen Windows-Version laufen, unter XP aber diese zusätzlichen Features benutzen, dann müsst ihr (Non-VCL) sowohl den alten als auch den eben gezeigten neuen Weg zum Wechseln der Ansicht einbauen.
Ich hoffe, Delphi 7 unterstützt (wenn überhaupt) beides, so dass es mit der D7-VCL leichter ist.

Zu Nachricht und Funktion gibt es natürlich auch die Gegenstücke: "LVM_GETVIEW" und "ListView_GetView", mit denen man ermitteln kann, welche Ansicht momentan aktiv ist.
  Mit Zitat antworten Zitat
MathiasSimmack
 
#3
  Alt 2. Mär 2003, 18:58
Das einfachste zuerst: Ihr kennt sicher den Effekt aus dem Explorer von Windows XP, bei dem die aktuelle Spalte grau markiert wird. Dazu ist keine Zeichenfunktion erforderlich, das OS stellt mit der Nachricht "LVM_SETSELECTEDCOLUMN" (bzw. "ListView_SetSelectedColumn") die notwendige Funktionalität zur Verfügung.
Man übergibt lediglich den Index der gewünschten Spalte; beim Start des Programms bspw.:
ListView_SetSelectedColumn(TListview1.Handle,0); Wenn man eine Sortierfunktion hat, bietet sich natürlich das "OnColumnClick"-Ereignis an, um die Spaltenmarkierung zu wechseln:
Delphi-Quellcode:
procedure TForm1.lv1ColumnClick(Sender: TObject; Column: TListColumn);
begin
  { ... sortieren ... }

  // sortierte Spalte kennzeichnen
  ListView_SetSelectedColumn(lv1.Handle,Column.Index);
end;
Anders herum geht´s auch: mit "LVM_GETSELECTEDCOLUMN" oder "ListView_GetSelectedColumn" könnt ihr ermitteln, welche Spalte sozusagen den Fokus hat.

Soviel dazu.
  Mit Zitat antworten Zitat
MathiasSimmack
 
#4
  Alt 2. Mär 2003, 18:59
Der Tile-Modus ist ebenfalls aus dem Explorer bekannt. Man sieht dabei das Icon, und rechts daneben steht der Dateiname, und darunter (abhängig vom Typ) noch zusätzliche Infos. Zur Benutzung müssen wir der List-View erst mitteilen, wie wir uns den Kachelmodus vorstellen. Dazu gibt´s das Record TLVTileViewInfo, das wir am besten im "OnCreate" der Form füllen:
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  tileview : TLVTileViewInfo; // CommCtrl_Fragment.pas
begin
  // Recordgröße initialisieren
  tileview.cbSize := sizeof(TLVTileViewInfo);

  // was wollen wir beeinflussen? die Anzahl der zusätzlichen Zeilen
  tileview.dwMask := LVTVIM_COLUMNS;

  // die Tiles sollen sich möglichst automatisch anpassen
  tileview.dwFlags := LVTVIF_AUTOSIZE;

  // wie viele zusätzliche Zeilen sollen es denn sein?
  tileview.cLines := 2;

  // und ab damit zur List-View
  ListView_SetTileViewInfo(lv1.Handle,tileview);
end;
Wichtig erscheint mir diese Zeile:
  tileview.cLines := 2; Damit wird der List-View mitgeteilt, wie viele zusätzliche Zeilen im Kachelmodus zu sehen sein sollen. Das bedeutet: zusätzlich zum Itemnamen (TListItem.Caption), der ja sowieso und in jedem Darstellungsmodus angezeigt wird!

Nehmen wir als Beispiel einen kleinen Explorer, der die Dateien des aktuellen Ordners anzeigt. Im Reportmodus zeigt er außerdem in der Spalte #1 die Dateigröße und in Spalte #2 das Datum. Was wir davon im Kachelmodus sehen, hat ursächlich mit diesen Spalten im Reportmodus zu tun.
Anzumerken ist noch, dass der Kachelmodus für jedes Item separat eingestellt werden muss. Das hört sich umständlich an, bietet aber auch den Vorteil, die Ansicht direkt zu beeinflussen. Ich zeige ein Mini-Beispiel aus dem "OnCreate" der Form:
Delphi-Quellcode:
res := FindFirst(ExtractFilePath(paramstr(0)) + '*.*',
  faAnyFile,ds);

while(res = 0) do begin
  if(ds.Attr and faDirectory = 0) then begin
    // wie gehabt, füllen wir die List-View
    lv := lv1.Items.Add;
    lv.Caption := ds.Name;
    lv.ImageIndex := 0;
    lv.SubItems.Add(inttostr(ds.Size) + ' Bytes');
    lv.SubItems.Add(FormatDateTime('',FileDateToDateTime(ds.Time)));

// JETZT KOMMT DER TILE-MODUS -->

    // wir initialisieren das TLVTileInfo-Record
    tile.cbSize := sizeof(TLVTileInfo);

    // welches Item ist´s?
    tile.iItem := lv1.Items.Count - 1;


    // wie viele zusätzliche Zeilen sollen es für
    // dieses spezielle Item sein?
    tile.cColumns := 2;

    // welche Spalten sollen angezeigt werden?
    tile.puColumns := @ca[0];

    // an die List-View übergeben
    ListView_SetTileInfo(lv1.Handle,tile);
  end;

  res := FindNext(ds);
end;
FindClose(ds);
Wichtig hierbei ist diese Anweisung:
    tile.puColumns := @ca[0]; "ca" ist dabei ein Integer-Array, das die Indexwerte der anzuzeigenden Spalten enthält:
Delphi-Quellcode:
var
  ca : array[0..1]of integer = (1,2);
Wenn wir davon ausgehen, dass -wie oben erwähnt!- die erste Spalte die Dateigröße und die zweite das Datum enthält, dann würden wir im Tile-Modus also auch unter dem Dateinamen erst die -größe und dann das Datum sehen. Tauschen wir die Angabe um, etwa
Delphi-Quellcode:
var
  ca : array[0..1]of integer = (2,1);
dann sähen wir erst das Datum und dann die Größe. Abhängig von den Spalten, die ihr im Reportmodus habt (oder generell: falls ihr versteckte Subitems habt, die in keiner Spalte auftauchen!), könnt ihr mit dem Array beeinflussen, was im Tile-Modus zu sehen sein soll.

That's all.

(PS: Fehler mit "Code" vs. "Delphi" im Text gefixt.)
  Mit Zitat antworten Zitat
MathiasSimmack
 
#5
  Alt 2. Mär 2003, 19:00
Das Schwierigste zuletzt; ´s hat mich auch am meisten Nerven gekostet. Das Wichtigste hierbei ist, dass man die Gruppen (leider) selbst bilden muss. Diese Arbeit nimmt einem das System nicht ab. Die WinXP-Benutzer unter euch kennen den Effekt, den ich persönlich für recht nützlich halte. Aktiviert im Explorer einfach mal die Option "in Gruppen anzeigen".
Der Explorer sortiert die Dateien dann z.B. alfabetisch. Klickt ihr aber auf die Spalte mit der Dateigröße, dann heißen die Gruppen plötzlich "Sehr klein", "Klein", usw., und die Dateien werden entsprechend zugeordnet.

All das muss man, wie gesagt!, selbst machen. Auf der anderen Seite hat man dadurch die Freiheit, die Gruppen an die jeweilige Anwendung anzupassen. Ich erstelle als Beispiel eine einzige Gruppe, der alle Dateien zugeordnet werden. Dazu brauchen wir das Record TLVGroup, das auch erst mal initialisiert werden muss:
group.cbSize := sizeof(TLVGroup); Da wir einen Gruppennamen und eine ID für die Gruppe vergeben (müssen), sind die entsprechenden Flags zu setzen:
group.mask := LVGF_HEADER or LVGF_GROUPID; Nun kommt der Name unserer Gruppe, der als PWideChar zu übergeben ist:
Delphi-Quellcode:
group.pszHeader := pwidechar(widestring('Testgruppe'));
group.cchHeader := lstrlenW('Testgruppe');
Und die ID unserer Gruppe:
group.iGroupId := 1; Das ganze reichen wir an die List-View weiter, wobei wir als zweiten Parameter (laut PSDK: Itemindex) -1 angeben, damit die neue Gruppe an das Ende des internen Gruppenarrays der List-View gesetzt wird:
ListView_InsertGroup(lv1.Handle,-1,group); Nun müssen die Items noch irgendwie in die Gruppe rein. Ich hatte meine ersten Versuche dazu mit der Nachricht "LVM_MOVEITEMTOGROUP" (respektive "ListView_MoveItemToGroup") begonnen, aber diese Nachricht hat entweder einen vollkommen anderen Sinn, oder sie funktioniert nicht.
Tatsächlich brauchen wir das erweiterte TLVItem-Record, das man im PSDK und in der Unit "CommCtrl_Fragment.pas" (s. Anhang im ersten Beitrag) finden kann. Dieses erweiterte Record besitzt u.a. die neue Membervariable iGroupId, die wir nun brauchen. Das heißt, wir nehmen eine for-Schleife und laden nacheinander alle Items der List-View:
Delphi-Quellcode:
for i := 0 to lv1.Items.Count - 1 do begin
  // Record leeren
  ZeroMemory(@lv60,sizeof(TLVItem60));

  // Flag setzen, weil wir die Gruppen-ID ändern wollen
  lv60.mask := LVIF_GROUPID;

  // von welchem Item?
  lv60.iItem := i;

  // wie lautet die ID?
  lv60.iGroupId := 1;

  // und ab dafür
  SendMessage(lv1.Handle,LVM_SETITEM,0,LPARAM(@lv60));
end;
Bitte nicht wundern: um Probleme mit dem Originalrecord zu vermeiden, habe ich in meiner Unit den erweiterten Typ als TVLItem60 deklariert.

So, nun können wir die Gruppenansicht einschalten. Dazu gibt´s "LVM_ENABLEGROUPVIEW" bzw. die Funktion:
Delphi-Quellcode:
ListView_EnableGroupView(lv1.Handle,
  not ListView_IsGroupViewEnabled(lv1.Handle));
Diese Funktion zeigt auch gleich noch das toggeln der Ansicht mit Hilfe von "ListView_IsGroupViewEnabled" (bool-Funktion).

Ja, das war´s auch schon.
Ich bitte um Verständnis, dass ich ein paar Sachen für das nächste Update der Win32-API-Tutorials aufgehoben habe. Dort zeigt euch die neue List-View-Demo dann, wie man z.B. mehrere Gruppen erstellt, wie man prüft, ob es eine Gruppe evtl. schon gibt, und wie man die Gruppen ändert, wenn man die Items nach anderen Kriterien sortieren lässt. Wer nicht so lange warten möchte, hier die Kurzform für die alfabetische Sortierung:
  • Den Namen eines Items auslesen und nur das erste Zeichen (UPCASE!) berücksichtigen. Wenn es im Bereich von A bis Z liegt, kann eine entsprechende Gruppe erzeugt werden; alles, was außerhalb davon liegt, ist im XP-Explorer z.B. in der Gruppe "Andere" zu finden.
  • Mit Hilfe von "ListView_GetGroupInfo" (PSDK) kann man Informationen zu vorhandenen Gruppen auslesen (in dem Fall wäre nur der Header -der Gruppenname!- interessant). Die Funktion liefert -1 zurück, wenn ein Fehler auftrat, bzw. wenn die Gruppe noch nicht existiert.
  • Im Erfolgsfall sind der geplante Gruppenname (1. Zeichen des Items) und der ausgelesene Gruppenname zu vergleichen. Stimmen sie überein, existiert die Gruppe. Man verlässt die Schleife, merkt sich die ID und ordnet das aktuelle Item der gefundenen Gruppe zu. Stimmen Sie nicht überein, macht man mit Punkt #2 und der nächsten Gruppe weiter, bis man die gesuchte Gruppe findet, oder bis man alle Gruppen durchsucht hat.
  • Und jetzt wieder bei Schritt #1 beginnen und das nächste Item auslesen, usw.
Wer Delphi 7 hat, kann gern ergänzen, wie es dort funktioniert - sofern die genannten Features dort in der VCL implementiert sind.


Gruß, und viel Spaß damit.
Mathias.
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

 
Delphi 2006 Professional
 
#6
  Alt 2. Mär 2003, 21:12
Pack ruhig noch ein paar Screenshots rein, damit die Leute auch wissen, von was du redest.
Michael
  Mit Zitat antworten Zitat
MathiasSimmack
 
#7
  Alt 3. Mär 2003, 06:53
Hier also ein Screenshot, der sowohl den Tile-Modus als auch die Gruppierung von Items zeigt:
[i]Bild Link auf Wunsch entfernt, da Bild nicht mehr vorhanden.

Wie gesagt, es handelt sich dabei um eine normale List-View. Es ist keine spezielle Komponente. Einzig WinXP läuft, und die Anwendung benutzt eine Manifest-Ressource.

[edit=Luckie]Bild Link entfernt. Mfg, Luckie[/edit]
  Mit Zitat antworten Zitat
wolv
 
#8
  Alt 15. Aug 2003, 19:44
Weiß irgendwer ob man den Group - View Style auch auf Virtuelle Listviews anwenden kann? d.h. wenn man LVS_OWNERDATA setzt und die OnDispInfo Notification abfängt?
Bei mir scheint die LVM_ENABLEGROUPVIEW hierbei nicht zu funktionieren!

danke im vorraus .. stefan mairhofer
  Mit Zitat antworten Zitat
Experience1986

 
Delphi 7 Personal
 
#9
  Alt 3. Feb 2006, 10:02
Hi,

Ich habe das nun mal Versucht in Delphi 7 mit XPMan Komponente und VCL umzusetzen. Das Hervorheben von Spalten funktioniert wunderbar. Allerdings die Gruppenansicht verweigert komplett die Darstellung.

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
    group:TLVGroup;
    lvItem: TLVItem60;
begin SetLength(dataset, 0);
  for i:=0 to 99 do
    begin
      SetLength(dataset, length(dataset)+1);

      dataset[length(dataset)-1].Name:='Wert '+inttostr(i+1);
      dataset[length(dataset)-1].Value:=AValue(random(10)+10, 0);
      dataset[length(dataset)-1].Mode:=random(2)+1;
    end;
  lv.Items.Count:=length(Dataset);

  // Spalte Name Grau hinterlegen
  ListView_SetSelectedColumn(lv.Handle,1);

  // Gruppen "Modus 1" anlegen
  group.cbSize := sizeof(TLVGroup);
  group.mask := LVGF_HEADER or LVGF_GROUPID;
  group.pszHeader := pwidechar(widestring('Modus 1'));
  group.cchHeader := lstrlenW('Modus 1');
  group.iGroupId := 1;
  ListView_InsertGroup(lv.Handle,-1,group); // Gruppe speichern

  // Items den Gruppen zuordnen
  for i := 0 to lv.Items.Count - 1 do
    begin
      // Record leeren
      ZeroMemory(@lvItem,sizeof(TLVItem60));

      // Flag setzen, weil wir die Gruppen-ID ändern wollen
      lvItem.mask := LVIF_GROUPID;

      // von welchem Item?
      lvItem.iItem := i;

      if Dataset[i].Mode = 1 then
        begin// wie lautet die ID?
      lvItem.iGroupId := Dataset[i].Mode; end;

      // und ab dafür
      SendMessage(lv.Handle,LVM_SETITEM,0,LPARAM(@lvItem));
    end;
  // Gruppen aktivieren
  ListView_EnableGroupView(lv.Handle, true);

  lv.Repaint;
end;


Gibt es dafür einen Grund?

Vielen Dank schonmal für jede Antwort. Suche schon ewig nach dieser Darstellungsart.
  Mit Zitat antworten Zitat
CCRDude
 
#10
  Alt 29. Jun 2007, 14:13
Ist zwar schon ein etwas älterer Beitrag, aber da ich heute gerade nach längerer Suche drüber gestolpert bin und hier die Screenshots inzwischen fehlen, und mir das perfekt für das erste Rumspielen mit einem class helper erschien, hab ich mal eben zwei davon zusammengeschustert und mitsamt Screenshots hier angehängt.

Funktionabel ab BDS X (hier Version einsetzen, ab der es class helper gibt... 2005 oder 2006?), alternativ stehen zumindest 8 Wrapper-Funktionen für dieselben Aktionen zur Verfügung. Die CommCtrl_Fragment.pas ist ebenfalls noch nötig, wollte nicht so frech sein und die als meine Arbeit ausgeben, ich habs ja bloß nochmal in Schönschrift abgeschrieben

Beispiel für die Anwendung:

Delphi-Quellcode:
procedure TForm1.AddSomeGroupedThings;
begin
   ListView1.AddGroup('Eigene Dateien', 1); // neu
   ListView1.AddGroup('Downloads', 2);
   ListView1.ViewStyleEx := vsTile; // neu
   with ListView1.Items.Add do begin
      Caption := 'Hallo Welt.txt';
      GroupId := 1; // neu
      SetMinimumSubItemCount(2, '?'); // neu
      SubItems[0] := '11 B';
      SubItems[1] := IntToStr(GroupId);
      SetTileViewColumns(2, [1]); // neu
   end;
   with ListView1.Items.Add do begin
      Caption := 'snlListView.pas';
      GroupId := 1;
      SetMinimumSubItemCount(2, '?');
      SubItems[0] := '10 KB';
      SubItems[1] := IntToStr(GroupId);
      SetTileViewColumns(2, [1]);
   end;
   with ListView1.Items.Add do begin
      Caption := 'Screenshot.png';
      GroupId := 2;
      SetMinimumSubItemCount(2, '?');
      SubItems[0] := '183 KB';
      SubItems[1] := IntToStr(GroupId);
      SetTileViewColumns(2, [1]);
   end;

   ListView1.TileViewLines := 1; // neu
   ListView1.GroupViewEnabled := true; // neu
end;
Miniaturansicht angehängter Grafiken
listviewtiledemo1_101.png   listviewgroupdemo1_154.png  
Angehängte Dateien
Dateityp: pas snllistview_582.pas (9,8 KB, 112x aufgerufen)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:05 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