\


 Tuesday, 14 April 2009
Windows Mobile formulář přes celý displej - v nativním kódu

Na MSDN fórech jsem si všiml, že se vícekrát objevil dotaz, jak v nativním kódu vytvořit okno přes celou obrazovku, které se bude chovat jako formulář v Compact .Net Framework aplikaci při nastavení vlastnosti WindowState = Maximized.

API SHFullScreen sice přepne okno do celoobrazovkového režimu, ale při zobrazení SIPu se opět objeví taskbar. Při skrytí SIPu se okno vrátí do celoobrazovkového režimu. První, co mě napadlo, je skrýt samotný taskbar. Idea dobrá, mohli jsme mít jednoduché řešení,  ale autoři Windows Mobile jako již tradičně řekli ne.

Po několika pokusech jsem zjistil, že jediné použitelné řešení představuje změna pozice a velikosti formuláře vždy, když odchytím zprávu WINDOWPOSCHANGED. To celé je korunováno opakovaným voláním SHFullScreen. Na řešení v Compact .Net Frameworku jsem se nedíval, abych si nekazil radost z vyřešeného úkolu, takže netuším, zda autoři CNF používají ještě nějaký další trik.:-)

Níže naleznete příklad, který je založen na výchozí šabloně Smart Device Windows API projektu. Zajímavé části jsou zvýrazněny tučně. Tento postup lze samozřejmě jednoduše použít ve WTL nebo MFC.

Tady se ještě zeptám:

1) Jsou alespoń pro někoho z Vás tyto tipy/FAQ zajímavé? Já sám miluji přecházení mezi nativním a “managed” kódem, ale asi nemá smysl, abych tyto tipy psal na blog, jestliže o nativní kód (již) nikdo nestojí.Potom by stačílo, abych je nechal utopeny ve fóru o mobilních zařízeních, kde poslouží podobným individuím jako jsem já. V zásobě mám například často kladený dotaz, jak ve Windows Mobile dialogu zachytit WM_KEY zprávy. :-)  I když sám si stále programování v (Compact) .Net frameworku bez dobré znalosti nativního kódu nedovedu představit - což je v roce 2009 možná tristní a nečekaná zpráva.:-)

2) A obecnější dotaz – zajímají někoho z vás tipy pro Windows Mobile/Compact .Net Framework? Pro mě, jak asi tušíte, je programování pro WIN Mobile zařízení potěšení, a proto se podobné tipy objevují i na blogu, který píšu hlavně pro zábavu. I když většinu času jsem nyní strávil vývojem v Silverlightu, WPF, WCF a léčením  roztomilých neštovic na zpočátku krásné tváři Linq2SQL, což znamená, že se na blogu se objeví i další témata, která se budou točit kolem návrhu různých typů aplikací a jako bonus odhalíme nejčastější průšvihy spojené s anemickými modely (i model-view-viewmodely :-))aplikací.

// FullScreen.cpp : Defines the entry point for the application. 
// 
 
#include "stdafx.h"
#include "FullScreen.h"  
 
 
#define MAX_LOADSTRING 100  
 
// Global Variables: 
HINSTANCE           g_hInst;            // current instance 
HWND                g_hWndMenuBar;      // menu bar handle 
RECT usedRect;  
// Forward declarations of functions included in this code module: 
ATOM            MyRegisterClass(HINSTANCE, LPTSTR);  
BOOL            InitInstance(HINSTANCE, int);  
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);  
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);  
void MakeFullScreen();  
 
void MakeFullScreen(HWND hWnd)  
{  
      
    SetRect(&usedRect, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));  
      
    LONG windowWidth = usedRect.right - usedRect.left;  
    LONG windowHeight = usedRect.bottom - usedRect.top;  
 
    MoveWindow(hWnd,   
               usedRect.left,   
               usedRect.top,  
               windowWidth,  
               windowHeight,  
               FALSE);  
 
    //SIPINFO info;  
    //info.cbSize = sizeof(info);  
    //ZeroMemory(&info, sizeof(info));  
    //info.rcVisibleDesktop = usedRect;  
    //  
    //if (!::SipSetInfo(&info))  
    //{  
    //  int error = GetLastError();  
    //  return FALSE;  
    //}  
 
    SHFullScreen(hWnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON);  
      
      
      
}
 
int WINAPI WinMain(HINSTANCE hInstance,  
                   HINSTANCE hPrevInstance,  
                   LPTSTR    lpCmdLine,  
                   int       nCmdShow)  
{  
    MSG msg;  
 
    // Perform application initialization: 
    if (!InitInstance(hInstance, nCmdShow))   
    {  
        return FALSE;  
    }  
 
    HACCEL hAccelTable;  
    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_FULLSCREEN));  
 
    // Main message loop: 
    while (GetMessage(&msg, NULL, 0, 0))   
    {  
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))   
        {  
            TranslateMessage(&msg);  
            DispatchMessage(&msg);  
        }  
    }  
 
    return (int) msg.wParam;  
}  
 
// 
//  FUNCTION: MyRegisterClass() 
// 
//  PURPOSE: Registers the window class. 
// 
//  COMMENTS: 
// 
ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)  
{  
    WNDCLASS wc;  
 
    wc.style         = CS_HREDRAW | CS_VREDRAW;  
    wc.lpfnWndProc   = WndProc;  
    wc.cbClsExtra    = 0;  
    wc.cbWndExtra    = 0;  
    wc.hInstance     = hInstance;  
    wc.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FULLSCREEN));  
    wc.hCursor       = 0;  
    wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);  
    wc.lpszMenuName  = 0;  
    wc.lpszClassName = szWindowClass;  
 
    return RegisterClass(&wc);  
}  
 
// 
//   FUNCTION: InitInstance(HINSTANCE, int) 
// 
//   PURPOSE: Saves instance handle and creates main window 
// 
//   COMMENTS: 
// 
//        In this function, we save the instance handle in a global variable and 
//        create and display the main program window. 
// 
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)  
{  
    HWND hWnd;  
    TCHAR szTitle[MAX_LOADSTRING];      // title bar text 
    TCHAR szWindowClass[MAX_LOADSTRING];    // main window class name 
 
    g_hInst = hInstance; // Store instance handle in our global variable 
 
    // SHInitExtraControls should be called once during your application's initialization to initialize any 
    // of the device specific controls such as CAPEDIT and SIPPREF. 
    SHInitExtraControls();  
 
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);   
    LoadString(hInstance, IDC_FULLSCREEN, szWindowClass, MAX_LOADSTRING);  
 
    //If it is already running, then focus on the window, and exit 
    hWnd = FindWindow(szWindowClass, szTitle);    
    if (hWnd)   
    {  
        // set focus to foremost child window 
        // The "| 0x00000001" is used to bring any owned windows to the foreground and 
        // activate them. 
        SetForegroundWindow((HWND)((ULONG) hWnd | 0x00000001));  
        return 0;  
    }   
 
    if (!MyRegisterClass(hInstance, szWindowClass))  
    {  
        return FALSE;  
    }  
 
    hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,  
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);  
 
    if (!hWnd)  
    {  
        return FALSE;  
    }  
 
    // When the main window is created using CW_USEDEFAULT the height of the menubar (if one 
    // is created is not taken into account). So we resize the window after creating it 
    // if a menubar is present 
    if (g_hWndMenuBar)  
    {  
        RECT rc;  
        RECT rcMenuBar;  
 
        GetWindowRect(hWnd, &rc);  
        GetWindowRect(g_hWndMenuBar, &rcMenuBar);  
        rc.bottom -= (rcMenuBar.bottom - rcMenuBar.top);  
          
        MoveWindow(hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, FALSE);  
    }  
      
    ShowWindow(hWnd, nCmdShow);  
    //MakeFullScreen(hWnd); 
    UpdateWindow(hWnd);  
      
      
    return TRUE;  
}  
 
// 
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) 
// 
//  PURPOSE:  Processes messages for the main window. 
// 
//  WM_COMMAND  - process the application menu 
//  WM_PAINT    - Paint the main window 
//  WM_DESTROY  - post a quit message and return 
// 
// 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
{  
    int wmId, wmEvent;  
    PAINTSTRUCT ps;  
    HDC hdc;  
    LPWINDOWPOS pos;  
 
    static SHACTIVATEINFO s_sai;  
      
    switch (message)   
    {  
        case WM_COMMAND:  
            wmId    = LOWORD(wParam);   
            wmEvent = HIWORD(wParam);   
            // Parse the menu selections: 
            switch (wmId)  
            {  
                case IDM_HELP_ABOUT:  
                    DialogBox(g_hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, About);  
                    break;  
                case IDM_OK:  
                    SendMessage (hWnd, WM_CLOSE, 0, 0);               
                    break;  
                default:  
                    return DefWindowProc(hWnd, message, wParam, lParam);  
            }  
            break;  
        case WM_CREATE:  
            SHMENUBARINFO mbi;  
 
            memset(&mbi, 0, sizeof(SHMENUBARINFO));  
            mbi.cbSize     = sizeof(SHMENUBARINFO);  
            mbi.hwndParent = hWnd;  
            mbi.nToolBarId = IDR_MENU;  
            mbi.hInstRes   = g_hInst;  
 
            if (!SHCreateMenuBar(&mbi))   
            {  
                g_hWndMenuBar = NULL;  
            }  
            else 
            {  
                g_hWndMenuBar = mbi.hwndMB;  
            }  
 
            // Initialize the shell activate info structure 
            memset(&s_sai, 0, sizeof (s_sai));  
            s_sai.cbSize = sizeof (s_sai);  
            break;  
        case WM_PAINT:  
              
            hdc = BeginPaint(hWnd, &ps);  
              
            // TODO: Add any drawing code here... 
              
            EndPaint(hWnd, &ps);  
            break;  
        case WM_DESTROY:  
            CommandBar_Destroy(g_hWndMenuBar);  
            PostQuitMessage(0);  
            break;  
        case WM_WINDOWPOSCHANGED: 
           pos = (LPWINDOWPOS) lParam;  
            if ((pos->cx != usedRect.right - usedRect.left) ||  
                (pos->cy != usedRect.bottom - usedRect.top))  
            {  
                MakeFullScreen(hWnd);  
            }  
            break;
  
        case WM_ACTIVATE:  
               
            // Notify shell of our activate message 
             SHHandleWMActivate(hWnd, wParam, lParam, &s_sai, FALSE);  
            break;  
        case WM_SETTINGCHANGE:  
            SHHandleWMSettingChange(hWnd, wParam, lParam, &s_sai);  
            break;  
 
        default:  
            return DefWindowProc(hWnd, message, wParam, lParam);  
    }  
    return 0;  
}  
 
// Message handler for about box. 
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)  
{  
    switch (message)  
    {  
        case WM_INITDIALOG:  
            {  
                // Create a Done button and size it.   
                SHINITDLGINFO shidi;  
                shidi.dwMask = SHIDIM_FLAGS;  
                shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;  
                shidi.hDlg = hDlg;  
                SHInitDialog(&shidi);  
            }  
            return (INT_PTR)TRUE;  
 
        case WM_COMMAND:  
            if (LOWORD(wParam) == IDOK)  
            {  
                EndDialog(hDlg, LOWORD(wParam));  
                return TRUE;  
            }  
            break;  
 
        case WM_CLOSE:  
            EndDialog(hDlg, message);  
            return TRUE;  
 
    }  
    return (INT_PTR)FALSE;  
}  



Tuesday, 14 April 2009 15:04:43 (Central Europe Standard Time, UTC+01:00)       
Comments [8]  Compact .Net Framework | Nativní kód


Tuesday, 14 April 2009 16:22:59 (Central Europe Standard Time, UTC+01:00)
Pokud se resize a re-fullscreen deje az reakce na POSCHANGED, neblika to?

Jinak ja pro WinMobile uz nejaky cas neprogramuju, ale tipy/triky a obludnosti si rad prectu (a pritom si potvrdim, ze mi vyvoj pro mobilni Windows vlastne nijak nechybi).
Tuesday, 14 April 2009 16:30:41 (Central Europe Standard Time, UTC+01:00)
Diky za reakci, alespon jeden dalsi, i když dnes už platonický WM zvrhlík. :)

Zkoušel jsem a nebliká. Ta změna není postřehnutelná. Zkoušeno na HTC Touch Pro, HTC Kaiser a MDA Vario s různými verzemi OS a ROM.
Tuesday, 14 April 2009 20:25:21 (Central Europe Standard Time, UTC+01:00)
Ja je taky prelitnu, i kdyz vzdy kdyz si koupim novy mobil a cekam, ze uz konecne bude dost rychly, tak nadseni po par testech upada. Ted spise cekam na SL na Mobilu, aby se ten vyvoj co nejvice mohl sjednotit ...
Firemni software s vlastni GUI knihovnou (C++), je bohuzel moc velky a narazi na limity WM. Takze jsem mel naportovanu jen konsolovou verzi, ktera sice porazela rychlosti Mainframy, ale to dnes umi kazdy Atom :-)
Takze ja bych spis preferoval zkusenosti z navrhu aplikaci a "velkeho" .Netu. Treba mi ale vyjde cas se dostat na tvou prednasku v GMC (tedy hlavne jde o to, jestli zbyde na me misto v zasedacce :-).
PS: Taky diky za povzbuzujici komentar na mem blogu :-)
Tuesday, 14 April 2009 20:50:51 (Central Europe Standard Time, UTC+01:00)
Bobris: Take dekuju za komentar, na vasem (tvem) blogu jsem si rekl, ze povzbuzujici komentar napsat musim, aby dalsi dobry blog neumrel, protoze autorovi se misto odpovedi dostava zaryteho mlceni. ;) Takze z cisteho sobectvi jsem vykonal neco dobreho. ;)
Take se tesim na port mobilniho SL, i kdyz vyvoj pro WM me bavi i ted, protoze si trochu pripadam jako vyvojar a ne pasivni instrument, ktery je anonymni funkci nejakeho frameworku.
Na prednasku se tesim, i kdyz mozna bude lepsi, kdyz si nejake specialitky nechame na neoficialni cast kurzu.
Jeste jednou diky za reakci!
Thursday, 16 April 2009 20:32:17 (Central Europe Standard Time, UTC+01:00)
Dekuji za podobne tipy. Pro WM vyvijim jak drobne aplikace ve sve praci, tak jeste drobnejsi ve svem volnu. Kvuli WM jsem se dokonce opet zacal ucit C/C++
Thursday, 16 April 2009 21:44:57 (Central Europe Standard Time, UTC+01:00)
Arci: Diky za komentar, ktery ve mne vzbudil dojem, ze nejsem jen hlasem volajiciho na pousti.;)
Friday, 17 April 2009 10:48:16 (Central Europe Standard Time, UTC+01:00)
Ja rozhodne tipy a triky ... pro WM (at uz CF nebo native code) rozhodne zeru. Takze neprestavat. :)
Friday, 17 April 2009 10:52:31 (Central Europe Standard Time, UTC+01:00)
Jiri Cincura: Tak vida, dalsi nadsenec. :) Diky za reakci, budu tedy v techto tipech (obcas) pokracovat.
Comments are closed.