Programování PocketPC naruby (6/12)

Patříte mezi uživatele PocketPC, kteří by si rádi také něco naprogramovali, řadíte se však k tzv. falešným začátečníkům? Pak právě vám je určen náš seriál věnovaný programování pro kapesní počítače s Windows Mobile. Dnešní díl věnujme nastavení a hlavnímu oknu programu.
V minulé části našeho seriálu jsme si popsali návrh vlastního programu. Dnes se podíváme na nastavení a hlavní okno aplikace.

Nastavení

Nastavení obou verzí aplikace bude standardně uloženo v registru. Funkce pro manipulaci s nastavením v registru RegCreateKeyEx, RegQueryValueEx jsou podporovány ve všech verzích Windows. Nastavení aplikace bude v kapesním i stolním počítači uloženo v klíči HKEY_CURRENT_USER\\Software\\Petr Lesny\\List1.0.

Pro přenositelnost kódu nesmíme zapomenout i zde použít pro manipulaci s textem datový typ TCHAR a odpovídající funkce pro práci s ním. Práci s registrem je možné zapouzdřit do jednoduchého objektu. I tento objekt budeme využívat na obou platformách beze změny.

Hlavní okno aplikace

Hlavní okno aplikace bude obsahovat jediný prvek uživatelského rozhraní: seznam s ikonami (ListView). Prvek ListView může zobrazovat informace v jednom ze čtyř režimů: velké ikony s popisem, malé ikony, seznam a seznam s hlavičkami. Pro náš účel bude stačit zobrazení s malými ikonami.

ListView vytvoříme při obsluze zprávy WM_CREATE; po vytvoření k němu musíme přidat seznam ikon a naplnit jej obsahem. Abychom ušetřili paměť, jsou všechny položky uloženy v objektu Storage a ListView je vytvořen s příznakem LVS_OWNERDATA; stačí nastavit počet prvků seznamu a prvek ListView se bude pomocí upozornění LVN_GETDISPINFO dotazovat naší aplikace na texty jednotlivých položek.


HWND CreateListView(const HWND hwnd)
{
    RECT rect;

    GetClientRect(hwnd, &rect);
    
    HWND hwndList = CreateWindowEx(0L, 
                WC_LISTVIEW, TEXT(""), 
                WS_CHILD|WS_VISIBLE|LVS_LIST|LVS_SINGLESEL|
                LVS_EDITLABELS|LVS_AUTOARRANGE|LVS_OWNERDATA, 
                rect.left, rect.top, rect.right, rect.bottom,
                hwnd, NULL, g_hInstance, NULL);

    HIMAGELIST himl = ImageList_Create(16, 16, ILC_COLOR, 3, 1);
    ImageList_AddIcon(himl, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ITEMBLANK)));
    ImageList_AddIcon(himl, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ITEMRED)));
    ImageList_AddIcon(himl, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ITEMGREEN)));

    ListView_SetImageList(hwndList, himl, LVSIL_SMALL);
    ListView_DeleteAllItems(hwndList);
    ListView_SetItemCount(hwndList, g_STORAGE->getN());
    ListView_SetColumnWidth(hwndList, 0, 240); // Šířka podobně jako na PPC

    return hwndList;
}

Prvek ListView ovládáme pomocí funkcí ListView_XXX, které jsou popsány v dokumentaci Windows SDK. Všechny zprávy od prvku ListView jsou naší aplikaci zaslány prostřednictvím upozornění (WM_NOTIFY). Šipky, funkční klávesy apod. obsluhuje prvek ListView sám.

Nesmíme také zapomenout změnit velikost prvku ListView při každé změně velikosti okna (při obsluze zprávy WM_SIZE).

Změna některé položky našeho seznamu ListView probíhá následujícím způsobem:

  • Uživatel se rozhodne změnit text u položky ListView tak, že na ní klikne 2x myší nebo programátor zavolá funkci ListView_EditLabel.
  • Prvek ListView se "zeptá" naší aplikace, zda může povolit změnu prvku (zasláním upozornění LVN_BEGINLABELEDIT). Vrátíme hodnotu FALSE, proto bude změna položky povolena.
  • Až uživatel dokončí změnu položky, zašle ListView naší aplikaci upozornění LVN_ENDLABELEDIT a je na programátorovi, aby se postaral o uložení změněného textu.

Pro všechny funkce prvku ListView, které budeme potřebovat si vytvoříme obalující funkce (wrappers):

Co budeme chtít udělat Jméno naší obalující funkce Kdy bude funkce volána
Vytvoření CreateListView Obsluha zprávy WM_CREATE
Zrušení DestroyListView Obsluha zprávy WM_DESTROY
Změna velikosti prvku ListView ResizeListViewToWindow Obsluha zprávy WM_SIZE
Přidání nové položky na konec seznamu AddNewItemToListView Položka menu "New", stisk klávesy Insert, stisk kláves Shift+Enter
Vymazání všech položek seznamu DeleteAllItemsInListView Položka menu "Clear".
Editace vybrané položky seznamu EditListViewItem Stisk klávesy F2, dvojklik na položce seznamu; editaci mohou Windows zahájit i samostatně.
Smazání vybrané položky seznamu DeleteListViewItem Stisk klávesy Delete.
Aktualizace textu položky seznamu, která byla editována UpdateListViewItem Obsluha upozornění LVN_ENDLABELEDIT.
Vyzvednutí textu a čísla ikony z prvku Storage a jejich předání prvku ListView GetListViewItemInfo Obsluha upozornění LVN_GETDISPINFO.
Změna ikony u vybrané položky seznamu ListViewChangeIcon Stisk mezerníku na vybrané položce.
Posun vybrané položky seznamu nahoru nebo dolů MoveListBoxItem Shift + klávesa se šipkou nahoru nebo dolů
Vybrání první položky seznamu. SelectFirstListViewItem Stisk klávesy Enter v situaci, kdy zatím není zvýrazněna žádná položka seznamu.

Tyto obalující funkce (wrappers) zjednodušují ovládání prvku ListView a jeho propojení s obsluhou zpráv v hlavním okně aplikace. Příklad dvou obalujících funkcí:


void DeleteListViewItem(const HWND hwnd, const HWND hwndList)
{
    // Zjistíme základní informace o vybrané položce a počtu prvků
	
    int item = ListView_GetNextItem(hwndList, -1, LVNI_ALL|LVNI_SELECTED);
    int last = ListView_GetItemCount(hwndList);

    if(item != -1)
    {
        // Vymažeme prvek v paměti
			
        g_STORAGE->deleteItem(item);
		
        // Překreslíme všechny prvky seznamu
		
        ListView_SetItemCount(hwndList, g_STORAGE->getN());
        ListView_RedrawItems(hwndList, 0, last - 1);
        UpdateWindow(hwnd);
    }
}

a


void AddNewItemToListView(const HWND hwnd, const HWND hwndList)
{
    // Vytvoříme nový prvek v paměti
	
    int item = g_STORAGE->createItem(S_NONE, g_szNewItemText);

    // Přidáme informaci o něm prvku ListView
	
    LV_ITEM lvitem;
    lvitem.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_STATE;
    lvitem.iSubItem = 0;
    lvitem.state = LVIS_FOCUSED|LVIS_SELECTED;
    lvitem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
    lvitem.iImage = S_NONE;
    lvitem.pszText = LPSTR_TEXTCALLBACK;
    lvitem.iItem = item;
    ListView_InsertItem(hwndList, &lvitem);

    // Zahájíme editaci
	
    SetFocus(hwndList);
    ListView_EditLabel(hwndList, item);
}

V další části našeho pravidelného seriálu si blíže popíšeme návrh uživatelského rozhraní.