Sdílet prostřednictvím


Architektura WPF

Toto téma obsahuje prohlídku hierarchie tříd WPF (Windows Presentation Foundation). Popisuje většinu hlavních subsystémů WPF a popisuje, jak interagují. Také podrobně popisuje některé volby provedené architekty WPF.

System.Object

Primární programovací model WPF je vystavený prostřednictvím spravovaného kódu. V rané fázi návrhu WPF došlo k řadě debat o tom, kde by měla být čára vytažena mezi spravovanými součástmi systému a nespravovanými. MODUL CLR poskytuje řadu funkcí, díky kterým je vývoj produktivnější a robustnější (včetně správy paměti, zpracování chyb, systému běžných typů atd.), ale za cenu.

Hlavní komponenty WPF jsou znázorněny na obrázku níže. Červené části diagramu (PresentationFramework, PresentationCore a milcore) jsou hlavní části kódu WPF. Z nich je to jen jedna nespravovaná komponenta – milcore. Milcore je napsaný v nespravovaném kódu, který umožňuje úzkou integraci s DirectX. Veškerý displej v WPF se provádí prostřednictvím modulu DirectX, který umožňuje efektivní vykreslování hardwaru a softwaru. WPF také vyžaduje přesnou kontrolu nad pamětí a spouštěním. Kompoziční motor v milcore je extrémně citlivý na výkon a vyžaduje, aby se mnoho výhod CLR získal výkon.

Pozice WPF v rozhraní .NET Framework.

Komunikace mezi spravovanými a nespravovanými částmi WPF je popsána dále v tomto tématu. Zbývající část spravovaného programovacího modelu je popsaná níže.

System.Threading.DispatcherObject

Většina objektů v WPF je odvozena od DispatcherObject, který poskytuje základní konstrukty pro zpracování souběžnosti a vláken. WPF je založen na systému zasílání zpráv implementovaném dispečerem. To funguje podobně jako známé čerpadlo zpráv Win32; dispečer WPF ve skutečnosti používá k provádění volání mezi vlákny zprávy User32.

Existují skutečně dva základní koncepty, které je třeba pochopit při diskusi o souběžnosti ve WPF: dispečer a spřažení se závity.

Během fáze návrhu WPF bylo cílem přejít na jedno vlákno provádění, ale model bez vláken "spřažení". Spřažení vláken nastane, když komponenta používá identitu spuštěného vlákna k uložení určitého typu stavu. Nejběžnější formou je použití místního úložiště vláken (TLS) k uložení stavu. Spřažení vláken vyžaduje, aby každé logické vlákno spouštění bylo vlastněno pouze jedním fyzickým vláknem v operačním systému, které může být náročné na paměť. Na konci byl model vláken WPF udržován v synchronizaci s existujícím modelem threadingu User32 s jedním spuštěním s vlákny se spřažením vláken. Hlavním důvodem této interoperability byla interoperabilita – systémy, jako je OLE 2.0, schránka a Internet Explorer, vyžadují provádění spřažení s jedním vláknem (STA).

Vzhledem k tomu, že máte objekty s vlákny STA, potřebujete způsob, jak komunikovat mezi vlákny a ověřit, že jste ve správném vlákně. Zde spočívá role dispečera. Dispečer je základní systém odesílání zpráv s několika prioritními frontami. Mezi příklady zpráv patří oznámení o nezpracovaném vstupu (posun myši), funkce rámce (rozvržení), nebo uživatelské příkazy (provedení této metody). Odvozením z DispatcherObject vytvoříte objekt CLR, který má chování STA a bude vám při vytváření přidělen ukazatel na dispečera.

System.Windows.DependencyObject

Jednou z primárních architekturních filozofie používaných při sestavování WPF byla předvolba vlastností před metodami nebo událostmi. Vlastnosti jsou deklarativní a umožňují snadněji určit záměr místo akce. To také podporuje modelem řízený systém nebo systém řízený daty pro zobrazení obsahu uživatelského rozhraní. Tato filozofie měla zamýšlený účinek na vytvoření více vlastností, se kterými byste se mohli svázat, aby bylo možné lépe řídit chování aplikace.

Aby bylo možné mít více systémů řízených vlastnostmi, bohatší systém vlastností, než jaký modul CLR poskytuje, byl potřeba. Jednoduchým příkladem této pestrosti je oznámení o změnách. Pokud chcete povolit obousměrnou vazbu, potřebujete obě strany vazby, aby podporovaly oznámení o změnách. Pokud chcete mít chování svázané s hodnotami vlastností, musíte být upozorněni, když se hodnota vlastnosti změní. Rozhraní Microsoft .NET Framework má rozhraní INotifyPropertyChange, které umožňuje objekt publikovat oznámení o změnách, ale je volitelný.

WPF poskytuje bohatší systém vlastností odvozený od DependencyObject typu. Systém vlastností je skutečně systém vlastností závislostí, který sleduje závislosti mezi výrazovými vlastnostmi a automaticky revaliduje hodnoty vlastností při změně závislostí. Pokud například máte vlastnost, která dědí (například FontSize), systém se automaticky aktualizuje, pokud se vlastnost změní u nadřazeného prvku, který dědí hodnotu.

Základem systému vlastností WPF je koncept výrazu vlastnosti. V této první verzi WPF je systém výrazů vlastností uzavřen a výrazy jsou poskytovány jako součást architektury. Výrazy jsou důvodem, proč systém vlastností nemá pevně zakódovanou datovou vazbu, stylování nebo dědičnost, ale tyto jsou poskytovány pozdějšími vrstvami v rámci frameworku.

Systém vlastností také poskytuje zhuštěné ukládání hodnot vlastností. Vzhledem k tomu, že objekty můžou mít desítky (pokud ne stovky) vlastností a většina hodnot je ve výchozím stavu (zděděno, nastaveno podle stylů atd.), ne každá instance objektu musí mít plnou váhu každé vlastnosti definované v ní.

Poslední novou funkcí systému vlastností je pojem připojených vlastností. Prvky WPF jsou založeny na principu složení a opětovného použití komponent. Často se jedná o případ, že některé obsahující elementy (například Grid element rozložení) potřebují další data o podřízených prvcích, aby bylo možné řídit jeho chování (například informace o řádku nebo sloupci). Místo přidružení všech těchto vlastností ke každému prvku může jakýkoli objekt poskytovat definice vlastností pro jakýkoli jiný objekt. To se podobá funkcím "expando" JavaScriptu.

System.Windows.Media.Visual

Když je systém definován, dalším krokem je zobrazování pixelů na obrazovce. Třída Visual poskytuje pro vytvoření stromu vizuálních objektů, z nichž každý volitelně obsahuje pokyny pro kreslení a metadata o tom, jak tyto instrukce vykreslit (výřez, transformace atd.). Visual je navržen tak, aby byl extrémně jednoduchý a flexibilní, takže většina funkcí nemá žádné veřejné rozhraní API a spoléhá na chráněné funkce zpětného volání.

Visual je skutečně vstupním bodem systému WPF pro tvorbu sestavení. Visual je spojovacím bodem mezi těmito dvěma subsystémy, spravovaným rozhraním API a nespravovaným systémem milcore.

WPF zobrazuje data procházením nespravovaných datových struktur, které spravuje milcore. Tyto struktury, označované jako uzly složení, představují hierarchický strom zobrazení s pokyny pro vykreslování na každém uzlu. Tento strom, který je znázorněn na pravé straně obrázku níže, je přístupný pouze prostřednictvím protokolu zasílání zpráv.

Při programování WPF vytvoříte Visual prvky a odvozené typy, které interně komunikují s kompozičním stromem prostřednictvím tohoto protokolu zasílání zpráv. Každý Visual v WPF může vytvořit jeden, žádný nebo několik uzlů kompozice.

Vizuální strom Windows Presentation Foundation.

Zde je velmi důležitý architektonický detail – celý strom vizuálů a kreslicích instrukcí se ukládá do mezipaměti. Z hlediska grafiky používá WPF zachovaný vykreslovací systém. To umožňuje systému překreslit při vysokých obnovovacích frekvencích bez toho, aby se kompoziční systém blokoval zpětnými voláními do uživatelského kódu. To pomůže zabránit dojmu nereagující aplikace.

Další důležitý detail, který není ve skutečnosti znatelný v diagramu, je způsob, jakým systém ve skutečnosti provádí složení.

V User32 a GDI systém funguje na okamžitý režim ořezávání. Když je potřeba vykreslit komponentu, systém vytvoří ořezy, mimo které komponenta nesmí dotýkat pixelů, a pak se zobrazí výzva k vykreslení pixelů v daném rámečku. Tento systém funguje velmi dobře v paměťových omezených systémech, protože když se něco změní, stačí se dotkne jenom ovlivněné komponenty – žádné dvě komponenty nikdy nepřispívají k barvě jednoho pixelu.

WPF používá model obrazu "malířský algoritmus". To znamená, že místo ořezávání jednotlivých komponent je každá komponenta renderována od zadní části k přední části displeje. To umožňuje, aby každá komponenta překreslovala zobrazení té předchozí. Výhodou tohoto modelu je, že můžete mít složité, částečně průhledné obrazce. Díky dnešnímu modernímu grafickému hardwaru je tento model poměrně rychlý (což nebylo případ, kdy byl vytvořen User32/ GDI).

Jak už bylo zmíněno dříve, základní filozofie WPF spočívá v přechodu k deklarativnímu modelu programování zaměřenému na vlastnosti. Ve vizuálním systému se to zobrazí na několika zajímavých místech.

Za prvé, pokud uvažujete o grafickém systému v režimu zachování, je to skutečně posun od příkazového modelu typu Čára()/Čára() k modelu orientovanému na data – nová Čára()/nová Čára(). Tento přechod na vykreslování řízené daty umožňuje vyjádřit složité operace s pokyny výkresu pomocí vlastností. Typy odvozené z Drawing jsou v podstatě objektový model pro vykreslování.

Za druhé, pokud vyhodnotíte animační systém, uvidíte, že je téměř úplně deklarativní. Místo toho, aby vývojář musel vypočítat další umístění nebo další barvu, můžete animace vyjádřit jako sadu vlastností objektu animace. Tyto animace pak můžou vyjádřit záměr vývojáře nebo návrháře (přesuňte toto tlačítko sem za 5 sekund) a systém dokáže určit nejúčinnější způsob, jak toho dosáhnout.

System.Windows.UIElement

UIElement definuje základní subsystémy, včetně rozložení, vstupu a událostí.

Rozložení je základní koncept WPF. V mnoha systémech existuje pevná sada modelů rozložení (HTML podporuje tři modely pro rozložení; tok, absolutní a tabulky) nebo žádný model pro rozložení (User32 skutečně podporuje pouze absolutní umístění). WPF začal s předpokladem, že vývojáři a návrháři chtěli flexibilní, rozšiřitelný model rozložení, který by mohl být řízen hodnotami vlastností místo imperativní logiky. Na úrovni UIElement se zavádí základní kontrakt pro uspořádání – dvoufázový model s Measure a Arrange pasy.

Measure umožňuje komponentě určit, jakou velikost by chtěla vzít. Toto je samostatná fáze než Arrange, protože existuje mnoho situací, kdy nadřazený prvek požádá podřízený prvek, aby několikrát změřil optimální pozici a velikost. Skutečnost, že nadřazené prvky požádají podřízené prvky, aby změřily obsah, což demonstruje další klíčovou filozofii WPF – velikost podle obsahu. Všechny ovládací prvky WPF podporují možnost přizpůsobit velikost podle přirozené velikosti jejich obsahu. Díky tomu je lokalizace mnohem jednodušší a umožňuje dynamické rozložení prvků při změně velikosti. Fáze Arrange umožňuje rodiči, aby mohl umístit a určit konečnou velikost každého podřízeného objektu.

Hodně času se často věnuje mluvení o výstupní straně WPF – Visual a souvisejících objektech. Na vstupní straně však existuje obrovské množství inovací. Pravděpodobně nejzákladnější změna vstupního modelu pro WPF je konzistentní model, pomocí kterého se vstupní události směrují přes systém.

Vstup pochází jako signál ovladače zařízení v režimu jádra a směruje se na správný proces a vlákno prostřednictvím složitého procesu zahrnujícího jádro Windows a User32. Jakmile se zpráva User32 odpovídající vstupu směruje do WPF, převede se na nezpracovanou vstupní zprávu WPF a odešle se dispečerovi. WPF umožňuje převést nezpracované vstupní události na více skutečných událostí, což umožňuje implementaci funkcí, jako je MouseEnter, na nízké úrovni systému s garantovaným doručením.

Každá vstupní událost se převede na aspoň dvě události – událost "preview" a skutečnou událost. Všechny události ve WPF mají koncept směrování skrze strom prvků. Události jsou označovány jako "bublající", pokud procházejí od cíle směrem nahoru stromem k kořeni, a jako "tunelující", pokud začínají u kořene a procházejí stromem směrem dolů k cíli. Vstupní tunel událostí náhledu, který umožňuje libovolnému prvku ve stromu filtrovat nebo provádět akce s událostí. Běžné události (bez náhledu) pak bublají od cíle až ke kořeni.

Díky tomuto rozdělení mezi tunelovou a bublinovou fází funguje implementace funkcí, jako jsou akcelerátory klávesnice, konzistentním způsobem ve složeném světě. V user32 byste implementovali akcelerátory klávesnice tak, že máte jednu globální tabulku obsahující všechny akcelerátory, které chcete podporovat (Ctrl+N mapování na "Nový"). V dispečeru pro vaši aplikaci byste volali TranslateAccelerator, který by prozkoumal vstupní zprávy v User32 a určil, jestli se některá shoduje s registrovaným akcelerátorem. Ve WPF by to nefungilo, protože systém je plně "kompozibilní" – jakýkoli prvek dokáže zpracovat a použít jakýkoli akcelerátor klávesnice. Tento dvoufázový model pro vstup umožňuje komponentám implementovat vlastní "TranslateAccelerator".

Abychom šli s tímto krokem dále, UIElement také představuje pojem CommandBindings. Systém příkazů WPF umožňuje vývojářům definovat funkce z hlediska koncového bodu příkazu – něco, co implementuje ICommand. Vazby příkazů umožňují elementu definovat mapování mezi vstupním gestem (Ctrl+N) a příkazem (Nový). Jak vstupní gesta, tak definice příkazů jsou rozšiřitelné a dají se připojit společně v době použití. Díky tomu je například triviální, aby koncový uživatel mohl přizpůsobit vazby klíčů, které chce použít v rámci aplikace.

Do této chvíle v tématu byly středem pozornosti "základní" funkce WPF – funkce implementované v sestavení PresentationCore. Při sestavování WPF bylo žádoucím výsledkem čisté oddělení základních částí (jako je smlouva o rozložení s Measure a Arrange) a částí architektury (například implementace konkrétního rozložení jako Grid). Cílem bylo poskytnout bod rozšiřitelnosti nízko v zásobníku, který by umožnil externím vývojářům, v případě potřeby, vytvářet vlastní frameworky.

System.Windows.FrameworkElement

Na FrameworkElement lze pohlížet dvěma různými způsoby. Zavádí sadu zásad a přizpůsobení subsystémů zavedených v nižších vrstvách WPF. Zavádí také sadu nových subsystémů.

Hlavní zásada zavedená FrameworkElement se týká rozložení aplikace. FrameworkElement vychází ze základní smlouvy o rozložení zavedené UIElement a přidává pojem "prostor" rozložení, který autorům rozložení usnadňuje mít konzistentní soubor sémantik rozložení řízených vlastnostmi. Vlastnosti jako HorizontalAlignment, VerticalAlignment, MinWidth a Margin (například) poskytují všem komponentám odvozeným z FrameworkElement konzistentní chování uvnitř kontejnerů rozložení.

FrameworkElement poskytuje také snadnější vystavení rozhraní API pro mnoho funkcí nalezených v základních vrstvách WPF. Poskytuje například FrameworkElement přímý přístup k animaci prostřednictvím BeginStoryboard metody. A Storyboard poskytuje způsob, jak skriptovat více animací proti sadě vlastností.

Dvě nejkritičtější věci, které FrameworkElement představuje, jsou datové vazby a styly.

Subsystém datové vazby ve WPF by měl být poměrně známý pro každého, kdo použil Windows Forms nebo ASP.NET k vytvoření uživatelského rozhraní aplikace. V každém z těchto systémů existuje jednoduchý způsob, jak vyjádřit, že chcete, aby jedna nebo více vlastností z daného prvku byla vázána na část dat. WPF plně podporuje vazbu vlastností, transformace a vazbu seznamu.

Jednou z nejzajímavějších funkcí datové vazby ve WPF je zavedení šablon dat. Šablony dat umožňují deklarativní určení způsobu vizualizace části dat. Místo vytvoření vlastního uživatelského rozhraní, které lze svázat s daty, můžete místo toho problém obejít a nechat data určit zobrazení, které se vytvoří.

Styling je opravdu lehká forma datové vazby. Pomocí stylů můžete vytvořit vazbu sady vlastností ze sdílené definice na jednu nebo více instancí elementu. Styly se aplikují na prvek buď explicitním odkazem (nastavením Style vlastnosti), nebo implicitně přidružením stylu k typu CLR elementu.

System.Windows.Controls.Control

Nejdůležitější funkcí ovládacího prvku je šablonování. Pokud vnímáte kompoziční systém WPF jako systém vykreslování v režimu zachovaného stavu, šablonování umožňuje ovládacímu prvku popsat jeho vykreslování parametrizovaně a deklarativním způsobem. A ControlTemplate není ve skutečnosti nic víc než skript k vytvoření sady podřízených prvků s vazbami na vlastnosti nabízené ovládacím prvku.

Control poskytuje sadu výchozích vlastností, jako například Foreground, Background, Padding, které autoři šablon mohou použít k přizpůsobení zobrazení ovládacího prvku. Implementace ovládacího prvku poskytuje datový model a model interakce. Model interakce definuje sadu příkazů (například Zavřít pro okno) a vazby na vstupní gesta (například kliknutí na červené X v horním rohu okna). Datový model poskytuje sadu vlastností pro přizpůsobení modelu interakce nebo přizpůsobení zobrazení (určené šablonou).

Toto rozdělení mezi datový model (vlastnosti), model interakce (příkazy a události) a model zobrazení (šablony) umožňuje úplné přizpůsobení vzhledu a chování ovládacího prvku.

Běžným aspektem datového modelu ovládacích prvků je model obsahu. Pokud se podíváte na ovládací prvek jako Button, uvidíte, že má vlastnost s názvem "Content" typu Object. Ve Windows Forms a ASP.NET by tato vlastnost obvykle byla řetězcem , ale omezuje typ obsahu, který můžete vložit do tlačítka. Obsah tlačítka může být jednoduchý řetězec, složitý datový objekt nebo celý strom elementu. V případě datového objektu se šablona dat používá k vytvoření zobrazení.

Shrnutí

WPF je navržený tak, aby umožňoval vytvářet dynamické prezentační systémy řízené daty. Každá část systému je navržená tak, aby vytvářela objekty prostřednictvím sad vlastností, které řídí chování. Datová vazba je základní součástí systému a je integrovaná v každé vrstvě.

Tradiční aplikace vytvoří zobrazení a pak vytvoří vazbu na některá data. Ve WPF se vše o ovládacím prvku, každém aspektu zobrazení, generuje pomocí určitého typu datové vazby. Text nalezený uvnitř tlačítka se zobrazí vytvořením složeného ovládacího prvku uvnitř tlačítka a vazbou jeho zobrazení na vlastnost obsahu tlačítka.

Když začnete s vývojem aplikací založených na WPF, mělo by to být velmi známé. Vlastnosti, použití objektů a datové vazby můžete nastavit velmi stejným způsobem, jako můžete použít Windows Forms nebo ASP.NET. S hlubším zkoumáním architektury WPF zjistíte, že existuje možnost vytváření mnohem bohatších aplikací, které v zásadě považují data za základní ovladač aplikace.

Viz také