Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > C/C++: Системное программирование и WinAPI > Изображение в ListBox


Автор: Maksim V. 10.01.08, 14:12
Сунул картинку в лисбокс. Однако возникла проблема. Фон надписей в листбоксе затирает картинку, но если поизменять размеры окна, этот фон убирается. Пробовал UpdateWindow по таймеру и SetBkMode для надписей в листбоксе - бесполезно. Подскажите что надо исправить. Теряюсь в догадках. Вот код:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    var
     hBMP : hBitmap;
     
    function DlgProc(hWin: HWND; uMsg: UINT; wp: WPARAM; lp: LPARAM): bool; stdcall;
    var
      ps: TPaintStruct;
      dc: HDC;
      hMemDC: HDC;
      rect: TRect;
    begin
      Result := False;
      case uMsg of
       WM_INITDIALOG :
       begin
       hBMP := LoadBitmap(hInstance, 'IMAGE');
       SendMessage(GetDlgItem(hWin, 113), LB_ADDSTRING, 0, Integer(PChar('Ñòðîêà')));
       SendMessage(GetDlgItem(hWin, 113), LB_ADDSTRING, 0, Integer(PChar('Ñòðîêà')));
       end;
       WM_PAINT:
        begin
         dc := BeginPaint(getdlgitem(hWin, 113), ps);
         hMemDC := CreateCompatibleDC(dc);
         SelectObject(hMemDC, hBMP);
         GetClientRect(getdlgitem(hWin, 113), rect);
         BitBlt(dc, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, SRCCOPY);
         DeleteDC(hMemDC);
         EndPaint(getdlgitem(hWin, 113), ps);
         InvalidateRect(getdlgitem(hWin, 113), 0, False);
         UpdateWindow(hwin);
        end;
       WM_DESTROY, WM_CLOSE:
        begin
         DeleteObject(hBMP);
         PostQuitMessage(0);
        end;
     end;
    end;

Автор: ElcnU 10.01.08, 18:57
Цитата msdn
The WM_DRAWITEM message is sent to the parent window of an owner-drawn button, combo box, list box, or menu when a visual aspect of the button, combo box, list box, or menu has changed.

+
Цитата
LBS_OWNERDRAWFIXED
The owner of the list box is responsible for drawing its contents; the items in the list box are the same height.

только так :yes:

Автор: Jenya 10.01.08, 20:13
А если вместо рисования через WM_PAINT нарисовать фон через WM_ERASEBKGND ??

Автор: Maksim V. 10.01.08, 20:54
ElcnU, добавил это свойство к листбоксу. Однако взамешательстве насчет WM_DRAWITEM (я не знаю что именно там прорисовать - цвет или кисть, потому как никогда не прорисовывал самостоятельно элементы).

Jenya, это как через WM_ERASEBKGND? Все-таки разные посылаемые сообщения. Ну-ка пример в студию...

Автор: B.V. 11.01.08, 07:49
http://www.vbaccelerator.com/home/vb/Code/Controls/Combo_and_List_Boxes/Owner_Draw_Combo_and_List_Box/article.asp

С VB, надеюсь, проблем нет?
Если все же есть, тогда тебе сюда: http://www.codeproject.com/KB/combobox/MFC_OwnerDraw_Listbox.aspx

Автор: Maksim V. 11.01.08, 12:17
Я на Делфи, а не с Визуал Си. А на другом регистрироваться надо чтобы скачать пример.

Автор: Jenya 12.01.08, 09:36
Следует уточнить, я имел ввиду использование WM_ERASEBKGND , если картинку использовать в виде фона ListBox. А для картинки в каждом элементе следует использовать OWNERDRAW, как тебе и сказали.

Автор: Maksim V. 25.08.08, 16:58
Так, а если и то и то сразу использовать. То есть самостоятельную отрисовку строк и отрисовку фоновой картинки. Строки окрашиваю цветом черезстрочно (немного научился):
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    var
      ListWnd  : THandle;
      ListDC   : hDC;
      RectBox  : TRect;
      TextBox  : Array [0..$400] of Char;
      BrushNew : hBrush;
      BrushOld : hBrush;
      lBoxBitmap : HBITMAP;
      MemoryDC   : hDC;
      BitmapInfo : TBitmapInfo;
      Rect       : TRect;
     
    //////////////////////////////
     
        WM_DRAWITEM :
          case wParam of
            ID_PLAYLIST_LISTBOX :
                begin
                  ListWnd := PDRAWITEMSTRUCT(lParam).hwndItem;
                  RectBox := PDRAWITEMSTRUCT(lParam).rcItem;
                  ListDC := PDRAWITEMSTRUCT(lParam).hDC;
                  if Integer(PDRAWITEMSTRUCT(lParam).ItemID) > -1 then
                    begin
                      if (PDRAWITEMSTRUCT(lParam).itemState and ODS_SELECTED) <> 0 then
                        begin
                          FillRect(ListDC, RectBox, GetSysColorBrush(COLOR_HIGHLIGHT));
                          SetBkMode(ListDC, GetSysColor(COLOR_HIGHLIGHT));
                          SetTextColor(ListDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
                        end
                      else
                        begin
                          FillRect(ListDC, RectBox, GetSysColorBrush(COLOR_WINDOW));
                          SetBkColor(ListDC, GetSysColor(COLOR_WINDOW));
                          SetTextColor(ListDC, GetSysColor(COLOR_WINDOWTEXT));
                          if (PDRAWITEMSTRUCT(lParam).itemID mod 2) <> 0 then
                            begin
                              BrushNew := CreateSolidBrush(RGB(245, 245, 245));
                              BrushOld := SelectObject(ListDC, BrushNew);
                              FillRect(ListDC, RectBox, BrushNew);
                              SetBkColor(ListDC, RGB(245, 245, 245));
                              SelectObject(ListDC, BrushOld);
                              DeleteObject(BrushNew);
                            end;
                        end;
                      RectBox.Left := RectBox.Left + 5;
                      RectBox.Right := RectBox.Right - 5;
                      SendMessage(ListWnd, LB_GETTEXT, PDRAWITEMSTRUCT(lParam).ItemID, LongInt(@TextBox[0]));
                      DrawText(ListDC, @TextBox[0], -1, RectBox, DT_SINGLELINE or DT_VCENTER);
     
                    end;
                end;
          end;


Нужно проместить на фон лисбокса битмап. Его загружаю при инициализации диалога:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            lBoxBitmap := LoadImage(hInstance, MAKEINTRESOURCE('Logo'), IMAGE_BITMAP, 0, 0, 0);


Ну и остался такой код, ранее использованный в WM_DRAWITEM:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            GetClientRect(GetDlgItem(hWndPls, ID_PLAYLIST_LISTBOX), Rect);
            MemoryDC := CreateCompatibleDC(ListDC);
            SelectObject(MemoryDC, lBoxBitmap);
            GetObject(lBoxBitmap, SizeOf(TBitmapInfo), @BitmapInfo);
            BitBlt(ListDC, ((Rect.Right - Rect.Left) - BitmapInfo.bmiHeader.biWidth) - 7, ((Rect.Bottom - Rect.Top) - BitmapInfo.bmiHeader.biHeight) - 7, BitmapInfo.bmiHeader.biWidth, BitmapInfo.bmiHeader.biHeight, MemoryDC, 0, 0, SRCAND);
            SelectObject(MemoryDC, ListDC);
            DeleteObject(MemoryDC);
            DeleteDC(MemoryDC);


Понятно что отрисовка растра происходит для кадого пункта в списке. Поэтому при скроллинге контрола или перемещении строк эта картинка накладывается на предыдущие в строках и получается смесь. Самое легко е что можно придумать в такой ситуации - обновлять окно. Но это не устраивает. Как в WM_ERASEBKGND растр подставить в контекст? Что-то не получается. Попробовал подставить, но овнедравные строки перекрывают картинку. Как совместить-то и то и то?

Автор: Maksim V. 29.08.08, 11:11
Неужели никто из программистов тутошних не знает? В аттаче пример текущей реализации. Как нужно рисовать то в WM_ERASEBKGND? Я пытался в такой же контекст копировать картинку - вообще ничего не было, да и ерундень была с фоном тулбара на диалоге из-за этого. Хотя бы на C/С++ дайте код, я уж переделаю как нужно. Или вообще сабклассинг тут нужен?
Текущий код рисует битмап через WM_DRAWITEM - соответственно в каждом пункте списка из-за чего при перемещении по элементам битмап в списке кучу раз на себя накладывается. FillRect пытался добавить, но затираются строчки в списке.

Автор: Jenya 30.08.08, 12:09
Имелось ввиду WM_ERASEBKGND в оконной процедуре ListBox, то есть сабклассить - надо.
Определяешь клиентскую область и заливаешь кистью с нужным цветом.

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)