Tipy pro UI desktop aplikací - DialogWindow, DialogForm

Jan Holan       22. 3. 2012       WPF, WinForms       6292 zobrazení

Přestože je technologie WPF asi ta nejlepší volba pro tvorbu desktopových aplikací, je zde horší podpora pokud děláme klasicky vypadající Windows aplikace. Pro modální dialogy zde nejsou všechny možnosti jako u Windows Forms. Proto jsem udělal pomocnou třídu DialogWindow sloužící jako base třída pro okna dialogů.
Podíváme se, jak třída funguje.

FolderDialog

FolderDialog2

Spot the difference?

Ano jedná se o možnost ovládání zobrazení ovládacích tlačítek v titulku dialogu. Standardně ve WPF u okna, když je povoleno měnění velikosti (konkrétně ResizeMode="CanResizeWithGrip"), pak již není umožněno skrýt Maximize a Minimize tlačítka. Také u okna není vůbec umožněno například skrýt ikonu nalevo. Třída DialogWindow zavádí vlastnosti ControlBox, ShowIcon, tak jak jsme na ně zvyklí u Windows Forms. Tlačítka Maximize a Minimize jsou skryté vždy, ty u modálních dialogu nepotřebujeme.

Jak je to udělané? Ačkoliv vnitřek WPF okna a prvky na něm již nepoužívají standardní Win32 API funkce, to neplatí o okně samotném. To je stále tvořeno pomoci Win32 API, a proto můžeme získat jeho Handler, a s ním volat standartní API funkce pro ovládání oken. Handler okna lze ve WPF získat pomoci třídy WindowInteropHelper, na změnu vlastností okna pak použijeme API funkce GetWindowLong, SetWindowLong, SetWindowPos a SendMessage. Změny provedeme v události Loaded okna, kód vypadá takto:

private void DialogWindow_Loaded(object sender, RoutedEventArgs e)
{
    const string cFakeIcon = @"AAABAAEAEBACAAAAAACwAAAAFgAAACgAAAAQAAAAIAAAAAEAAQAAAAAAgAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////";

    IntPtr hWnd = new WindowInteropHelper(this).Handle;

    ...

    UInt32 windowStyleex = GetWindowLong(hWnd, GWL_EXSTYLE);
    windowStyleex |= WS_EX_DLGMODALFRAME;   //Hide dialog icon if icon is not set (Fixed Dialog style)
    SetWindowLong(hWnd, GWL_EXSTYLE, windowStyleex);

    if (this.ResizeMode == ResizeMode.CanResize || this.ResizeMode == ResizeMode.CanResizeWithGrip || !this.ControlBox)
    {
        UInt32 windowStyle = GetWindowLong(hWnd, GWL_STYLE);

        //Disable Minimize and Maximize buttons
        if (this.ResizeMode == ResizeMode.CanResize || this.ResizeMode == ResizeMode.CanResizeWithGrip)
        {
            if (originalResizeMode == System.Windows.ResizeMode.CanMinimize)
            {
                windowStyle = windowStyle & ~WS_MAXIMIZEBOX;
            }
            else
            {
                windowStyle = windowStyle & ~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX;
            }
        }
        //Set ControlBox (hides dialog Icon, Minimize, Maximize and Close buttons)
        if (!this.ControlBox)
        {
            windowStyle = windowStyle & ~WS_SYSMENU;
        }

        SetWindowLong(hWnd, GWL_STYLE, windowStyle);
    }

    SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);

    if (!this.ShowIcon)
    {
        if (this.Icon == null)
        {
                //Fix hide dialog icon - Set dummy icon and then clear icon
                var fakeIconData = new System.IO.MemoryStream(Convert.FromBase64String(cFakeIcon));
                this.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(fakeIconData);
                this.Icon = null;
        }

        //Hide icon
        SendMessage(hWnd, 0x80, 0, 0);
        SendMessage(hWnd, 0x80, 1, 0);
    }
}

Celou třídu DialogWindow si můžete prohlédnou nebo stáhnout zde.

Ve třídě je toho řešeno více, tak už jen stručněji:

  • Třída mění typ vlastnosti DialogResult na enum System.Windows.MessageBoxResult.
  • Ve změněných metodách ShowDialog je navíc automaticky nastavována vlastnost SizeToContent.
  • Jsou změněny výchozí nastavení některých vlastností - WindowStartupLocation = WindowStartupLocation.CenterOwner, ShowInTaskbar = false a ResizeMode = ResizeMode.NoResize.
  • Je automaticky nastaveno TextOptions.TextFormattingMode="Display" (viz popis zde).
  • Poslední změna je o něco složitější, jedná se o změnu border dialogu. U dialogů, kde není povolen resize okna, není ve Windows 7 při vypnuté Aero standardní okraj okna vykreslen tlustým okrajem, ale jen jednoduchým.

TestDialog

Oprava je řešena tak, že border dialogu je zaměněn pomoci nastavení ResizeMode = ResizeMode.CanResize a dále je zaregistrována metoda DialogWindow_WndProc, ve které je potlačen resize dialogu. (Pokud by někdo věděl, jak to řešit jinak, tak mi napište, protože stejný problém mám i u klasických MessageBox dialogů.)

Pro Windows Forms jsem připravil podobnou třídu DialogFormTemplate, která mění výchozí nastavení některých vlastností pro dialog (FormBorderStyle = FormBorderStyle.FixedDialog, MaximizeBox = false, MinimizeBox = false, ShowIcon = false, ShowInTaskbar = false, StartPosition = CenterScreen) a řeší výše popsaný problém s okrajem formuláře. K dispozici je zde.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

RE: Tipy pro UI desktop aplikací - DialogWindow, DialogForm

Ahoj,

Vem si jednochuchý příklad, chceš udělat dialog, který vypadá jako MessageBox, jenom tam například potřebuješ jiná tlačítka nebo trochu poupravit design. Proč by jsi tam musel mít iconu nalevo? Proč by si něměl ve WPF takový dialog vytvořit, když standardní MessageBox tak prostě vypadá? Tyhle vlastnosti okna ve WPF prostě z nevysvětlitelného důvodu chybí (ve WinForm normálně jsou), a tahle třída ti je tam zase vrátí.

To jestli se poruší UI guideline je podle mě už na kontrétním designu dialogu.

Kdyžtak to o víkendu probereme.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

RE: Tipy pro UI desktop aplikací - DialogWindow, DialogForm

Honzo, není tak trochu návod na to, jak obejít s použitím WinAPI UI guideline pro Widnows? Takhle se dá totiž obejít cokoli. Což o to, článek je to hezký, ale nějak mi uniká, k čemu je to dobré.

Vždy upřednostňujte doporučované řešení: neohýbat dialogy, ale vytvořit si vlastní okno...

Jirka

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback