Delen via


WPF-architectuur

In dit onderwerp vindt u een rondleiding door de klassehiërarchie van Windows Presentation Foundation (WPF). Het behandelt de meeste van de belangrijkste subsystemen van WPF en beschrijft hoe ze communiceren. Ook worden enkele van de keuzes van de architecten van WPF nader uitgelegd.

System.Object

Het primaire WPF-programmeermodel wordt weergegeven via beheerde code. Vroeg in de ontwerpfase van WPF waren er een aantal debatten over waar de lijn moet worden getrokken tussen de beheerde onderdelen van het systeem en de onbeheerde onderdelen. De CLR biedt een aantal functies die de ontwikkeling productiever en robuuster maken (waaronder geheugenbeheer, foutafhandeling, gemeenschappelijk typesysteem, enzovoort), maar ze kosten.

De belangrijkste onderdelen van WPF worden geïllustreerd in de onderstaande afbeelding. De rode secties van het diagram (PresentationFramework, PresentationCore en milcore) zijn de belangrijkste codegedeelten van WPF. Van deze, slechts één is een onbeheerde component - milcore. Milcore is geschreven in onbeheerde code om een nauwe integratie met DirectX mogelijk te maken. Alle weergaven in WPF worden uitgevoerd via de DirectX-engine, waardoor efficiënte hardware- en softwarerendering mogelijk is. WPF vereist ook fijne controle over geheugen en uitvoering. De samenstellingsengine in milcore is uiterst prestatiegevoelig en vereiste dat er veel voordelen van de CLR werden opgegeven om prestaties te winnen.

De positie van WPF binnen .NET Framework.

De communicatie tussen de beheerde en onbeheerde delen van WPF wordt verderop in dit onderwerp besproken. De rest van het beheerde programmeermodel wordt hieronder beschreven.

System.Threading.DispatcherObject

De meeste objecten in WPF zijn afgeleid van DispatcherObject, die de basisconstructies biedt voor het omgaan met gelijktijdigheid en threading. WPF is gebaseerd op een berichtensysteem dat door de dispatcher wordt geïmplementeerd. Dit werkt net als de bekende Win32-berichtpomp; in feite gebruikt de WPF-dispatcher User32-berichten voor het uitvoeren van cross thread-aanroepen.

Er zijn echt twee kernconcepten die u moet begrijpen bij het bespreken van gelijktijdigheid in WPF: de dispatcher en threadaffiniteit.

Tijdens de ontwerpfase van WPF was het doel om over te stappen op één enkele uitvoeringsthread, maar naar een niet-threadgebonden model. Threadaffiniteit treedt op wanneer een onderdeel de identiteit van de uitvoerbare thread gebruikt om een bepaald type status op te slaan. De meest voorkomende vorm hiervan is het gebruik van het lokale threadarchief (TLS) om de status op te slaan. Threadaffiniteit vereist dat elke logische thread van uitvoering eigendom is van slechts één fysieke thread in het besturingssysteem, wat geheugenintensief kan worden. Uiteindelijk werd het threadingmodel van WPF gesynchroniseerd met het bestaande User32-threadingmodel van uitvoering met één thread met threadaffiniteit. De belangrijkste reden hiervoor was interoperabiliteit: systemen zoals OLE 2.0, het klembord en Internet Explorer vereisen allemaal uitvoering op basis van een enkele thread-affiniteit (STA).

Gezien het feit dat u objecten met STA-threading hebt, hebt u een manier nodig om te communiceren tussen threads en te controleren of u zich op de juiste thread bevindt. Hierin ligt de rol van de dispatcher. De dispatcher is een eenvoudig systeem voor het verzenden van berichten, met meerdere wachtrijen met prioriteit. Voorbeelden van berichten zijn onbewerkte invoermeldingen (muis verplaatst), frameworkfuncties (indeling) of gebruikersopdrachten (voer deze methode uit). Door af te leiden van DispatcherObject, creëert u een CLR-object met STA-gedrag en krijgt u een pointer naar een dispatcher bij het aanmaken.

System.Windows.DependencyObject

Een van de primaire architectuurfiosofieën die worden gebruikt bij het bouwen van WPF, was een voorkeur voor eigenschappen voor methoden of gebeurtenissen. Eigenschappen zijn declaratief en stellen u in staat om de intentie gemakkelijker op te geven in plaats van actie. Dit biedt ook ondersteuning voor een modelgestuurd of gegevensgestuurd systeem voor het weergeven van inhoud van de gebruikersinterface. Deze filosofie had het beoogde effect van het creëren van meer eigenschappen waaraan u kon binden, om het gedrag van een toepassing beter te beheren.

Om meer van het systeem aangestuurd te hebben door eigenschappen, was een uitgebreider eigenschappensysteem nodig dan wat de CLR biedt. Een eenvoudig voorbeeld van deze rijkdom is wijzigingsmeldingen. Als u binding in twee richtingen wilt inschakelen, hebt u beide zijden van de binding nodig om wijzigingsmeldingen te ondersteunen. Als u gedrag wilt koppelen aan eigenschapswaarden, moet u een melding ontvangen wanneer de eigenschapswaarde wordt gewijzigd. Microsoft .NET Framework heeft een interface, INotifyPropertyChange-, waarmee een object wijzigingsmeldingen kan publiceren, maar dit is optioneel.

WPF biedt een uitgebreider eigenschappensysteem, afgeleid van het DependencyObject type. Het eigenschapssysteem is echt een 'afhankelijkheidseigenschapssysteem' omdat hiermee afhankelijkheden tussen eigenschapsexpressies worden bijgehouden en eigenschapswaarden automatisch opnieuw worden gevalideerd wanneer afhankelijkheden worden gewijzigd. Als u bijvoorbeeld een eigenschap hebt die overneemt (zoals FontSize), wordt het systeem automatisch bijgewerkt als de eigenschap wordt gewijzigd op een bovenliggend element dat de waarde overneemt.

De basis van het WPF-eigenschappensysteem is het concept van een eigenschapsexpressie. In deze eerste release van WPF wordt het eigenschapsexpressiesysteem gesloten en worden de expressies allemaal geleverd als onderdeel van het framework. Expressies zijn waarom het eigenschappensysteem geen vaste code voor gegevensbinding, styling of overname heeft, maar in plaats daarvan wordt geleverd door latere lagen binnen het framework.

Het eigenschappensysteem biedt ook een beperkte opslag van eigenschapswaarden. Omdat objecten tientallen (zo niet honderden) eigenschappen kunnen hebben en de meeste waarden de standaardstatus hebben (overgenomen, ingesteld op stijlen, enzovoort), hoeft niet elk exemplaar van een object het volledige gewicht te hebben van elke eigenschap die erop is gedefinieerd.

De laatste nieuwe functie van het eigenschappensysteem is het begrip gekoppelde eigenschappen. WPF-elementen zijn gebaseerd op het principe van samenstelling en hergebruik van onderdelen. Het is vaak het geval dat sommige elementen (zoals een Grid indelingselement) aanvullende gegevens over onderliggende elementen nodig hebben om het gedrag ervan te bepalen (zoals de rij-/kolomgegevens). In plaats van al deze eigenschappen te koppelen aan elk element, mag elk object eigenschapsdefinities opgeven voor elk ander object. Dit is vergelijkbaar met de 'expando'-functies van JavaScript.

System.Windows.Media.Visual

Met een systeem dat al is gedefinieerd, is de volgende stap om pixels op het scherm te tekenen. De klasse Visual voorziet in het bouwen van een boomstructuur met visuele objecten, die optioneel tekeninstructies en metagegevens bevatten over het weergeven van deze instructies (knipsel, transformatie, enzovoort). Visual is ontworpen om uiterst lichtgewicht en flexibel te zijn, zodat de meeste functies geen openbare API-blootstelling hebben en sterk afhankelijk zijn van beveiligde callback-functies.

Visual is echt het ingangspunt voor het WPF-compositiesysteem. Visual is het punt van verbinding tussen deze twee subsystemen, de beheerde API en de onbeheerde milcore.

WPF geeft gegevens weer door de onbeheerde datatypestructuren te doorlopen die worden beheerd door de milcore. Deze structuren, samenstellingsknooppunten genoemd, vertegenwoordigen een hiërarchische weergavestructuur met renderinginstructies op elk knooppunt. Deze structuur, geïllustreerd aan de rechterkant van de onderstaande afbeelding, is alleen toegankelijk via een berichtenprotocol.

Bij het programmeren van WPF maakt u Visual elementen en afgeleide typen, die intern communiceren met de samenstellingsstructuur via dit berichtenprotocol. Elke Visual in WPF kan één, geen of meerdere samenstellingsknooppunten maken.

de visualstructuur van de Windows Presentation Foundation.

Er is hier een zeer belangrijk architectuurdetail te zien: de hele structuur van visuals en tekeninstructies wordt in de cache opgeslagen. In grafische termen maakt WPF gebruik van een behouden renderingsysteem. Hierdoor kan het systeem met hoge vernieuwingssnelheden opnieuw worden geschilderd zonder dat het samenstellingssysteem callbacks naar gebruikerscode blokkeert. Dit helpt voorkomen dat een toepassing niet reageert.

Een ander belangrijk detail dat niet echt merkbaar is in het diagram, is hoe het systeem daadwerkelijk compositie uitvoert.

In User32 en GDI werkt het systeem op een immediate mode afsnijdsysteem. Wanneer een onderdeel moet worden weergegeven, stelt het systeem een knipgrens vast waaraan het onderdeel de pixels niet mag raken en wordt het onderdeel gevraagd pixels in dat vak te schilderen. Dit systeem werkt zeer goed in systemen met geheugenbeperkingen, omdat wanneer iets verandert u alleen het betreffende onderdeel hoeft aan te raken. Er dragen nooit twee onderdelen bij aan de kleur van één pixel.

WPF maakt gebruik van een schildersalgoritmenmodel. Dit betekent dat in plaats van elk onderdeel te knippen, elk onderdeel wordt gevraagd om van de achterkant naar de voorzijde van de weergave weer te geven. Hierdoor kan elk onderdeel tekenen over de weergave van het vorige onderdeel. Het voordeel van dit model is dat u complexe, gedeeltelijk transparante vormen kunt hebben. Met moderne grafische hardware van vandaag is dit model relatief snel (wat niet het geval was toen User32/GDI werd gemaakt).

Zoals eerder vermeld, is een kern filosofie van WPF om over te stappen op een meer declaratief, 'eigenschapgericht' model van programmeren. In het visuele systeem wordt dit weergegeven op een aantal interessante plaatsen.

Eerst, als je denkt aan het retained mode-grafische systeem, is dit echt aan het verschuiven van een imperatief model zoals DrawLine/DrawLine naar een gegevensgeoriënteerd model – nieuwe Lijn()/nieuwe Lijn(). Door deze verplaatsing naar gegevensgestuurde rendering kunnen complexe bewerkingen op de tekeninstructies worden uitgedrukt met behulp van eigenschappen. De typen die zijn afgeleid van Drawing zijn effectief het objectmodel voor rendering.

Ten tweede, als u het animatiesysteem evalueert, ziet u dat het bijna volledig declaratief is. In plaats van dat een ontwikkelaar de volgende locatie of volgende kleur moet berekenen, kunt u animaties uitdrukken als een set eigenschappen op een animatieobject. Deze animaties kunnen vervolgens de intentie van de ontwikkelaar of ontwerper uitdrukken (verplaats deze knop van hier naar daar in 5 seconden) en het systeem kan de meest efficiënte manier bepalen om dat te bereiken.

System.Windows.UIElement

UIElement definieert kernsubsystemen, waaronder Indeling, Invoer en Gebeurtenissen.

Indeling is een kernconcept in WPF. In veel systemen is er een vaste set indelingsmodellen (HTML ondersteunt drie modellen voor indeling; stroom, absoluut en tabellen) of geen model voor indeling (Gebruiker32 ondersteunt echt alleen absolute positionering). WPF begon met de veronderstelling dat ontwikkelaars en ontwerpers een flexibel, uitbreidbaar indelingsmodel wilden, dat kan worden aangestuurd door eigenschapswaarden in plaats van imperatieve logica. Op het UIElement-niveau wordt het basiscontract voor indeling geïntroduceerd - een tweefasenmodel met Measure en Arrange stappen.

Measure stelt een onderdeel in staat om te bepalen hoeveel grootte het wil aannemen. Dit is een afzonderlijke fase van Arrange omdat er veel situaties zijn waarin een bovenelement een kind herhaaldelijk vraagt om te meten om de optimale positie en grootte te bepalen. Het feit dat bovenliggende elementen onderliggende elementen vragen om te meten, demonstreert een andere belangrijke filosofie van WPF: afmetingen naar inhoud. Alle besturingselementen in WPF ondersteunen de mogelijkheid om zich aan te passen aan de natuurlijke grootte van hun inhoud. Dit maakt lokalisatie veel eenvoudiger en maakt dynamische indeling van elementen mogelijk wanneer de grootte wordt aangepast. In de Arrange-fase kan een ouder de positie en de uiteindelijke grootte van elk kind bepalen.

Er wordt vaak veel tijd besteed aan het praten over de uitvoerzijde van WPF - Visual en gerelateerde objecten. Er is echter ook een enorme hoeveelheid innovatie aan de inputzijde. Waarschijnlijk de meest fundamentele wijziging in het invoermodel voor WPF is het consistente model waarmee invoergebeurtenissen worden gerouteerd via het systeem.

Invoer komt voort uit een signaal op een apparaatstuurprogramma in de kernelmodus en wordt doorgestuurd naar het juiste proces en wordt doorgeleid via een ingewikkeld proces met betrekking tot de Windows-kernel en Gebruiker32. Zodra het User32-bericht dat overeenkomt met de invoer wordt doorgestuurd naar WPF, wordt het geconverteerd naar een onbewerkt WPF-invoerbericht en verzonden naar de dispatcher. MET WPF kunnen onbewerkte invoergebeurtenissen worden geconverteerd naar meerdere werkelijke gebeurtenissen, waardoor functies zoals 'MouseEnter' op een laag niveau van het systeem kunnen worden geïmplementeerd met gegarandeerde levering.

Elke invoergebeurtenis wordt geconverteerd naar ten minste twee gebeurtenissen: een preview-gebeurtenis en de werkelijke gebeurtenis. Alle gebeurtenissen in WPF hebben een concept van routering door de elementenboom. Gebeurtenissen worden "opborrelen" genoemd als ze van een doel omhoog door de boomstructuur naar de wortel gaan en worden "tunnelen" genoemd als ze bij de wortel beginnen en naar een doel omlaag gaan. Invoer van een preview-gebeurtenistunnel, waardoor elk element in de structuur een mogelijkheid heeft om de gebeurtenis te filteren of actie te ondernemen. De reguliere (niet-preview) gebeurtenissen propageren vervolgens van het doel naar de wortel.

Deze splitsing tussen de tunnel- en bellenfase zorgt ervoor dat de implementatie van functies zoals toetsenbordversnellers op een consistente manier werkt in een samengestelde wereld. In Gebruiker32 zou u toetsenbordversnellers implementeren door één globale tabel te hebben met alle accelerators die u wilt ondersteunen (Ctrl+N-toewijzing aan 'Nieuw'). In de dispatcher voor uw applicatie zou u TranslateAccelerator aanroepen, die de invoerberichten in User32 zou controleren en bepalen of er een geregistreerde accelerator overeenkomt. In WPF zou dit niet werken omdat het systeem volledig 'samenstelbaar' is: elk element kan elke toetsenbordversneller verwerken en gebruiken. Als u dit model met twee fasen voor invoer gebruikt, kunnen onderdelen hun eigen 'TranslateAccelerator' implementeren.

Om deze stap verder te zetten, introduceert UIElement ook het begrip CommandBindings. Met het WPF-opdrachtsysteem kunnen ontwikkelaars functionaliteit definiëren in termen van een opdrachteindpunt, iets dat ICommandimplementeert. Met opdrachtbindingen kan een element een toewijzing definiëren tussen een invoergebaren (Ctrl+N) en een opdracht (Nieuw). Zowel de invoerbewegingen als de opdrachtdefinities zijn uitbreidbaar en kunnen tijdens het gebruik samen worden aangesloten. Hierdoor kan een eindgebruiker bijvoorbeeld de sleutelbindingen aanpassen die ze in een toepassing willen gebruiken.

Tot op dit punt in het onderwerp hebben de kernfuncties van WPF – functies die zijn geïmplementeerd in de PresentationCore-assembly – centraal gestaan. Bij het bouwen van WPF was een schone scheiding tussen basisstukken (zoals het contract voor indeling met Measure en Arrange) en frameworkstukken (zoals de implementatie van een specifieke indeling zoals Grid) het gewenste resultaat. Het doel was om een uitbreidbaarheidspunt laag in de stack te bieden waarmee externe ontwikkelaars indien nodig hun eigen frameworks kunnen maken.

System.Windows.FrameworkElement

FrameworkElement kunnen op twee verschillende manieren worden bekeken. Het introduceert een set beleidsregels en aanpassingen op de subsystemen die zijn geïntroduceerd in lagere lagen van WPF. Er wordt ook een set nieuwe subsystemen geïntroduceerd.

Het primaire beleid dat door FrameworkElement is geïntroduceerd, is rond de indeling van de toepassing. FrameworkElement bouwt voort op het basisindelingscontract dat door UIElement is geïntroduceerd en voegt het idee toe van een indelingssite waarmee auteurs van indelingen gemakkelijker een consistente set semantiek voor eigenschapsgestuurde lay-outs kunnen hebben. Eigenschappen zoals HorizontalAlignment, VerticalAlignment, MinWidthen Margin (om er een paar te noemen) geven alle onderdelen die zijn afgeleid van FrameworkElement consistent gedrag binnen indelingscontainers.

FrameworkElement biedt ook eenvoudigere API-blootstelling aan veel functies in de kernlagen van WPF. FrameworkElement biedt bijvoorbeeld directe toegang tot animatie via de methode BeginStoryboard. Een Storyboard biedt een manier om meerdere animaties te scripten op basis van een set eigenschappen.

De twee belangrijkste dingen die FrameworkElement introduceert, zijn gegevensbinding en -stijlen.

Het subsysteem voor gegevensbinding in WPF moet relatief bekend zijn met iedereen die Windows Forms of ASP.NET heeft gebruikt voor het maken van een gebruikersinterface (UI). In elk van deze systemen is er een eenvoudige manier om uit te drukken dat u wilt dat een of meer eigenschappen van een bepaald element worden gebonden aan een stukje gegevens. WPF biedt volledige ondersteuning voor eigenschapsbinding, transformatie en lijstbinding.

Een van de meest interessante functies van gegevensbinding in WPF is de introductie van gegevenssjablonen. Met gegevenssjablonen kunt u declaratief opgeven hoe een stukje gegevens moeten worden gevisualiseerd. In plaats van een aangepaste gebruikersinterface te maken die kan worden gebonden aan gegevens, kunt u in plaats daarvan het probleem omdraaien en de gegevens laten bepalen welke weergave wordt gemaakt.

Styling is eigenlijk een lichtgewicht vorm van gegevensbinding. Met stijl kunt u een set eigenschappen van een gedeelde definitie binden aan een of meer exemplaren van een element. Stijlen worden toegepast op een element door expliciete verwijzing (door de eigenschap Style in te stellen) of impliciet door een stijl te koppelen aan het CLR-type van het element.

System.Windows.Controls.Control

De belangrijkste functie van Control is sjabloneren. Als je het samenstellingssysteem van WPF beschouwt als een weergavesysteem voor de bewaarde modus, dan maakt templating het mogelijk voor een besturingselement om zijn weergave op een geparameteriseerde, declaratieve manier te beschrijven. Een ControlTemplate is eigenlijk niets meer dan een script om een set onderliggende elementen te maken, met bindingen naar eigenschappen die door het besturingselement worden aangeboden.

Control biedt een set standaardeigenschappen, zoals Foreground, Background, Padding, die sjabloonauteurs vervolgens kunnen gebruiken om de weergave van een component aan te passen. De implementatie van een besturingselement biedt een gegevensmodel en interactiemodel. Het interactiemodel definieert een set opdrachten (zoals Sluiten voor een venster) en bindingen voor invoerbewegingen (zoals klikken op de rode X in de bovenhoek van het venster). Het gegevensmodel biedt een set eigenschappen om het interactiemodel aan te passen of de weergave aan te passen (bepaald door de sjabloon).

Deze splitsing tussen het gegevensmodel (eigenschappen), interactiemodel (opdrachten en gebeurtenissen) en weergavemodel (sjablonen) maakt het mogelijk om het uiterlijk en gedrag van een besturingselement volledig aan te passen.

Een veelvoorkomend aspect van het gegevensmodel van besturingselementen is het inhoudsmodel. Als u een besturingselement bekijkt zoals Button, ziet u dat het een eigenschap heeft met de naam 'Inhoud' van het type Object. In Windows Forms en ASP.NET is deze eigenschap meestal een tekenreeks. Dit beperkt echter het type inhoud dat u in een knop kunt plaatsen. Inhoud voor een knop kan een eenvoudige tekenreeks, een complex gegevensobject of een hele elementstructuur zijn. In het geval van een gegevensobject wordt de gegevenssjabloon gebruikt om een weergave te maken.

Samenvatting

WPF is ontworpen om u in staat te stellen dynamische, gegevensgestuurde presentatiesystemen te maken. Elk onderdeel van het systeem is ontworpen om objecten te maken via eigenschappensets die het gedrag bepalen. Gegevensbinding is een fundamenteel onderdeel van het systeem en is geïntegreerd in elke laag.

Traditionele toepassingen maken een weergave en binden vervolgens aan bepaalde gegevens. In WPF wordt alles over het besturingselement, elk aspect van de weergave, gegenereerd door een bepaald type gegevensbinding. De tekst in een knop wordt weergegeven door een samengesteld besturingselement in de knop te maken en de weergave ervan te koppelen aan de inhoudseigenschap van de knop.

Wanneer u aan de slag gaat met het ontwikkelen van WPF-toepassingen, moet het zich vertrouwd voelen. U kunt eigenschappen instellen, objecten en gegevens binden op ongeveer dezelfde manier als u Windows Forms of ASP.NET kunt gebruiken. Met een dieper onderzoek naar de architectuur van WPF zult u merken dat de mogelijkheid bestaat voor het maken van veel rijkere toepassingen die gegevens fundamenteel behandelen als het kernstuurprogramma van de toepassing.

Zie ook