XAML.cz Magazín moderních technologií založených na XAML

Silverlight 4–použití DataAnnotations

Napsáno pro Silverlight od Lukáš Kubis [14.12.2010]
Úvod

Namespace System.ComponentModel.DataAnnotations nabízí celou řadu atributů, které můžeme v naší aplikaci využít. V následující ukázce si ukážeme použití několika atributů v různých případech.

V ukázkové aplikaci budeme pracovat se třídou Person

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public SexType SexType { get; set; }
    public string Pin { get; set; }
}

public enum SexType
{
    Male, Female
}

No a když si vytvoříme kolekci těchto osob

public class People : ObservableCollection<Person>
{
    public People()
    {
        this.Add(new Person() { FirstName = "Lukas", LastName = "Kubis", SexType = SexType.Male, Pin = "123456"});
        this.Add(new Person() { FirstName = "Jan", LastName = "Novak", SexType = SexType.Male, Pin = "123456" });
        this.Add(new Person() { FirstName = "Andrejka", LastName = "Bogey", SexType = SexType.Female, Pin = "123456" });
    }
}

a zobrazíme v Gridu (AutoGenerateColumns = True), dostaneme následující výsledek

image

Data se zobrazují správně, ale co když budume chtít mít názvy sloupců česky a poslední sloupec Pin skrýt ?

Samozřejmě můžeme nastavit AutoGenerateColumns na False a definovat sloupečky tak jak chci s příslušným Header textem, nebo přepsat událost, ve které se vytvářejí sloupečky a zde nastavit Header text a skrýt nepotřebný sloupec Pin.

Obě varianty budou fungovat, ale my si ukážeme aspoň podle mě lepší způsob jak toho docílit.

DisplayAttribute

Pro změnu zobrazení názvu sloupce využijeme DisplayAttribute, kde do vlastnosti Name napíšeme náš vlastní text. Každou vlastnost třídy Person, u které budeme chtít změnit zobrazovaný text označíme tímto atributem. Pro skrytí sloupce slouží vlastnost AutoGenerateField, kterou nastavíme na false

public class Person
{
    [DisplayAttribute(Name = "Jméno")]
    public string FirstName { get; set; }

    [DisplayAttribute(Name = "Příjmení")]
    public string LastName { get; set; }

    [DisplayAttribute(Name = "Pohlaví")]
    public SexType SexType { get; set; }

    [DisplayAttribute(AutoGenerateField = false)]
    public string Pin { get; set; }
}

Po těchto jednoduchých úpravách dostaneme hned lepší výsledek

image

V případě, že vyvíjíme lokalizované aplikace, nemusíme řetězec zapisovat přímo do vlastnosti Name, ale můžeme využít ResourceType a odkázat na příslušné Resources, ve kterých budou řetězce přeloženy do jednotlivých jazyků.

Dále se zde zmíním o vlastnosti Order, která určuje v jakém pořadí se sloupce vygenerují. Nastavíme-li jménu Order = 2 a příjmení Order = 1, budeme mít v Gridu zobrazeno nejdříve Příjmení a až potom Jméno. Poslední vlastnost Description, kterou chci představit si necháme na později.

Validace

Další ukázka použití atributů se bude zabývat validací. Vytvoříme si formulář, kde bude uživatel vyplňovat informace o osobě a při špatném zadání se mu zobrazí informace o chybě.

Opět provedeme úpravu třídy Person, kde nyní nebudeme využívat automatické vlastnosti

public class Person
{
    private string _firstName;
    private string _lastName;
    private SexType _sexType;

    [Display(Name = "Jméno", Order = 1)]
    public string FirstName
    {
        get { return this._firstName; }
        set
        {            
            this._firstName = value;
        }
    }

    [Display(Name = "Příjmení", Order = 2)]
    public string LastName
    {
        get { return this._lastName; }
        set
        {
            this._lastName = value;
        }
    }

    [Display(Name = "Pohlaví")]
    public SexType SexType
    {
        get { return this._sexType; }
        set
        {
            this._sexType = value;
        }
    }

    [Display(AutoGenerateField = false)]
    public string Pin { get; set; }
}

A náš jednoduchý formulář vypadá následovně (obsah XAMLu vysvětlím později)

<Grid x:Name="LayoutRoot" Margin="2">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition Width="300"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="35"/>
        <RowDefinition Height="35"/>
        <RowDefinition Height="35"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <!-- FirstName -->
    <sdk:Label Content="Jméno" IsRequired="True" Margin="5" />
    <StackPanel Orientation="Horizontal" Grid.Column="1">
        <TextBox x:Name="ui_txtFirstName" Width="200"
                    Text="{Binding FirstName, Mode=TwoWay, 
            ValidatesOnExceptions=True, NotifyOnValidationError=True}"/>
        <sdk:DescriptionViewer Target="{Binding ElementName=ui_txtFirstName}" />
    </StackPanel>

    <!-- FirstName -->
    <sdk:Label Grid.Row="1" Content="Příjmení" IsRequired="True" Margin="5" />
    <StackPanel Grid.Row="1" Orientation="Horizontal" Grid.Column="1">
        <TextBox x:Name="ui_txtLastName" Width="200"
                    Text="{Binding LastName, Mode=TwoWay, 
            ValidatesOnExceptions=True, NotifyOnValidationError=True}"/>
        <sdk:DescriptionViewer Target="{Binding ElementName=ui_txtLastName}" />
    </StackPanel>

    <!-- SexType -->
    <sdk:Label Grid.Row="2" Content="Pohlaví" IsRequired="True" Margin="5" />
    <StackPanel Grid.Row="2" Orientation="Horizontal" Grid.Column="1">
        <TextBox x:Name="ui_txtSexType" Width="200"
                    Text="{Binding SexType, Mode=TwoWay, 
            ValidatesOnExceptions=True, NotifyOnValidationError=True}"/>
        <sdk:DescriptionViewer Target="{Binding ElementName=ui_txtSexType}" />
    </StackPanel>
        
    <!-- ValidationSummary-->
    <sdk:ValidationSummary Grid.Row="3" Grid.ColumnSpan="2"/>
</Grid>

Podíváme se tedy na několik atributů, které nám mohou být nápomocné při provádění validace.

Poznámka: Každý validační atribut má vlastnost ErrorMessage, ve které uvedeme chybovou hlášku a nebo můžeme opět využít Resources.

Opět se pustíme do úpravy třídy Person a přidáme atributy pro validaci

[Required(ErrorMessage = "Jméno je povinné")]
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage =
     "Čísla a speciální znaky nejsou ve jméně povoleny.")]
public string FirstName

[Required(ErrorMessage = "Příjmení je povinné")]
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage =
     "Čísla a speciální znaky nejsou v příjmení povoleny.")]
public string LastName

[EnumDataType(typeof(SexType))]
public SexType SexType

Když ovšem spustíme aplikaci a v editačním formuláři nevyplníme třeba jméno tak se nic nestane. Je potřeba provést ještě několik kroků. Prvním důležitým krokem je upravit jednotlivé settery našich vlastností. Zde použijeme třídu Validator a pouze zavoláme metodu ValidateProperty, která provede validaci vůči našim atributům.

public string FirstName
{
    get { return this._firstName; }
    set
    {
        Validator.ValidateProperty(value, new ValidationContext(
            this, null, null) { MemberName = "FirstName" });
        this._firstName = value;
    }
}

Validaci vlastnosti provádíme ještě před přiřazením, protože když validace selže, dojde k vyvolání výjimky a je třeba nastavit v XAMLu ValidatesOnExceptions=True a  NotifyOnValidationError=True

Nyní už po spuštění aplikace a zadání špatných údajů dostaneme informace o chybách.

image

Ještě než ukočíme povídání o DataAnnotations, tak bych se rád vrátil k XAMLu a některým pomocným ovládacím prvkům a vlastnosti Description Display atributu.

Na obrázku výše vidíte nejníže souhrn všech validačních chyb. O zobrazení se stará ValidationSummary, který pouze stačí přidat do UI a není potřeba nic nastavovat. Dále si můžete všimnout takového malého kolečka s “i”, zde se jedná o ovládací prvek DescriptionViewer. Pomocí vlastnosti Target svážeme ovládací prvek s textboxem (např. pro zadání příjmení) a jelikož je textbox napojen na vlastnost LastName, která má nastaven Description tak po najetí myši přes tuto ikonku dojde k zobrazení příslušného textu.

image

Materiály ke stažení

Ukázkový projekt stáhnete zde Pro jakýkoliv dotaz mně neváhejte kontaktovat buď pomocí komentářů pod článkem nebo zde

Komentáře

ukládám komentář, vyčkejte prosím..
  1. Petr

    No škoda, že se to nedá použít s autoproperty, definovat property takto je dost opruz a navíc je to pořád ten stejný kod.

    12.01.2011 @ 16:26
  2. Petr

    Definovíní IPropertyChanged pomocí AOP code.google.com/.../notifypropertyw

    12.01.2011 @ 22:46

@xamlcz

  • RT @jvanrhyn: XAML, It's a bit like olives. Takes a while to get used to. But once you're used to it. It is actually pretty good. <3 XAML
  • RT @moser_christian: WPF Inspector 0.9.7 is released. It supports .NET 3.5 and 4.0 The project is now open source and available on CodeP ...
  • Jeff Handley oznámil vydání WCF RIA Services v.1.0 SP1 RTM http://bit.ly/gOgckn ke stažení na http://bit.ly/gVAXdK
  • jedna výzva pro Brno. Byl někdo z vás na přednášce o RIA v MS Akvárku? Dejte o sobě vědět. Děkuji
  • také jste uvažovali o tom, že zkusíte na projekt použít Caliburn Micro nebo naopak Prism 4? A co tak obojí, šlo by to nebo ne? Již brzy