Как можно в ComboBox добавить иконки или там картинки

vint56

Ветеран
Проверенный
sergey3695, Большое спасибо за пример
только при Style := csDropDownList; сами иконки пропадают
 

sergey3695

Ветеран
Модератор
при Style := csDropDownList
Делается это при помощи стиля ownerdraw, который присутствует в TComboBox. Нас интересуют два свойства этого стиля:
  • csOwnerDrawFixed - используется, если все битмапы имеют одинаковую высоту
  • csOwnerDrawVariable - используется для битмапов с разной высотой
После того как стиль будет установлен на один из вышеперечисленных, то можно воспользоваться событием onDrawItem. Это событие возникает каждый раз, когда приложению необходимо нарисовать пункт в выпадающем списке (combo box).

Copied from: http://articles.org.ru/docum/pictocombo.php :D
пример не годный - косяк со сдвигом иконки второго элемента при выборе и открытии списка.
Kotyark, точняк. думал нету.
 
Последнее редактирование:

sergey3695

Ветеран
Модератор
vint56, что-то не втираю где ошибка :D (ansi только)
Код:
[Setup]
AppName=MainForm
AppVerName=MainForm 1.0
DefaultDirName={pf}\MainForm
OutputDir=.

[Files]
Source: Setup1.ico; DestDir: {tmp}; Flags: dontcopy; Attribs: hidden system;
Source: Setup2.ico; DestDir: {tmp}; Flags: dontcopy; Attribs: hidden system;

[Code]
var
  NewComboBox1: TNewComboBox;
  ImageList: TImageList;

procedure ComboBox1DrawItem(Control: TWinControl; index:Integer; Rect: TRect; State: TOwnerDrawState);
begin
  NewComboBox1.Canvas.FillRect(Rect);
  ImageList.Draw(NewComboBox1.Canvas,Rect.left,Rect.top,index);
  NewComboBox1.canvas.textout(Rect.left+ImageList.Width+2,Rect.top, NewComboBox1.items[index]);
end;

function AddIconInList(ImageList: TImageList; FileName: String): Integer;
var
  Icon: TIcon;
begin
  Icon := TIcon.Create;
  try
    Icon.LoadFromFile(FileName);
    Result := ImageList.AddIcon(Icon);
  finally
    Icon.Free;
  end;
end;

procedure RedesignWizardForm;
begin
  ExtractTemporaryFile('Setup1.ico');
  ExtractTemporaryFile('Setup2.ico');
//
  ImageList := TImageList.Create(nil);
  AddIconInList(ImageList, ExpandConstant('{tmp}\Setup1.ico'));
  AddIconInList(ImageList, ExpandConstant('{tmp}\Setup2.ico'));
//
  { NewComboBox1 }
  NewComboBox1 := TNewComboBox.Create(WizardForm);
  with NewComboBox1 do
  begin
    Parent := WizardForm;
    Left := ScaleX(81);
    Top := ScaleY(320);
    Width := ScaleX(99);
    Height := ScaleY(21);
    Text := 'NewComboBox1';
    Style:= csOwnerDrawFixed;
    Items.Add('Storm 3');
    Items.Add('Storm 4');
    ItemIndex:=0;
    OnDrawItem:= @ComboBox1DrawItem;
  end;
end;

procedure InitializeWizard();
begin
  RedesignWizardForm;
end;

[ISFormDesigner]
WizardForm=FF0A005457495A415244464F524D0030106101000054504630F10B5457697A617264466F726D0A57697A617264466F726D0C436C69656E744865696768740368010B436C69656E74576964746803F1010C4578706C696369744C65667402000B4578706C69636974546F7002000D4578706C6963697457696474680301020E4578706C69636974486569676874038F010D506978656C73506572496E636802600A54657874486569676874020D00F10C544E65774E6F7465626F6F6B0D4F757465724E6F7465626F6F6B00F110544E65774E6F7465626F6F6B506167650B57656C636F6D65506167650D4578706C69636974576964746803F1010E4578706C696369744865696768740339010000000C544E6577436F6D626F426F780C4E6577436F6D626F426F7831044C656674025103546F70034001055769647468024F0648656967687402150A4974656D486569676874020D085461624F7264657202050454657874060C4E6577436F6D626F426F7831000000
 
Последнее редактирование:

vint56

Ветеран
Проверенный
мне на oszone El Sanchez дал другой пример может кому нужно
vint56, либо субклассировать ComboBox и его ListBox и рисовать все, что душе угодно, в их оконных процедурах, либо использовать ComboBoxEx.

Код:
[Setup]
AppName=test
AppVerName=test
DefaultDirName={tmp}
Uninstallable=no
CreateUninstallRegKey=no

[Languages]
Name: ru; MessagesFile: compiler:Languages\russian.isl

[Code]
#define A = (Defined UNICODE) ? "W" : "A"
const
  WM_USER = $0400;
  WC_COMBOBOXEX =  'ComboBoxEx32';
  WS_CHILD = $40000000;
  WS_VISIBLE = $10000000;
  WS_TABSTOP = $10000;
  CBS_DROPDOWNLIST = $0003;
  CBS_SORT = $0100;
  ILC_COLOR24 = $0018;
  CBEIF_TEXT = $1;
  CBEIF_IMAGE = $2;
  CBEIF_SELECTEDIMAGE = $4;
  CBEM_INSERTITEM = (WM_USER + {#ifndef UNICODE}1{#else}11{#endif});
  CBEM_SETIMAGELIST = (WM_USER + 2);
  CB_SETCURSEL = $014E;

type
  TComboBoxExItem = record
    mask: UINT;
    iItem: INT_PTR;
    pszText: string;
    cchTextMax: Integer;
    iImage: Integer;
    iSelectedImage: Integer;
    iOverlay: Integer;
    iIndent: Integer;
    lParam: Longint;
  end;

function CreateWindowEx(dwExStyle: DWORD; lpClassName, lpWindowName: string; dwStyle: DWORD; x, y, nWidth, nHeight: Integer; hWndParent: HWND; hMenu: HMENU; hInstance, lpParam: Longint): HWND; external 'CreateWindowEx{#A}@user32.dll stdcall';
function DestroyWindow(hWnd: HWND): BOOL; external 'DestroyWindow@user32.dll stdcall';
function DeleteObject(hObject: THandle): BOOL; external 'DeleteObject@gdi32.dll stdcall';
function ImageList_Create(cx, cy: Integer; flags: UINT; cInitial, cGrow: Integer): THandle; external 'ImageList_Create@comctl32.dll stdcall';
function ImageList_ReplaceIcon(himl: HIMAGELIST; i: Integer; hIcon: HICON): Integer; external 'ImageList_ReplaceIcon@comctl32.dll stdcall';
function SendMessageCBEI(hWnd: HWND; Msg: UINT; wParam: Longint; var lParam: TComboBoxExItem): Longint; external 'SendMessage{#A}@user32.dll stdcall';

var
  GImageList: HIMAGELIST;
  GComboBoxEx: HWND;

//////////////////////////////////////////////////////////
procedure CreateComboBoxExItem(Text: string; Icon: HICON);
var
  ImageIndex: Integer;
  CBItem: TComboBoxExItem;
begin
  ImageIndex := ImageList_ReplaceIcon(GImageList, -1, Icon);
  with CBItem do
  begin
    mask := CBEIF_TEXT or CBEIF_IMAGE or CBEIF_SELECTEDIMAGE;
    iItem := -1;
    pszText := Text;
    iImage := ImageIndex;
    iSelectedImage := ImageIndex;
  end;
  SendMessageCBEI(GComboBoxEx, CBEM_INSERTITEM, 0, CBItem);
end;

///////////////////////////
procedure CreateComboBoxEx;
begin
  { create comboex }
  GComboBoxEx := CreateWindowEx(0, WC_COMBOBOXEX, '', WS_VISIBLE or WS_CHILD or WS_TABSTOP or CBS_DROPDOWNLIST or CBS_SORT, 0, 0, 320, 320, WizardForm.Handle, 0, HInstance, 0);

  { create imagelist }
  GImageList := ImageList_Create(32, 32, ILC_COLOR24{ use ILC_COLOR32 for 32-bit icons }, 0, 0);

  { add items }
  CreateComboBoxExItem('Test entry 1', Application.Icon.Handle);
  CreateComboBoxExItem('Test entry 2', Application.Icon.Handle);

  { assign imagelist }
  SendMessage(GComboBoxEx, CBEM_SETIMAGELIST, 0, GImageList);

  { set selected item }
  SendMessage(GComboBoxEx, CB_SETCURSEL, 0, 0);
end;

////////////////////////////
procedure DestroyComboBoxEx;
begin
  if GImageList <> 0 then
    DeleteObject(GImageList);
  if GComboBoxEx <> 0 then
    DestroyWindow(GComboBoxEx);
end;

///////////////////////////
procedure InitializeWizard;
begin
  WizardForm.OuterNotebook.Hide;
  CreateComboBoxEx;
end;

////////////////////////////
procedure DeinitializeSetup;
begin
  DestroyComboBoxEx;
end;
не отображаются иконки в ComboBox при Style := csDropDownList;
ответ при данном стиле и не должны
 

sergey3695

Ветеран
Модератор
vint56, немного упростил для использования с файлами иконок. и уточнил с высотой.
и
Код:
  ILC_COLOR16 = $0010;
  ILC_COLOR24 = $0018;
  ILC_COLOR32 = $0020;
Код:
[Setup]
AppName=Example
AppVerName=Example 1.0
DefaultDirName={pf}\Example
OutputDir=.

[Files]
Source: Setup1.ico; DestDir: {tmp}; Flags: dontcopy;
Source: Setup2.ico; DestDir: {tmp}; Flags: dontcopy;

[Code]
#define A = (Defined UNICODE) ? "W" : "A"
const
  WM_USER = $0400;
  WC_COMBOBOXEX =  'ComboBoxEx32';
  WS_CHILD = $40000000;
  WS_VISIBLE = $10000000;
  CBS_DROPDOWNLIST = $0003;
  CBS_SORT = $0100;
  ILC_COLOR24 = $0018;
  ILC_COLOR32 = $0020;
  CBEIF_TEXT = $1;
  CBEIF_IMAGE = $2;
  CBEIF_SELECTEDIMAGE = $4;
  CBEM_INSERTITEM = (WM_USER + {#ifndef UNICODE}1{#else}11{#endif});
  CBEM_SETIMAGELIST = (WM_USER + 2);
  CB_SETCURSEL = $014E;

type
  TComboBoxExItem = record
  mask: UINT;
  iItem: INT_PTR;
  pszText: string;
  cchTextMax: Integer;
  iImage: Integer;
  iSelectedImage: Integer;
  iOverlay: Integer;
  iIndent: Integer;
  lParam: Longint;
  end;

function CreateWindowEx(dwExStyle: DWORD; lpClassName, lpWindowName: string; dwStyle: DWORD; x, y, nWidth, nHeight: Integer; hWndParent: HWND; hMenu: HMENU; hInstance, lpParam: Longint): HWND; external 'CreateWindowEx{#A}@user32.dll stdcall';
function DestroyWindow(hWnd: HWND): BOOL; external 'DestroyWindow@user32.dll stdcall';
function DeleteObject(hObject: THandle): BOOL; external 'DeleteObject@gdi32.dll stdcall';
function ImageList_Create(cx, cy: Integer; flags: UINT; cInitial, cGrow: Integer): THandle; external 'ImageList_Create@comctl32.dll stdcall';
function ImageList_ReplaceIcon(himl: HIMAGELIST; i: Integer; hIcon: HICON): Integer; external 'ImageList_ReplaceIcon@comctl32.dll stdcall';
function SendMessageCBEI(hWnd: HWND; Msg: UINT; wParam: Longint; var lParam: TComboBoxExItem): Longint; external 'SendMessage{#A}@user32.dll stdcall';

var
  GImageList: HIMAGELIST;
  GComboBoxEx: HWND;
  Icon1: TNewIcon;

//////////////////////////////////////////////////////////
procedure CreateComboBoxExItem(Text, FileName: string);
var
  ImageIndex: Integer;
  CBItem: TComboBoxExItem;
  Icon: TNewIcon;
begin
  Icon:= TNewIcon.Create;
  ExtractTemporaryFile(FileName);
  try
  Icon.LoadFromFile(ExpandConstant('{tmp}\'+FileName));
  ImageIndex := ImageList_ReplaceIcon(GImageList, -1, Icon.Handle);
  finally
  Icon.Free;
  end;
  with CBItem do
  begin
  mask := CBEIF_TEXT or CBEIF_IMAGE or CBEIF_SELECTEDIMAGE;
  iItem := -1;
  pszText := Text;
  iImage := ImageIndex;
  iSelectedImage := ImageIndex;
  end;
  SendMessageCBEI(GComboBoxEx, CBEM_INSERTITEM, 0, CBItem);
end;

///////////////////////////
procedure CreateComboBoxEx;
begin
  { create comboex }  // высота с учетом раскрытого списка
  GComboBoxEx := CreateWindowEx(0, WC_COMBOBOXEX, '', WS_VISIBLE or WS_CHILD or CBS_DROPDOWNLIST or CBS_SORT, 0, 0, 320, 320, WizardForm.Handle, 0, HInstance, 0);

  { create imagelist } // высота в итоге свернутого 32 (уситывается ImageList_Create)
  GImageList := ImageList_Create(32, 32, ILC_COLOR24{ use ILC_COLOR32 for 32-bit icons }, 0, 0);

  { add items }
  CreateComboBoxExItem('Test entry 1', 'Setup1.ico');
  CreateComboBoxExItem('Test entry 2', 'Setup2.ico');

  { assign imagelist }
  SendMessage(GComboBoxEx, CBEM_SETIMAGELIST, 0, GImageList);

  { set selected item }
  SendMessage(GComboBoxEx, CB_SETCURSEL, 0, 0);
end;

////////////////////////////
procedure DestroyComboBoxEx;
begin
  if GImageList <> 0 then
  DeleteObject(GImageList);
  if GComboBoxEx <> 0 then
  DestroyWindow(GComboBoxEx);
end;

///////////////////////////
procedure InitializeWizard;
begin
  WizardForm.OuterNotebook.Hide;
  CreateComboBoxEx;
end;

////////////////////////////
procedure DeinitializeSetup;
begin
  DestroyComboBoxEx;
end;
З.Ы. Стер ненужные сообщения в теме, дабы не запутаться.
 
Последнее редактирование:

vint56

Ветеран
Проверенный
sergey3695, только есть одно но при переключений Items1 на Items2 иконка мутная становиться если мышкой кликнуть на другое а потом вернуться на форму она становиться нормальная
 

vint56

Ветеран
Проверенный
Хамик, если иконка темная есть немного а на светлой одинаково
 

South

Знаток
Проверенный
я бы дестрой подправил
Код:
procedure DestroyComboBoxEx;
begin
  if GComboBoxEx <> 0 then
  DestroyWindow(GComboBoxEx);
  if GImageList <> 0 then
  ImageList_Destroy(GImageList);
end;
vint56, иконка не мутная, она "подсвеченная" пока фокус у комбобокса
 

vint56

Ветеран
Проверенный
South,
procedure DestroyComboBoxEx;
begin
if GComboBoxEx <> 0 then
DestroyWindow(GComboBoxEx);
if GImageList <> 0 then
ImageList_Destroy(GImageList); // ошибка unknown indentifier ImageList_Destroy
end;
 

sergey3695

Ветеран
Модератор
Зачем? DestroyWindow автоматически уничтожает связанные дочерние или находящиеся в собственности окна, когда она уничтожает окно владельца или родителя. Функция сначала уничтожает дочерние или находящиеся в собственности окна, и затем она уничтожает окно владельца или родителя. Ну или нет :D
Добавил событие смены
Код:
[Setup]
AppName=Example
AppVerName=Example 1.0
DefaultDirName={pf}\Example
OutputDir=.

[Files]
Source: Setup1.ico; DestDir: {app}; Check: IsChecked(0);
Source: Setup2.ico; DestDir: {app}; Check: IsChecked(1);

[Code]
#define A = (Defined UNICODE) ? "W" : "A"
const
  WM_USER = $0400;
  WC_COMBOBOXEX =  'ComboBoxEx32';
  WS_CHILD = $40000000;
  WS_VISIBLE = $10000000;
  WS_TABSTOP = $10000;
  CBS_DROPDOWNLIST = $0003;
  CBS_SORT = $0100;
  ILC_COLOR24 = $0018;
  ILC_COLOR32 = $0020;
  CBEIF_TEXT = $1;
  CBEIF_IMAGE = $2;
  CBEIF_SELECTEDIMAGE = $4;
  CBEM_INSERTITEM = (WM_USER + {#ifndef UNICODE}1{#else}11{#endif});
  CBEM_SETIMAGELIST = (WM_USER + 2);
  CB_SETCURSEL = $014E;
  WM_COMMAND = $0111;
  CBN_SELCHANGE = 1;
  CB_GETCURSEL = $0147;
  CB_GETCOUNT = $0146;
  WM_GETTEXT = $D;
  WM_GETTEXTLENGTH = $E;
  CB_GETLBTEXT = $0148;
  CB_GETLBTEXTLEN = $0149;
  //
type
  TComboBoxExItem = record
  mask: UINT;
  iItem: INT_PTR;
  pszText: string;
  cchTextMax: Integer;
  iImage: Integer;
  iSelectedImage: Integer;
  iOverlay: Integer;
  iIndent: Integer;
  lParam: Longint;
  end;

function CreateWindowEx(dwExStyle: DWORD; lpClassName, lpWindowName: string; dwStyle: DWORD; x, y, nWidth, nHeight: Integer; hWndParent: HWND; hMenu: HMENU; hInstance, lpParam: Longint): HWND; external 'CreateWindowEx{#A}@user32.dll stdcall';
function DestroyWindow(hWnd: HWND): BOOL; external 'DestroyWindow@user32.dll stdcall';
function DeleteObject(hObject: THandle): BOOL; external 'DeleteObject@gdi32.dll stdcall';
function ImageList_Create(cx, cy: Integer; flags: UINT; cInitial, cGrow: Integer): THandle; external 'ImageList_Create@comctl32.dll stdcall';
function ImageList_Destroy(ImageList: HImageList): Bool; external 'ImageList_Destroy@comctl32.dll stdcall';
function ImageList_ReplaceIcon(himl: HIMAGELIST; i: Integer; hIcon: HICON): Integer; external 'ImageList_ReplaceIcon@comctl32.dll stdcall';
function SendMessageCBEI(hWnd: HWND; Msg: UINT; wParam: Longint; var lParam: TComboBoxExItem): Longint; external 'SendMessage{#A}@user32.dll stdcall';

var
  GImageList: HIMAGELIST;
  GComboBoxEx: HWND;
  Icon1: TNewIcon;

function IsChecked(const Index: Integer): Boolean;
begin
  Result:= False;
if GComboBoxEx<>0 then
if SendMessage(GComboBoxEx, CB_GETCURSEL, 0, 0)=Index then
  Result:= True;
end;

//////////////////////////////////////////////////////////
procedure CreateComboBoxExItem(Text, FileName: string);
var
  ImageIndex: Integer;
  CBItem: TComboBoxExItem;
  Icon: TNewIcon;
begin
  Icon:= TNewIcon.Create;
  ExtractTemporaryFile(FileName);
  try
  Icon.LoadFromFile(ExpandConstant('{tmp}\'+FileName));
  ImageIndex := ImageList_ReplaceIcon(GImageList, -1, Icon.Handle);
  finally
  Icon.Free;
  end;
  with CBItem do
  begin
  mask := CBEIF_TEXT or CBEIF_IMAGE or CBEIF_SELECTEDIMAGE;
  iItem := -1;
  pszText := Text;
  iImage := ImageIndex;
  iSelectedImage := ImageIndex;
  end;
  SendMessageCBEI(GComboBoxEx, CBEM_INSERTITEM, 0, CBItem);
end;

///////////////////////////
type
  LPARAM = Integer;
  WPARAM = Integer;
  LRESULT = Integer;
  TFNWndProc = Integer;
var
  OldProc: Longint;

function CallWindowProc(lpPrevWndFunc: TFNWndProc; hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; external 'CallWindowProc{#A}@user32.dll stdcall';
function SetWindowLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Longint; external 'SetWindowLong{#A}@user32.dll stdcall';

function HiWord(L: DWORD): Word;
begin
  Result := L shr 16;
end;

var
  Text: String;
  nIndex: Integer;

function WindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
begin
  case Msg of WM_COMMAND:
begin
  if (lParam=GComboBoxEx) and (HIWORD(wParam) = CBN_SELCHANGE) then
  begin
  nIndex:= SendMessage(GComboBoxEx, CB_GETCURSEL, 0, 0);
  WizardForm.NextButton.Caption:= IntToStr(nIndex); // Выбранная строка
  WizardForm.CancelButton.Caption:= IntToStr(SendMessage(GComboBoxEx, CB_GETCOUNT, 0, 0)); // Количество строк
  //
  Text:= '';
  SetLength(Text,SendMessage(GComboBoxEx, CB_GETLBTEXTLEN, nIndex, 0));
  SendMessage(GComboBoxEx, CB_GETLBTEXT, nIndex, CastStringToInteger(Text));
  if WizardForm.Caption<>Text then
  WizardForm.Caption:= Text;
  end;
  end;
end;
  Result := CallWindowProc(OldProc, hWnd, Msg, wParam, lParam);
end;

///////////////////////////
procedure CreateComboBoxEx;
begin
  { create comboex }  // высота с учетом раскрытого списка
  GComboBoxEx := CreateWindowEx(0, WC_COMBOBOXEX, '', WS_VISIBLE or WS_CHILD or CBS_DROPDOWNLIST or CBS_SORT, ScaleX(20), ScaleX(340), ScaleX(210), ScaleY(320), WizardForm.Handle, 0, HInstance, 0);

  { create imagelist } // высота в итоге свернутого 32 (учитывается ImageList_Create)
  GImageList := ImageList_Create(32, 32, ILC_COLOR32{ use ILC_COLOR24 for 24-bit icons }, 0, 0);

  { add items }
  CreateComboBoxExItem('Test entry 1', 'Setup1.ico');
  CreateComboBoxExItem('Test entry 2', 'Setup2.ico');

  { assign imagelist }
  SendMessage(GComboBoxEx, CBEM_SETIMAGELIST, 0, GImageList);

  { set selected item }
  SendMessage(GComboBoxEx, CB_SETCURSEL, 0, 0);

  if GComboBoxEx <> 0 then
  OldProc:= SetWindowLong(WizardForm.Handle, -4, CallbackAddr('WindowProc'));
end;

////////////////////////////
procedure DestroyComboBoxEx;
begin
  if GImageList <> 0 then
  ImageList_Destroy(GImageList);
  if GComboBoxEx <> 0 then
  DestroyWindow(GComboBoxEx);
end;

///////////////////////////
procedure InitializeWizard;
begin
//  WizardForm.OuterNotebook.Hide;
  CreateComboBoxEx;
end;

////////////////////////////
procedure DeinitializeSetup();
begin
  SetWindowlong(WizardForm.Handle, -4, OldProc);
  DestroyComboBoxEx;
end;
в загашник упёр бы
теперь думаю можно.
 
Последнее редактирование:

South

Знаток
Проверенный
sergey3695, у imagelist нет родителя/владельца (оно не окно)
т.е. затем же, зачем ты пишешь Icon.Free; при создании айтема
 

EvilAlex

Старожил
Lamer.Code, в faq пример есть.
Подскажите пожалуйста, первый раз увидел такую функцию, очень понравилась. но не могу скрыть ее на других страницах инсталла...

переделал под себя) скорее всего накосячил) если не сложно, гляньте что не так? Заранее спасибо!
 

Вложения

sergey3695

Ветеран
Модератор
EvilAlex, максимум через 3 месяца гляну, пока нет возможности сесть за пк. Я думаю, мне капитан не разрешит ноутбуком пользоваться )
 
Сверху