Személyes eszközök
Keresés

 

A InfoWiki wikiből


Tartalomjegyzék

XAML

(ez csak vázlat, ide mentem, hogy itt is meglegyen - hz)

XAML (ejtsd zammel) az Extensible Application Markup Language rövidítése.

A XAML egy XML file. Egyetlen célja van: hogy .NET osztályok példányosítását végezze el. Mivel ez nem kód (nem C#), így a példányosítást deklaratív módon végezzük:

  • elkészítünk egy XML bejegyzést, azonosítva melyik osztály (class) példányát kívánjuk elkészíteni.
  • opcionálisan nevet is adunk ennek a példánynak, hogy a kódból ezen változónévvel (mezőnévvel) lehessen rá hivatkozni
  • leírjuk melyik property-jének mi legyen a kezdőértéke (ez utóbbi nem triviális, mivel a property tetszőleges adattípus lehet, értéke ezért nem feltétlenül írható le egy string-el)

Persze a példányosítást tipikusan kódból szoktuk végezni. Ezért a WPF nem igényli kötelezően a XAML használatát. Minden elvégezhető kódból is, ahogy az korábban megszoktuk. A XAML használatának azonban az az előnye, hogy a XAML készítését a designer is el tudja végezni, hiszen kódolást neki nem kell végeznie, a xaml deklaratív. Így a design a kódkészítéssel párhuzamosan végezhető tevékenységgé vált.

A XAML-nak valójában több változata is van:

  • WPF XAML: direkt WPF jellegű alkalmazások xml nyelve, tipikusan WPF-ben használt objektumok példányosítását és beállítását írjuk le benne.
  • XPS XAML: (XML Paper Specification), az .xps dokumentumok által használt nyelv, elsősorban dokumentumokban használt
  • WPF/E XAML: a SilverLight alkalmazások által használt. Tipikusan a WPF XAML-jének egy részhalmaza, csak bizonyos objektumok példányosítása megengedett, mivel a Silverlight alkalmazások böngészőben futtatandók.
  • WF XAML: Workflow Foundation alkalmazások által használt xml.

Az XAML file-okat .xaml néven mentjük, és mivel ezek gyakorlatilag xml file-ok, ezért akár notepaddal is szerkeszthetőek. Gyakorlatilag érdemesebb azonban syntax highlight, és code completition eszközökkel ellátott xml szerkesztőt használni.

Az XAML file-ok a project fordításakor feldolgozásra kerülnek, és alapos szintaktikai ellenőrzés után bináris állománnyá (BAML - Binary XAML) formátumra fordulnak le. Ezen formátum belső szerkezete gyakorlatilag megegyezik az XAML belső szerkezetével, de ezen bináris formája az olvasást és feldolgozást segíti és gyorsítja. Ezen BAML file a DLL-be beszúródik erőforrás (resource) alakban, és futás közben kerül kiértékelésre (és végrehajtásra).

Ez sem kötelező elem. A WPF alkalmazás design-je maradhat eredeti szöveges XAML alakban is, külső file formájában csatolva a futtatható .exe mellé. Ezen XAML file-t ekkor kódból kell betölteni, feldolgozni, és kiértékelni.

XAML alapok

A XAML-nek egyetlen célja van: .NET osztályok példányosítását leírni. Ezért szerkezete erősen kötött, de a programozók (akik számára a példányosítás megszokott lépés, és általában ismerik az objektumokosztályok neveit, és a property-k neveit) számára gyorsan megérthető és megszokható.

Az osztályok nevét (amiből a példányt készíteni szeretnénk) nem csak egyszerűen le kell írni. A XAML-ben az osztályt a névtérrel kell azonosítani. Ez hasonló, mint a using szerepe a C# kódban. Azonban az xml file-okban ez úgy van, hogy nem végtelen sok using'-unk van, s amíg nincs névütközés, addig minden rendben. Egyetlen névterünk lehet az alapértelmezett. A további névtereinknek alias nevet kell adni, és minden esetben ezen alias neveket ki kell írni.

Egy átlagos XAML file-ban két két névtér szokott lenni:

  • az első névtér magát a .NET osztályhierarchiát képviseli, azon belül is a WPF által használt osztályok elérhetőségét. Ez szokott az alapértelmezett névtér lenni:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  • a második névtér magát a XAML sémára hivatkozik. Ennek a tipikus alias neve az x:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

A névterek megadásakor leírt érték úgy néz ki mintha az egy weblap címe lenne. Nem az. Ez egy URI (Uniform Resource Identifier), vagyis egy szabványos leírása egy egyedi azonosítónak. A schemas.microsoft.com egy olyan előtagja ennek a tagolt azonosítónak, ami a Microsoft-ra utal. Ezen azonosító értelmét és jelentését a XML feldolgozó érti. Ezen névtér megadása egyben sok C#-beli using-ot helyettesít az XAML file-ban. Ezért a fenti alapértelmezett névtér egyben magában foglalja, helyettesíti gyakorlatilag az összes C#-ban használt WPF-hez tartozó névteret.

Példa:

<Window x:Class="WindowsApplication1.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 Title="WindowsApplication1" Height="300" Width="300">
   <Grid>
   </Grid>
</Window>

A példa magyarázata:

  • definiáljuk a két névteret
  • a Window objektumosztály egy leszármazott (gyerek) osztályát készítjük el
  • a származtatott típus a WindowsApplication1 névtérbe kerül, Window1 névvel lesz
  • ebből fog a XAML futás közben példányosítani
  • a Class kulcsszó a XAML névtér által kezelt kulcsszó, ezért x: előtaggal rendelkezik
namespace WindowsApplication1
{
  partial class Window1:Window
  {
  }
}


  • a továbbiakban beállítjuk a példány Title property-jének értékét WindowsApplication1-re
  • a Height property értékét 300 WPF Device Independent Unit-ra
  • a Width értékét is 300-ra
  • ezen példány Content-jét (tartalom) feltöltjük egy Grid osztálybeli példánnyal


Egy XAML file gyökéreleme az alábbi 3 elem egyike lehet:

  • Window - form jellegű alkalmazás esetén egy ablak leírása
  • Page - page alapú működés esetén a page leírása
  • Application - alkalmazás leírása (tipikusan erőforrást tartalmaz)

Ha a gyökérelem egy window, akkor akár Main() függvényre sincs szükség az alkalmazáson belül. A window példány automatikusan létrejön, átadásra kerül az Application.Run()-nak - a program elindul.

A szokásos névteren túl a XAML file tetszőleges további névteret is tartalmazhat. Ez esetben az alábbi mintát lehet használni:

xmlns:Prefix="clr-namespace:Namespace;assembly=AssemblyName"
  • Prefix: ide kell egy egyszerű azonosító, ami nem az "x:"
  • Namespace: ide kell behelyettesíteni a saját névterünket
  • AssemblyName: opcionális, a DLL neve

Példa:

xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:MyNamespace"

x:Name vs Name

Korábban, a hagyományos Windows alkalmazásokban minden elemnek, ami a felületen megjelent - nevet kellett adni. Még akkor is, ha az a név csak annyi volt, hogy 'label12'.

WPF-ben nem szokás minden elemnek nevet adni, csak amelyekre a kódból ténylegesen hivatkozni kívánunk. A névadási szabályokra természetesen a szokásos azonosító elnevezési konvenciók vonatkoznak. A nevet az "x:Name" attribútumon keresztül lehet/kell megadni:

<Window x:Class="WindowsApplication1.Window1" ...>
  <Grid x:Name="grid1">
  </Grid>
 </Window>

Ez megfelel az alábbi kódnak:

private System.Windows.Controls.Grid grid1;

Sok .NET osztályban van Name property, amely a példány azonosítóját tartalmazza (ilyen pl. a FrameworkElement osztály is). Ez esetben elvileg az alábbi szintaxist kellene használni:

<Grid x:Name="grid1" Name="grid1"> ...  </Grid>

Ennek elkerülése végett a XAML fordítók intelligens módon bármelyiket elfogadják külön-külön is elegendőnek:

<Grid x:Name="grid1"> ...  </Grid>
<Button Name="OK_Button"> ... </Button>

Mindkét forma esetén felismerik hogy a példánynak nevet kívánunk adni, és ezen nevet el kell helyezni a Name property-ben mint érték. Ez a látszat, a valóságban azt használja ki a XAML fordító, hogy az objektumosztálnyak van RuntimeNameProperty attribútuma, és ezen keresztül működteti a névbeállítást.

Property értékének beállítása

A XAML file-ok egyetlen célja, hogy .NET osztályok példányosítását és beállítását írja le deklaratív módon. Ehhez azonosítani kell a .NET osztályt, opcionálisan megadni egy példánynevet (ha a kódoló ezt igényli), majd értékadó utasítássorozat formájában megadni a példány attribútumainak kezdőbeállítását.

String típusú property

Mindaddig, amíg a példány beállítandó atribútuma string típusú, a kezdőérték-megadás problémamentes, hiszen a XML file-ok attribútumjai is string-ek:

<Window ... Title="WindowsApplication1" ... />

Egyszerű típusú property

Bonyolultabb a helyzet, amikor a property típusa nem string:

<Window ... Width="300"... />

Ez azért működik jól, mert az értékekhez ún. típus-konverterek vannak megadva. A XAML fordító felismeri, hogy a Width property double típusú, így ehhez ezt a típuskonvertert kell használni. A fenti XAML részt az alábbi kód helyettesíthetné:

this.Width = Double.Parse(300);

Jegyezzük meg, hogy a XAML fordító nem C# kódot generál a XAML-ból, mindössze bináris formává alakítja át, és futás közben értékeli ki. Ennek megfelelően futás közben kell a bár bináris, de az attribútumértékeket továbbra is stringek formájában tároló BAML-ből a 300 értéket átrakni a Width property-be.

Hasonló típuskonverter van használva a felsorolás (enum) eseténis:

<... HorizontalAlignment="Stretch" ... />

A HorizontalAlignment property típusa egy enumeration, a HorizontalAlignment enum. Ebben van Stretch nevű tag, melyre hivatkozunk jelen példában. A helyettesítő kód az alábbi lehetne:

this.HorizontalAlignment = (HorizontalAlignment)Enum.Parse(typeof(HorizontalAlignment), "Stretch", true);

A típuskonverterek általában kisbetű-nagybetű érzékenyek!

Összetett típusú property

Amennyiben egy property értéke nem leírható egyetlen string-el, úgy a property értékadását ki kell bontani. Ez meghetető egyébként akkor is, ha a property értéke amúgy leírható lenne az előző egyszerű módon is. A kibontás során beágyazott XML bejegyzést kell készíteni, amelynek nevében utalnia kell a kibontandó property-re:


<Button Content="Klikk engem" .../>

... kibontva ...

<Button ...>
  <Button.Content>Klikk engem</Button.Content>
</Button>

Amennyiben egy XAML tag beágyazott tartalommal bír, melyet nem azonosítottunk mint kibontott property, úgy az az adott tag Content-kének a kibontását jelenti. Ezért a fenti példa így is írható:

<Button ...>Klikk engem</Button>

Ez esetben a Content típusa egy, a megadott string lesz.

Bonyolultabb eset, amikor a property, akinek az értékét be akarjuk állítani - egy objektumpéldányt vár értékül. Ez esetben a kibontás elkerülhetetlen. Ilyen eset például a háttér beállítása:

<Button ...>
   <Button.Background>
      <LinearGradientBrush> ... </LinearGradientBrush>
   </ButtonBackground>
</Button>

Kollekció-jellegű property

Ha megvizsgáljuk akár ezen LinearGradientBrush példányosításának és beállításának menetét, észrevehetjük, hogy van egy GradientStops property-je, amely lista-jellegűen tartalmaz további elemeket. Ezen property értékét az alábbi módon tudjuk kibontani:

<LinearGradientBrush>
    <LinearGradientBrush.GradientStops>
      <GradientStop Offset="0.00" Color="Red" />
      <GradientStop Offset="0.50" Color="Indigo" />
      <GradientStop Offset="1.00" Color="Violet" />
    </LinearGradientBrush.GradientStops>
</LinearGradientBrush>

A listába három elemet helyezünk el, mindegyik GradientStop típusú, és a példányok beállítása is leolvasható. Ez az alábbi C# kóddal lenne leírható:

LinearGradientBrush brush = new LinearGradientBrush();
 
GradientStop gradientStop1 = new GradientStop();
gradientStop1.Offset = 0;
gradientStop1.Color = Colors.Red;
brush.GradientStops.Add(gradientStop1);
 
GradientStop gradientStop2 = new GradientStop();
gradientStop2.Offset = 0.5;
gradientStop2.Color = Colors.Indigo;
brush.GradientStops.Add(gradientStop2);
 
GradientStop gradientStop3 = new GradientStop();
gradientStop3.Offset = 1;
gradientStop3.Color = Colors.Violet;
brush.GradientStops.Add(gradientStop3);

Kiterjesztések

A bonyolultabb esetek leírása kevéssé triviális. Például hogyan tudjuk leírni azt XAML-ben, hogy egymás után van 3 db Button, az elsőnél definiáltam egy Bacground-t, ami egy LinearGradiendtBrush példány lett, a másik két gomb esetén nem szeretném újra leírni az adatokat, hanem ugyanazt a LinearGradiendtBrush-t szeretném használni? Kódban:

LinearGradientBrush b = new LinearGradientBrush();
...
Button button1 = new Button();
button1.Background = b;
Button button2 = new Button();
button1.Background = b;
Button button3 = new Button();
button1.Background = b;

A nem triviális esetek leírását markup extension-k támogatják. A markup extension-okat onnan lehet felismerni, hogy értékadáskor a string tartalma kapcsol zárójeleket tartalmaz:

<Button ... Foreground="{x:Static SystemColors.ActiveCaptionBrush}" >

Ezeket a kiterjesztéseket gyakran használjuk erőforrások elérésénél, vagy adatkötésnél, és a megfelelő fejezetekben lesznek tárgyalva.

Speciális karakterek

Az XML nyelvben speicális jelentéssel bíró karakterek az alábbiak:

  • < (kisebb) => &lt;
  • > (nagyobb) => &gt;
  • & (et-jel) => &amp;
  • " (idezojel) => &quot;


Ezek miatt nem string-ek belsejében ezeket a karaktereket leírni helyettesítő karakterekkel kell. Helytelen az alábbi példa:

<Button ...> klikkelj >>ide<< rám</Button>

Helyes az alábbi:

<Button ...> klikkelj &gt;&gt;ide&lt;&lt; rám</Button>

Attached Property

Van olyan eset, amikor egy példány property-jét nem magában a példányban, hanem az abba ágyazott (gyerek) példányban állítjuk be. A szülő valamely property-jére vissza lehet utalni:

<Grid>
 <TextBox x:Name="txtQuestion" ... Grid.Row="0">
   A kérdés: lenni vagy nem lenni?!
 </TextBox>
</Grid>

Ez a visszautalás valójában nem property-re irányul, sokkal inkább függvényhívás lesz belőle. A fordító feltételezi, hogy ekkor van egy "Set...()" függvény a szülő osztályban (ez egy osztály-szintű függvényt feltételez!), melynek átadja a beállító példányt, és a beállítandó értéket:

Grid.SetRow(txtQuestion, 0);

A fenti függvény természetesen nem kommunikál magával a tartalmazó (szülő) Grid példánnyal, mivel azt nem ismerheti (a SetRow() osztályszintű!). Helyette a beállító példány (txtQuestion)-ban fogja beállítani a megfelelő értéket oly módon, hogy létrehoz benne egy DependecyProperty-t, és elhelyezi benne az értéket:

txtQuestion.SetValue(Grid.RowProperty, 0);

Kollekció kezelés

Amikor egy példány valamely attribútumába több más példányt helyezünk el, akkor a XAML értelmező azt kollekció-bővítés műveletekkel helyettesíti:

<LinearGradientBrush.GradientStops>
  <GradientStop Offset="0.00" Color="Red" />
  <GradientStop Offset="0.50" Color="Indigo" />
  <GradientStop Offset="1.00" Color="Violet" />
</LinearGradientBrush.GradientStops>

Amennyiben a kiválasztott property

  • implementálta az IList interface-t, akkor .Add(...) műveleteket fog használni
  • implementálta IDictionary, interface-t, akkor .Add(x:Key, ...) műveleteket fog használni
  • ContentProperty-je van a parent-nek, akkor azt használja fel
Hernyák Zoltán
A lap eredeti címe: „http://wiki.ektf.hu/wiki/Wpf:page30
Nézetek
nincs sb_54.144.81.21 cikk