Przezroczyste ikony w nagłówku kontrolki ListView

Patyk

Kontrolka ListView, dostarczona nam przez twórców WinAPI, w swoim szerokim wachlarzu funkcji posiada m.in. możliwość umieszczenia własnych ikon w nagłówku każdej z kolumn widoku Report (styl LVS_REPORT).

Przypisując kontrolce ListView obiekt ImageList, przechowujący dodane przez nas bitmapy i ikony, dostajemy możliwość wzbogacenia nagłówka dowolnej kolumny o obrazek pochodzący z obiektu ImageList.

Po utworzeniu i wypełnieniu wybranymi obrazkami obiektu ImageList, kolejnym krokiem jest przypisanie tego obiektu kontrolce ListView. Robimy to korzystając z makra ListView_SetImageList lub wysyłając do kontrolki komunikat LVM_SETIMAGELIST z odpowiednimi parametrami. Ostatnią czynnością jest przypisanie każdej tworzonej przez nas kolumnie indeksu odpowiedniego obrazka z obiektu ImageList.

Cały proces przebiega bezproblemowo dopóki nasze obrazki nie wykorzystują koloru przezroczystości. Jak się okazuje, standardowo nie jest on poprawnie obsługiwany przez kontrolkę ListView. Zamiast wstawienia kolorów tła, przezroczyste piksele wypełniane są białym kolorem, co daje mało estetyczny efekt:

lv_n_trans.png
Rozwiązanie tego problemu jest banalnie proste, choć nie do końca oczywiste. Do wymuszenia poprawnego interpretowania przezroczystości przez ListView konieczne jest bowiem dodanie do styli kontrolki stałej LVS_SHAREIMAGELISTS, której opis pochodzący z dokumentacji WinAPI nie zawiera ani słowa o przezroczystości:

LVS_SHAREIMAGELISTS - Specifies that the control does not take ownership of the image lists assigned to it; that is, it does not destroy the image lists when it is destroyed. This style enables the same image lists to be used with multiple list view controls.”

Styl ten możemy dodać już przy tworzeniu kontrolki:

    HWND hListView = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL,
        WS_VISIBLE | WS_CHILD | LVS_REPORT | LVS_SHAREIMAGELISTS,
        5, 5, 300, 100,
        hWnd, NULL, hInstance, NULL);

...lub korzystając z funkcji SetWindowLong(), pamiętając, że podczas przypisywania kontrolce obiektu ImageList styl ten musi być już dodany:

    SetWindowLong(hListView, GWL_STYLE, GetWindowLong(hListView, GWL_STYLE) |
        LVS_SHAREIMAGELISTS);

W obydwu przypadkach osiągniemy satysfakcjonujący nas efekt:

lv_trans.png
Przykładowy kod:

    // Upewnienie się, że biblioteka kontrolek została poprawnie załadowana
    INITCOMMONCONTROLSEX icex;

    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icex.dwICC  = ICC_LISTVIEW_CLASSES;
    InitCommonControlsEx(&icex);

    // Utworzenie kontrolki ListView
    HWND hListView = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL,
        WS_VISIBLE | WS_CHILD | LVS_REPORT | LVS_SHAREIMAGELISTS,
        5, 5, 300, 100,
        hWnd, NULL, hInstance, NULL);

    // Utworzenie obiektu ImageList
    HIMAGELIST hImageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK,
        2, 0);

    // Wczytanie ikon do obiektu ImageList
    char* icons[] = { "square.ico", "circle.ico", "triangle.ico" };

    for(int i = 0; i < 3; i++) {
        ImageList_AddIcon(hImageList, (HICON) LoadImage(hInstance, icons[i],
            IMAGE_ICON, 16, 16, LR_LOADFROMFILE));
    }

    // Przypisanie obiektu ImageList do kontrolki ListView
    ListView_SetImageList(hListView, hImageList, LVSIL_SMALL);

    // Utworzenie kolumn kontrolki ListView
    LVCOLUMN lvColumn = {0};

    lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_IMAGE;
    lvColumn.fmt = LVCFMT_LEFT | LVCFMT_IMAGE;
    lvColumn.cx = 90;

    char* columnNames[] = { "First", "Second", "Third" };

    for(int i = 0; i < 3; i++) {
        lvColumn.pszText = columnNames[i];
        lvColumn.iImage = i;

        ListView_InsertColumn(hListView, i, &lvColumn);
    }

Gotowy projekt Code::Blocks: listview.zip

</p>

1 komentarz

Inspirowałem się: http://4programmers.net/Forum/viewtopic.php?id=106873

Zastanawiałem się, czy nie pasuje lepiej do Artykułów, ale ostatecznie zdecydowałem się na FAQ. Dobry wybór?