2012-03-30 27 views
5

Aquí hay (más o menos) una pregunta relacionada: Delphi - Populate an imagelist with icons at runtime 'destroys' transparency.Lista de imágenes con iconos de mezcla alfa pierde Transparencia

He probado @TOndrej answer. Pero parece que necesito tener estilos visuales (manifiesto de XP) habilitados para que esto funcione (se usará la versión 6.0 de los controles comunes de Windows, que no quiero en este momento). Agrego los iconos en tiempo de ejecución a través de ExtractIconEx y ImageList_AddIcon.

establecer parecer ImageList.Handle utilizar mango Imagen-Lista de sistema, no requieren XP Manifiesto. así que incluso un programa anterior que escribí en D3 aparece con los iconos alfa mezclados correctamente cuando uso la lista de imágenes del sistema para mostrar la lista de archivos (con un TListView).

Estaba vagando ¿Qué tiene de especial la Lista de imágenes del sistema y cómo se crea, por lo que es compatible con la mezcla alfa en todos los casos? No puedo entenderlo. Aquí hay un código de ejemplo:

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, Menus, ImgList, StdCtrls, ShellAPI, ExtCtrls, Commctrl; 

type 
    TForm1 = class(TForm) 
    ImageList1: TImageList; 
    PopupMenu1: TPopupMenu; 
    MenuItem1: TMenuItem; 
    Button1: TButton; 
    Button2: TButton; 
    procedure FormCreate(Sender: TObject); 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
    private 
    FileName: string; 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 
// {$R WindowsXP.res} 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    PopupMenu1.Images := ImageList1; 
    FileName := 'C:\Program Files\Mozilla Firefox\firefox.exe'; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    IconPath: string; 
    IconIndex: Integer; 
    hIconLarge, hIconSmall: HICON; 
begin 
    IconPath := FileName; 
    IconIndex := 0; // index can be other than 0 

    ExtractIconEx(PChar(IconPath), IconIndex, hIconLarge, hIconSmall, 1); 

    Self.Refresh; // erase form 
    DrawIconEx(Canvas.Handle, 10, 10, hIconSmall, 0, 16, 16, 0, 
    DI_IMAGE or DI_MASK); // this will draw ok on the form 

    // ImageList1.DrawingStyle := dsTransparent; 
    ImageList1.Handle := ImageList_Create(ImageList1.Width, ImageList1.Height, 
    {ILC_COLORDDB} ILC_COLOR32 or ILC_MASK, 0, ImageList1.AllocBy); 
    ImageList_AddIcon(ImageList1.Handle, hIconSmall); 

    MenuItem1.ImageIndex := 0; 

    DestroyIcon(hIconSmall); 
    DestroyIcon(hIconLarge); 

    PopupMenu1.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y); 
end; 

procedure TForm1.Button2Click(Sender: TObject); 
// using sys image-list will work with or without Manifest 
type 
    DWORD_PTR = DWORD; 
var 
    ShFileINfo :TShFileInfo; 
    SysImageList: DWORD_PTR; 
    FileName: string; 
begin 
    SysImageList := ShGetFileInfo(nil, 0, ShFileInfo, SizeOf(ShFileInfo), 
    SHGFI_SYSICONINDEX OR SHGFI_SMALLICON); 

    if SysImageList = 0 then Exit; 
    ImageList1.Handle := SysImageList; 
    ImageList1.ShareImages := True; 

    if ShGetFileInfo(PChar(FileName), 0, ShFileInfo, SizeOf(ShFileInfo), 
    SHGFI_SYSICONINDEX OR SHGFI_ICON OR SHGFI_SMALLICON) <> 0 then 
    begin 
    MenuItem1.ImageIndex := ShFileInfo.IIcon; 
    Self.Refresh; // erase form 
    DrawIconEx(Canvas.Handle, 10, 10, ShFileInfo.hIcon, 0, 16, 16, 0, 
     DI_IMAGE or DI_MASK); 
    DestroyIcon(ShFileInfo.hIcon); // todo: do I need to destroy here? 

    PopupMenu1.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y); 
    end;  
end; 

end. 

estilos visuales discapacitados:

enter image description here

estilos visuales Habilitado:

enter image description here


Una solución es utilizar la clase o subclase de interposición TImageList y anular DoDrawas shown here, pero lo que realmente quiero saber es cómo crear mi lista de imágenes igual que la lista de imágenes de sistema.

Nota: Conozco TPngImageList y no quiero usarlo en este caso.


Editar: @ La respuesta de David (y comentarios) eran precisas:

que tendrá que vincular explícitamente a ImageList_Create (v6) porque de lo contrario, está implícitamente ligado al cargar el módulo tiempo y será unido a v5.8

código de la muestra (sin uso de API contexto de activación):

function ImageList_Create_V6(CX, CY: Integer; Flags: UINT; Initial, Grow: Integer): HIMAGELIST; 
var 
    h: HMODULE; 
    _ImageList_Create: function(CX, CY: Integer; Flags: UINT; 
    Initial, Grow: Integer): HIMAGELIST; stdcall; 
begin 
    // TODO: find comctl32.dll v6 path programmatically 
    h := LoadLibrary('C:\WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.5512_x-ww_35d4ce83\comctl32.dll'); 
    if h <> 0 then 
    try 
    _ImageList_Create := GetProcAddress(h, 'ImageList_Create'); 
    if Assigned(_ImageList_Create) then 
     Result := _ImageList_Create(CX, CY, Flags, Initial, Grow); 
    finally 
    FreeLibrary(h); 
    end; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    ... 
    ImageList1.Handle := ImageList_Create_V6(ImageList1.Width, ImageList1.Height, 
    ILC_COLOR32 or ILC_MASK, 0, ImageList1.AllocBy); 
    ... 
end; 

EDI2:A sample code by @David que muestra cómo se hace correctamente a través de la API de activación de contexto.

Respuesta

5

Hay dos versiones de los controles de la lista de imágenes. La versión v5.8 y la versión v6. La lista de imágenes del sistema es compartida por el sistema y utiliza la versión v6. No es especial de ninguna otra manera, es solo una lista de imágenes v6. En su aplicación, su lista de imágenes es v5.8 o v6 dependiendo de si incluye o no el manifiesto.Pero la lista de imágenes propiedad del sistema siempre es v6.

No sé por qué no quiere usar controles comunes v6 en su aplicación. Pero con esa restricción, podría usar la API de contexto de activación para usar localmente los controles comunes de v6 solo mientras crea su lista de imágenes. Eso resolvería su problema y dejaría el resto de su aplicación con los controles comunes v5.8.

+0

Eso tiene mucho sentido. Nunca pensé que la lista de imágenes del sistema puede usar un control de versión diferente fuera de mi proceso. en función de su respuesta [aquí] (http://stackoverflow.com/a/5133222/937125) Elimino la línea 'if IsLibrary then', pero no entiendo cómo hacerlo en mi EXE. especialmente las líneas: 'ActCtx.dwFlags: = ACTCTX_FLAG_RESOURCE_NAME_VALID o ACTCTX_FLAG_HMODULE_VALID;' y 'ActCtx.lpResourceName: = MakeIntResource (2);' – kobik

+0

He intentado 'ActCtx.lpSource' con el manifiesto válido. 'ActCtx.dwFlags' se establece en 0. esa dosis no hizo ninguna diferencia. el ícono aún no es válido – kobik

+1

Tendrá que vincular explícitamente a 'ImageList_Create' porque, de lo contrario, se vinculará implícitamente en el momento de carga del módulo y se vinculará a v5.8. No puedo decir que haya intentado esto alguna vez. No es un trabajo completamente trivial. Tendrá que mirar debajo de un depurador (por ejemplo, ms depends o process explorer) y asegúrese de estar haciendo v6 comctl para cargar. –

Cuestiones relacionadas