Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi VirtualTreeView, OnGetImageIndexEx und OverlayImages (https://www.delphipraxis.net/195726-virtualtreeview-ongetimageindexex-und-overlayimages.html)

Codehunter 20. Mär 2018 14:12

VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Hallo!

Ich ärgere mich schon eine ganze Weile mit dem VST herum. Ich möchte ein Overlay-Image über das Node-Symbol legen. Die normalen Images und die Overlays liegen in verschiedenen ImageLists, daher verwende ich OnGetImageIndexEx:
Delphi-Quellcode:
procedure TForm1.tvGridGetImageIndexEx(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  var Ghosted: Boolean; var ImageIndex: TImageIndex;
  var ImageList: TCustomImageList);
var
  D: PDbTreeNodeData;
begin
  ImageIndex:= -1;
  if Column = 0 then begin
    case Kind of
      ikNormal, ikSelected:
      begin
        ImageList:= ilIcons;
        D:= Sender.GetNodeData(Node);
        if D <> NIL then begin
          ImageIndex:= D^.Data.ImageIndex;
        end;
      end;

      ikOverlay:
      begin
        ImageList:= ilOverlays;
        D:= Sender.GetNodeData(Node);
        if D <> NIL then begin
          ImageIndex:= D^.Data.OverlayIndex;
        end;
      end;
    end;
  end;
end;
Jedoch wird kein Overlay gepinselt. Der selbe Code in OnGetImageIndex (ohne -Ex) funktioniert einwandfrei. Nur eben mit dem Unterschied, dass ich hier alle Images in eine gemeinsame Liste packen muss. Das gefällt mir aber nicht so gut.

Laut Debugger passt alles, sämtliche Zuweisungen, ImageIndizes und Imagelisten sind so wie sie sein sollen. Allerdings blicke ich im VST-Quellcode nicht wirklich durch, der ist ziemlich verworren. Darum kann ich nicht nachvollziehen, ob OnGetImageIndexEx intern anders behandelt wird als OnGetImageIndex (abgesehen von der Zuweisung der ImageList im Eventhandler)

Grüße
Cody

KodeZwerg 20. Mär 2018 17:58

AW: VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Vielleicht kann die dieser Abschnitt aus meinen Code helfen, arbeitet mit ListView, der liest ein Verzeichniss ein und gibt es mit icons und mini-Info aus.
Leider weder optimiert, noch gut anzuschauen, zumindest macht es das was ich vorhatte.

Delphi-Quellcode:
procedure GetFiles(const hLV: HWND; Path, Mask: String);
var
  finddata : TWin32FindData;
  hFile   : cardinal;
  Loop    : dword;
  lvi     : TLVItem;
  buf     : array[0..25]of char;
  tile    : TLVTileInfo;
  fi      : TSHFileInfo;
  LVIMark : TLVInsertMark;
  tmp     : String;
  i       : integer;
begin
 hFile := FindFirstFile(PChar(IncludeTrailingPathDelimiter(Path) + Mask), finddata);
  if(hFile <> INVALID_HANDLE_VALUE) then begin
   LockWindowUpdate(hDlg);
   LastFolder := IncludeTrailingPathDelimiter(Path);
   Loop := 0;
    repeat
     if(finddata.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = 0) then begin

      ZeroMemory(@fi, sizeof(TSHFileInfo));
      ListView_SetImageList(hLV,
       SHGetFileInfo(PChar(IncludeTrailingPathDelimiter(Path) + finddata.cFilename), 0,
       fi, sizeof(TSHFileInfo), SHGFI_SYSICONINDEX or SHGFI_TYPENAME or SHGFI_SMALLICON), LVSIL_SMALL);

      ZeroMemory(@fi, sizeof(TSHFileInfo));
      ListView_SetImageList(hLV,
       SHGetFileInfo(PChar(IncludeTrailingPathDelimiter(Path) + finddata.cFilename), 0,
       fi, sizeof(TSHFileInfo), SHGFI_SYSICONINDEX or SHGFI_TYPENAME or SHGFI_ICON), LVSIL_NORMAL);

      lvi.mask    := LVIF_TEXT or LVIF_IMAGE;
      lvi.iItem   := Loop;
      lvi.iSubItem := 0;
      lvi.pszText := finddata.cFileName;
     i := 0;
      lvi.iImage  := fi.iIcon;
      ListView_InsertItem(hLV, lvi);

      tmp := IntToStr(finddata.nFileSizeLow)+' byte';
      if (finddata.nFileSizeLow / 1024) > 1 then tmp := FloatToStr((finddata.nFileSizeLow / 1024),-1,2)+' kb';
      if (finddata.nFileSizeLow / 1024 / 1024) > 1 then tmp := FloatToStr((finddata.nFileSizeLow / 1024 / 1024),-1,2)+' mb';
      if (finddata.nFileSizeLow / 1024 / 1024 / 1024) > 1 then tmp := FloatToStr((finddata.nFileSizeLow / 1024 / 1024 / 1024),-1,2)+' gb';
      lvi.mask    := LVIF_TEXT;
      lvi.iSubItem := 1;
      lvi.pszText := pChar(tmp);
      ListView_SetItem(hLV, lvi);

      lvi.mask    := LVIF_TEXT;
      lvi.iSubItem := 2;
      lvi.pszText := fi.szTypeName;
      ListView_SetItem(hLV, lvi);

//      ZeroMemory(@buf, sizeof(buf));
//      wvsprintf (buf, '%u byte', pchar(@finddata.nFileSizeLow));
      lvi.mask    := LVIF_TEXT;
      lvi.iSubItem := 3;
      lvi.pszText := pChar(IntToStr(finddata.nFileSizeLow)); //buf;
      ListView_SetItem(hLV, lvi);

     if GetOSVersion = osXP then begin
      tile.cbSize   := sizeof(TLVTileInfo);
      tile.iItem    := Loop;
//     tile.cColumns := length(colArray);
      tile.puColumns := @colArray[0];
      ListView_SetTileInfo(hLV, tile);
     end;
    LVIMark.cbSize    := sizeof(TLVInsertMark);
    LVIMark.dwFlags   := LVIM_AFTER;
    LVIMark.iItem     := Loop;
    LVIMark.dwReserved := 0;
    ListView_SetInsertMark(hLV, LVIMark);
    if LowerCase(ExtractFileName(SelectedFile)) = LowerCase(finddata.cFileName) then
    begin
{     if LowerCase(ExtractFileName(SelectedFile)) = LowerCase(finddata.cFileName) then
      ListView_SetItemState(hLV, Loop, 0, LVIS_SELECTED);}
      lvi.stateMask := LVIS_SELECTED;
      lvi.state := LVIS_SELECTED;
      SendMessage(hLV, LVM_SETITEMSTATE, Loop, Longint(@lvi))
    end;
    Inc(Loop);
   end;
  until (not FindNextFile(hFile, finddata));
   FindClose(hFile);
  LockWindowUpdate(0);
 end;
end;
Das war mein Versuch eine ListView ähnlich dem Explorer nachzubasteln, primitiv ich weiss.

Codehunter 20. Mär 2018 18:06

AW: VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Danke aber leider ist der VirtualTreeView nicht mit dem Listview verwandt. Die sind nicht mal ähnlich. Der VTV bzw. VST ist sehr komplex. Manchmal spielen da kleine Änderungen an den TreeOptions rein, das Theming, Events die bestimmte Parameter gesetzt haben wollen, tausend Möglichkeiten. Das sich OnGetImageIndex und OnGetImageIndexEx unterschiedlich verhalten, das Problem hatte ich auch noch nicht.

Uwe Raabe 20. Mär 2018 18:32

AW: VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Wenn es so abläuft, wie in anderen Controls mit den Overlays, dann müssen die Overlays zwingend in derselben ImageList liegen, wie die anderen Icons.

Kannst du mal versuchen, die Overlay-Imagelist so aufzubauen, daß die Overlay-Indices in dieser Liste alle >= 15 sind? In dem Fall kann das API-Handling für Overlays nicht mehr verwendet werden.

Sollte das funktionieren, wäre das dann ein Bug im VTV. Ist es aber wohl in jedem Fall, aber dann wüsste ich wie er zu beheben ist.

KodeZwerg 20. Mär 2018 18:35

AW: VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Für TreeView mit verschiedenen Bildern habe ich so etwas mir mal gebastelt.
Garantiert viel zu alt, stammt glaube ich noch aus Delphi 5/6/7 (?) Zeiten.

Delphi-Quellcode:
Procedure CreateImages;
   var
     SysIL: HImageList;
     SFI: TSHFileInfo;
   begin
     MyTreeView.Images := TImageList.Create(Self);
     SysIL := SHGetFileInfo('', 0, SFI, SizeOf(SFI),
     SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
     if SysIL <> 0 then begin
       Images.Handle := SysIL;
       Images.ShareImages := TRUE;
     end;
end;
Das ist praktisch das gleich wie bei dem ListView Beispiel, hier hatte ich es mit TreeView realisiert, diese Routine sammelte mir die Icons. Tut mir leid das ich nicht weiter helfen kann.

Codehunter 20. Mär 2018 18:42

AW: VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1396821)
Wenn es so abläuft, wie in anderen Controls mit den Overlays, dann müssen die Overlays zwingend in derselben ImageList liegen, wie die anderen Icons.

Das gerade nicht, weshalb mir die Implementierung mit OnGetImageIndexEx sehr gut gefällt. Man ist nicht gezwungen, wild zusammengewürfelte Endlos-Imagelisten zu bauen.

Zitat:

Zitat von Uwe Raabe (Beitrag 1396821)
Kannst du mal versuchen, die Overlay-Imagelist so aufzubauen, daß die Overlay-Indices in dieser Liste alle >= 15 sind? In dem Fall kann das API-Handling für Overlays nicht mehr verwendet werden.

Ich war praktisch zeitgleich mit deinem Post auch an genau der Sache dran. Der entsprechende Hinweis findet sich als Kommentar in der Virtualtrees.pas:
Delphi-Quellcode:
      // If the user returned an index >= 15 then we cannot use the built-in overlay image drawing.
      // Instead we do it manually.
Genau das war - rein zufällig - bei mir der Fall. So lange ich es mit OnGetImageIndex implementiert hatte, lagen alle Images gemeinsam in einer Imagelist und die Overlay-Images lagen alle oberhalb von Index 15. Wenn ich nun hier einen Overlay-Index kleiner 15 angebe, dann funktioniert das Overlaying auch in OnGetImageIndex nicht mehr.

Zitat:

Zitat von Uwe Raabe (Beitrag 1396821)
Sollte das funktionieren, wäre das dann ein Bug im VTV. Ist es aber wohl in jedem Fall, aber dann wüsste ich wie er zu beheben ist.

Ob es ein Bug ist sei mal dahin gestellt. Das scheint durchaus Absicht zu sein:
Delphi-Quellcode:
      if (ImageInfo[iiOverlay].Index > -1) and (ImageInfo[iiOverlay].Index < 15) then
        ExtraStyle := ILD_TRANSPARENT or ILD_OVERLAYMASK and IndexToOverlayMask(ImageInfo[iiOverlay].Index + 1)
      else
        ExtraStyle := ILD_TRANSPARENT;
Allerdings durchschaue ich noch nicht so ganz, was die zusätzlichen Extrastyles bewirken sollen wenn der ImageIndex < 15 ist. Würde man auf diese Extrawürste verzichten, müsste es nach meiner Logik eigentlich immer funktionieren.

Uwe Raabe 20. Mär 2018 18:54

AW: VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Zitat:

Zitat von Codehunter (Beitrag 1396823)
. Das scheint durchaus Absicht zu sein:

Na ja, der Entwickler ist stillschweigend davon ausgegangen, daß die Images eben in einer Liste liegen. Bei einem Index < 15 wird nämlich nur die eine Liste zum Zeichnen beider Icons an die DrawImage-Routine übergeben.

Delphi-Quellcode:
DrawImage(Images, Index, Canvas, XPos, YPos, Style[Images.ImageType] or ExtraStyle, DrawEnabled);


Die Entscheidung, ob das Overlay selbst gezeichnet werden muss, ist eben nicht nur vom ImageIndex < 15 abhängig, sondern auch davon, ob beide Imagelists gleich sind. Nur wenn beides zutrifft, kann die vereinfachte Draw-Methode verwendet werden.

Codehunter 20. Mär 2018 19:04

AW: VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Wenn man das so betrachtet, dann ist es wohl doch ein Bug "durch Unterlassen" im VST. Ich sehe zumindest keine Prüfung darauf hin, ob die Bilder in der selben oder unterschiedlichen ImageLists liegen. Sollte ich raten dann würde ich vermuten, OnGetImageIndexEx wurde nachträglich "drangeflanscht" und dieser Index-15-Fallstrick dabei vergessen.

Ist das eigentlich der selbe Fallstrick, weshalb TortoiseGit unter Windows 10 Probleme hat, die Overlay-Images in den Explorer einzubauen wenn gleichzeitig die Onedrive-Extension aktiv ist? Da gibts ganz ähnliche Probleme...

Ich hab mal ein bisschen quergelesen und scheinbar ist dieses Index-15-Problem ein Relikt aus 16-Bit-Windows-Zeiten. Ist sowas möglich nach so langer Zeit? Au weia.

@Uwe: Du hast mir jetzt in den letzten Tagen bei mehreren dicken Zeitklauern geholfen :thumb:

Codehunter 21. Mär 2018 07:52

AW: VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Ich habe jetzt die Quick&Dirty-Lösung ausprobiert und in der separaten Operlay-ImageList zu Beginn 15 leere Icons eingefügt. Klappt wunderbar. Da muss man aber auch erst mal drauf kommen. Wenn man nach dem Kommentar in der Virtualtrees.pas geht hat man die Unterscheidung ja deshalb gemacht, weil bei Indizes < 16 ein systeminternes Zeichnen verwendet wird das angeblich schneller wäre. Ich frage mich aber, ob das nun so ins Gewicht fällt.

Uwe Raabe 21. Mär 2018 08:36

AW: VirtualTreeView, OnGetImageIndexEx und OverlayImages
 
Zitat:

Zitat von Codehunter (Beitrag 1396847)
Wenn man nach dem Kommentar in der Virtualtrees.pas geht hat man die Unterscheidung ja deshalb gemacht, weil bei Indizes < 16 ein systeminternes Zeichnen verwendet wird das angeblich schneller wäre. Ich frage mich aber, ob das nun so ins Gewicht fällt.

Ich frage mich, ob das überhaupt so funktioniert. Damit die Overlay-Funktionalität vom ImageList_DrawEx genutzt werden kann, muss dem entsprechenden Image in der ImageList auch erstmal ein Overlay-Index zugewiesen werden. Damit wird ein Mapping geschaffen zwischen dem Overlay-Index (0..14) und dem tatsächlichen Image-Index innerhalb der ImageList. Somit müssen die Overlays auch nicht zwingend am Anfang der ImageList liegen. Da das interne Overlay-Drawing aber nur mit einer ImageList arbeitet, müssen diese Overlays auch in derselben ImageList liegen, wie die normalen Icons.

Innerhalb des OnGetImageIndexEx-Events muss das dann natürlich beachtet werden (sobald der Bug in VTV behoben ist). Liegen die Overlays in derselben ImageList wie die anderen Icons ist der Overlay-Index zurückzugeben, andernfalls der ImageIndex. Das gilt dann auch für den Fall, daß mehr als 15 Overlays innerhalb dieser ImageList benötigt werden und das gewünschte Overlay nicht mehr als solches gemapt werden kann. Ich finde die aktuelle Implementierung ziemlich verwirrend und fehleranfällig.

Zitat:

Zitat von ImageList_DrawEx function
An overlay image is drawn transparently over the primary image specified in the i parameter. To specify an overlay image in the fStyle parameter, use the INDEXTOOVERLAYMASK macro to shift the one-based index of the overlay image. Use the OR operator to logically combine the return value of the macro with the drawing style flags specified in the fStyle parameter. You must first specify this image as an overlay image by using the ImageList_SetOverlayImage function.



Alle Zeitangaben in WEZ +1. Es ist jetzt 19:33 Uhr.
Seite 1 von 2  1 2      

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