Department of InformatiX
Microsoft .NET Micro Framework Tools & Resources

Hlavní problém při zobrazování message boxu je blokovat volající okno. Mohli byste například udělat nekonečnou smyčku čekající na to, až bude váš message box zavřen, ale to by nebyl moc šťastný nápad, protože byste tim úplně zatížili celé zařízení. O malinko lepší by bylo uvažovat o blokování vlákna nebo nějaké synchronizaci, ale věřte mi, nechcete blokovat vlákno dispatcheru - má totiž mnohem důležitější věci na práci, než sedět a koukat na váš message box. Ve skutečnosti je tedy spíš problémem udržet okno blokované, ale přitom zpracovávající požadavky - mělo by se například překreslovat kdykoliv je třeba.

Dispatcher a ti ostatní

WPF model přišel s třídami jako je Dispatcher, DispatcherObject, DispatcherTimer a spoustu dalších Dispacther- věciček. Dispatcher udržuje frontu úkolů, které se mají vykonat. Je to seznam delegátů, kteří jsou voláni jeden po druhém. Typické úkoly ve frontě souvisí s událostmi - jako například překreslit nebo zmáčknuté tlačítko. Ve velkém frameworku řadí Dispatcher úkoly podle jejich priority, ale tady v .NET Micro Frameworku jsou si všichni rovni.

Jelikož je Dispatcher technicky vzato pouze fronta, musí existovat něco, co bude frontu hlídat a úkoly v ní uložené vykonávat. Zde přichází na scénu DispatcherFrame, což je vlastně taková ukončitelná while smyčka. Běžící dispatcher vypadá asi takto:

while (true) { DispatcherOperation operation = DeQueue(); // zkontrolujeme, máme-li ve frontě nějaké úkoly if (operation != null) // pokud ano, operation.Invoke(); // provedeme je; else // pokud ne, _event.WaitOne(); // počkáme si na ně! }

Je třeba podotknout, že na velkém frameworku to vypadá malinko jinak - uvnitř smyčky probíhá obvyklé odbavení zpráv (kvůli interopabilitě s Win32, které jsme tu prosti) a volání operací z fronty není na první pohled tak očividné. Když se podívate na tén kód pozorněji, zjistíte, že while nikdy neskončí, což není úplně to co potřebujeme. Když někdo zmáčkne ve vaší aplikaci tlačítko a vy zobrazíte message box, pravděpodobně budete chtít pokračovat v obslužné rutince tlačítka až bude message box zavřen. Takže, jak nám může DispatcherFrame pomoci? Pokud se podíváte na seznam jeho členů, zjistíte, že má jen jednu jedinou vlastnost, a to Continue. Ten kousek zmíněý kousek kódu se spouští voláním Dispatcher.PushFrame(DispatcherFrame frame) a vlastně vypadá malinko jinak:

while (frame.Continue) { // zpracování úkolů jako nahoře }

Kdykoliv za běhu vaší aplikace můžete nastavit frame.Continue na hodnotu false, smyčka se ukončí a aplikace bude pokračovat tam kde přestala, za voláním PushFrame.

Zobrazování modálního okna

S tím co jsme si právě řekli nejspíš již víte jak takové okno zobrazit modálně:

public static void ShowModal(/* this */ Window window) { // Parameter konstruktoru exitWhenRequested určuje, zda bude rámec hodný // a ukončí smyčku když o to bude požádán, např. při Application.Shutdown(). // Rámce jsou od přírody hodné a také Continue mají nastavené na true, takže DispatcherFrame modalBlock = new DispatcherFrame(); // je to samé, jako modalBlock = new DispatcherFrame(true); modalBlock.Continue = true; // Přihlásíme handler, který smyčku ve vhodné chvíli ukončí. window.IsVisibleChanged += delegate { Modal_VisibleChanged(modalBlock, window.Visibility); }; window.Visibility = Visibility.Visible; // nebo jiný váš způsob zobrazení okna Dispatcher.PushFrame(modalBlock); // spustíme nekonečnou smyčku, která zablokuje vlákno, // dokud nebude ukončena // Pěkně po sobě uklidíme a handler zase odhlásíme. window.IsVisibleChanged -= delegate { Modal_VisibleChanged(modalBlock, window.Visibility); }; window.Close(); // nebo jiný váš způsob zbavení se okna } private static void Modal_VisibleChanged(DispatcherFrame block, Visibility visibility) { if (visibility!= Visibility.Visible) // pokud bylo okno schováno, block.Continue = false; // nastavíme rámec tak, aby byla smyčka ukončena }

Tuto statickou metodu můžete umístit kam se vám hodí, např. rovnou do třídy Program. Vytvořte své okno jako obvykle, a při obsluze tlačítka nebo kdekoliv jinde zavolejte Program.ShowModal(vašeOkno) a je to! Vaše dialogové okno bude nyní viditelné a zatímco blokuje volající vlákno, vykonává práci ukládanou na dispatcher. Abyste vlákno zase uvolnili, stačí nastavit Visibility modálního okna na Hidden. Dispathcer dokončí právě probíhající operaci a provede návrat z PushFrame metody.

Mimochodem, Dispatcher si udržuje hloubku rámců, takže vaše modální okno může tuto metodu také zavolat a zobrazit další modální okno, čímž zablokuje sebe, původní volající vlákno také a tak dále. Když už není žádný rámec, který operace vykonával (hloubka je 0), celý Dispatcher se ukončí, ale to už je jiný příběh. Nemusíte se obávát, že byste to mohli neúmyslně způsobit, neboť spuštění vaší aplikace metodou Application.Run() vloží do dispatcheru svůj vlastní DispatcherFrame.

DoEvents

V modelu Windows Forms můžete zavolat metodu DoEvents(), která dá prostor všem čekajícím procesům k jejich realizaci. Toho se často využívá například když děláte dlouhodobější operaci s uživatelským rozhraním a chcete mu dát příležitost, aby se překreslilo. Windows Presentation Foundation je založena na úplně odlišném modelu a přístupu, takže žádná podobná metoda neexistuje (a ve skutečnosti ani není potřeba, pokud vše děláte správně). Nicméně je s pomocí DispatcherFrame technicky možné tuto metodu implementovat:

public void DoEvents() { DispatcherFrame doEventsFrame = new DispatcherFrame(); Dispatcher.CurrentDispatcher.BeginInvoke(new DispatcherOperationCallback(ExitFrame), doEventsframe); Dispatcher.PushFrame(doEventsFrame); } public object ExitFrame(object f) { ((DispatcherFrame)f).Continue = false; return null; }

Metoda jen zařadí delegáta nakonec fronty, takže ten bude zavolán až když jsou všechny předchozí úkoly dokončeny. Delegát pak jen řekne rámci, aby smyčku ukončil a tím může v této chvíli vaše aplikace zase pokračovat. Přestože by se mohlo zdát, že DoEvents by vám mohlo vyřešit vaše potíže, vždy se ujistěte, že neexistuje žádná jiná cesta, jak problém zvládnout.

Comments
Sign in using Live ID to be able to post comments.