Přehled datových vazeb (WPF .NET)

Datová vazba ve WPF (Windows Presentation Foundation) je jednoduchý a konzistentní způsob, jakým aplikace můžou zobrazovat data a pracovat s nimi. Elementy mohou být vázány na data z různých druhů zdrojů dat ve formě objektů .NET a XML. Jakékoli ContentControl, například Button a jakékoli ItemsControl, například ListBox a ListView, mají integrované funkce, které umožňují flexibilní použití stylů jednotlivých datových položek nebo kolekcí datových položek. Nad daty lze generovat zobrazení řazení, filtrování a seskupení.

Datová vazba v WPF má oproti tradičním modelům několik výhod, včetně vlastní podpory datových vazeb širokou škálou vlastností, flexibilní reprezentace dat v uživatelském rozhraní a jasného oddělení obchodní logiky od uživatelského rozhraní.

Tento článek nejprve popisuje základní koncepty datové vazby WPF a pak se zabývá používáním třídy Binding a dalších funkcí datové vazby.

Důležité

Dokumentace k desktopové příručce pro .NET 7 a .NET 6 se právě připravuje.

Co je datová vazba?

Datová vazba je proces, který vytvoří propojení mezi uživatelským rozhraním aplikace a daty, která zobrazuje. Pokud má vazba správné nastavení a data poskytují správná oznámení při změně jejich hodnoty, elementy vázané na data se automaticky změní. Datová vazba může také znamenat, že pokud se změní vnější reprezentace dat v elementu, mohou být podkladová data automaticky aktualizována tak, aby odrážela tuto změnu. Pokud například uživatel upraví hodnotu v elementu TextBox, hodnota podkladových dat se automaticky aktualizuje tak, aby tuto změnu odrážela.

Typickým použitím datové vazby je umístění serverových nebo místních konfiguračních dat do formulářů nebo jiných ovládacích prvků uživatelského rozhraní. Ve WPF je tento koncept rozšířen tak, aby zahrnoval vazbu široké škály vlastností na různé druhy zdrojů dat. Ve WPF mohou být vlastnosti závislostí elementů vázané na objekty .NET (včetně objektů ADO.NET nebo objektů přidružených webovým službám a webovým vlastnostem) a data XML.

Základní koncepty datových vazeb

Bez ohledu na to, pro jaký element vazbu vytváříte, a povahu zdroje dat se každá vazba vždy řídí modelem znázorněným na následujícím obrázku.

Diagram that shows the basic data binding model.

Jak je na obrázku vidět, datová vazba je v podstatě mostem mezi cílem vazby a zdrojem vazby. Obrázek představuje následující základní koncepty datových vazeb WPF:

  • Každá vazba má obvykle čtyři komponenty:

    • Objekt cíle vazby.
    • Vlastnost cíle.
    • Zdroj vazby.
    • Cestu k hodnotě ve zdroji vazby, která se má použít.

    Pokud například svážete obsah TextBox s vlastností Employee.Name, nastavíte vazbu jako v následující tabulce:

    Nastavení Hodnota
    Cíl TextBox
    Vlastnost cíle Text
    Zdrojový objekt Employee
    Cesta k hodnotě zdrojového objektu Name
  • Vlastnost cíle musí být vlastnost závislosti.

    Většina vlastností UIElement jsou vlastnosti závislostí a většina vlastností závislostí, kromě těch pouze pro čtení, podporuje datovou vazbu ve výchozím nastavení. Vlastnosti závislosti mohou být definovány pouze typy odvozenými z DependencyObject. Všechny typy UIElement jsou odvozeny od DependencyObject.

  • Zdroje vazeb nejsou omezeny na vlastní objekty .NET.

    I když to na obrázku není vidět, je třeba poznamenat, že objekt zdroje vazby není omezen na vlastní objekt .NET. Datová vazba WPF podporuje data ve formě objektů .NET, XML a dokonce objektů elementů XAML. Abychom uvedli několik příkladů, váš zdroj vazby může být UIElement, libovolný objekt seznamu, objekt ADO.NET nebo objekt webových služeb, nebo XmlNode obsahující vaše data XML. Další informace najdete v přehledu zdrojů vazeb.

Je důležité pamatovat na to, že když vytváříte vazbu, vytváříte vazbu mezi cílem vazby a zdrojem vazby. Pokud například zobrazujete určitá podkladová data XML v ListBox pomocí datové vazby, vytváříte vazbu ListBox na data XML.

K vytvoření vazby použijete objekt Binding. Zbytek tohoto článku popisuje koncepty spojené s objektem Binding a některé jeho vlastnosti a použití.

Kontext dat

Když se datová vazba deklaruje u elementů XAML, přeloží tyto elementy datovou vazbu z pohledu na jejich okamžitou vlastnost DataContext. Kontext dat je obvykle objekt zdroje vazby pro vyhodnocení cesty k hodnotě zdroje vazby. Toto chování ve vazbě můžete přepsat a nastavit konkrétní hodnotu objektu zdroje vazby. Pokud není vlastnost DataContext objektu hostujícího vazbu nastavená, kontroluje se vlastnost DataContext nadřazeného elementu atd. až po kořen stromu objektů XAML. Kontext dat použitý k překladu vazby se dědí z nadřazeného objektu, pokud není explicitně nastaven na daný objekt.

Vazby lze nakonfigurovat tak, aby byly přeloženy s konkrétním objektem, a nikoli pomocí kontextu dat pro překlad vazby. Přímé určení zdrojového objektu se používá, když například vytvoříte vazbu barvy popředí objektu na barvu pozadí jiného objektu. Kontext dat není potřeba, protože vazba se přeloží mezi těmito dvěma objekty. Vazby, které nejsou vázané na konkrétní zdrojové objekty, používají překlad kontextu dat.

Když dojde ke změně vlastnosti DataContext, všechny vazby, které by mohly být kontextem dat ovlivněny, budou znovu zhodnoceny.

Směr toku dat

Jak je naznačeno šipkou na předchozím obrázku, tok dat vazby může přecházet z cíle vazby do zdroje vazby (například zdrojová hodnota se změní, když uživatel upraví hodnotu TextBox) nebo ze zdroje vazby do cíle vazby (například obsah TextBox se aktualizuje změnami ve zdroji vazby), za předpokladu, že zdroj vazby poskytuje správná oznámení.

Můžete chtít, aby vaše aplikace umožňovala uživatelům změnit data a rozšířit je zpět do zdrojového objektu. Nebo nemusíte chtít uživatelům povolit, aby zdrojová data aktualizovali. Tok dat můžete řídit nastavením Binding.Mode.

Tento obrázek znázorňuje různé typy toku dat:

Data binding data flow

  • Vazba OneWay způsobí, že změny vlastnosti zdroje automaticky aktualizují vlastnost cíle, ale změny vlastnosti cíle se nešíří zpět do vlastnosti zdroje. Tento typ vazby je vhodný, pokud je vázaný ovládací prvek implicitně jen pro čtení. Můžete například vytvořit vazbu na zdroj, jako je burzovní pole, nebo možná vlastnost vašeho cíle nemá k dispozici žádné rozhraní ovládacího prvku pro provádění změn, jako je například barva pozadí vázaná na data tabulky. Pokud není potřeba monitorovat změny vlastnosti cíle, můžete se pomocí režimu vazby OneWay vyhnout režii režimu vazby TwoWay.

  • Vazba TwoWay způsobí, že se změny vlastnosti zdroje nebo vlastnosti cíle navzájem automaticky aktualizují. Tento typ vazby je vhodný pro upravitelné formuláře nebo jiné plně interaktivní scénáře uživatelského rozhraní. Výchozí hodnotou většiny vlastností je vazba OneWay, ale některé vlastnosti závislostí (obvykle vlastnosti ovládacích prvků upravitelných uživatelem, například TextBox.Text a checkBox.IsChecked) mají jako výchozí hodnotu vazbu TwoWay.

    Programový způsob, jak určit, zda vlastnost závislosti váže jednosměrnou nebo obousměrnou ve výchozím nastavení, je získat metadata vlastnosti s DependencyProperty.GetMetadata. Návratovým typem této metody je PropertyMetadata, který neobsahuje žádná metadata o vazbě. Pokud však lze tento typ přetypovat na odvozené FrameworkPropertyMetadata, lze zkontrolovat logickou hodnotu FrameworkPropertyMetadata.BindsTwoWayByDefault vlastnosti. Následující příklad kódu ukazuje získání metadat pro TextBox.Text vlastnost:

    public static void PrintMetadata()
    {
        // Get the metadata for the property
        PropertyMetadata metadata = TextBox.TextProperty.GetMetadata(typeof(TextBox));
    
        // Check if metadata type is FrameworkPropertyMetadata
        if (metadata is FrameworkPropertyMetadata frameworkMetadata)
        {
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:");
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}");
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}");
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}");
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}");
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}");
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}");
        }
    
        /*  Displays:
         *  
         *  TextBox.Text property metadata:
         *    BindsTwoWayByDefault: True
         *    IsDataBindingAllowed: True
         *          AffectsArrange: False
         *          AffectsMeasure: False
         *           AffectsRender: False
         *                Inherits: False
        */
    }
    
    Public Shared Sub PrintMetadata()
    
        Dim metadata As PropertyMetadata = TextBox.TextProperty.GetMetadata(GetType(TextBox))
        Dim frameworkMetadata As FrameworkPropertyMetadata = TryCast(metadata, FrameworkPropertyMetadata)
    
        If frameworkMetadata IsNot Nothing Then
    
            System.Diagnostics.Debug.WriteLine($"TextBox.Text property metadata:")
            System.Diagnostics.Debug.WriteLine($"  BindsTwoWayByDefault: {frameworkMetadata.BindsTwoWayByDefault}")
            System.Diagnostics.Debug.WriteLine($"  IsDataBindingAllowed: {frameworkMetadata.IsDataBindingAllowed}")
            System.Diagnostics.Debug.WriteLine($"        AffectsArrange: {frameworkMetadata.AffectsArrange}")
            System.Diagnostics.Debug.WriteLine($"        AffectsMeasure: {frameworkMetadata.AffectsMeasure}")
            System.Diagnostics.Debug.WriteLine($"         AffectsRender: {frameworkMetadata.AffectsRender}")
            System.Diagnostics.Debug.WriteLine($"              Inherits: {frameworkMetadata.Inherits}")
    
            '  Displays:
            '
            '  TextBox.Text property metadata:
            '    BindsTwoWayByDefault: True
            '    IsDataBindingAllowed: True
            '          AffectsArrange: False
            '          AffectsMeasure: False
            '           AffectsRender: False
            '                Inherits: False
        End If
    
    
    End Sub
    
  • OneWayToSource je obrácená vazba OneWay, aktualizuje vlastnost zdroje při změně vlastnosti cíle. Jedním z ukázkových scénářů je, že potřebujete pouze znovu vyhodnotit hodnotu zdroje z uživatelského rozhraní.

  • Na obrázku není znázorněna vazba OneTime, která způsobuje, že vlastnost zdroje inicializuje vlastnost cíle, ale nešíří následné změny. Pokud se změní kontext dat nebo objekt v kontextu dat, změna se neprojeví ve vlastnosti cíle. Tento typ vazby je vhodný, pokud je snímek aktuálního stavu správný nebo jsou data skutečně statická. Tento typ vazby je užitečný také v případě, že chcete inicializovat vlastnost cíle s určitou hodnotou z vlastnosti zdroje a kontext dat není předem známý. Tento režim je v podstatě jednodušší formou vazby OneWay, která poskytuje lepší výkon v případech, kdy se hodnota zdroje nezmění.

Aby bylo možné zjistit změny zdroje (týkající se vazeb OneWay a TwoWay), musí zdroj implementovat vhodný mechanismus oznámení změny vlastnosti, jako je například INotifyPropertyChanged. Viz Postupy: Implementace oznámení změn vlastností (.NET Framework), kde najdete příklad implementace INotifyPropertyChanged.

Vlastnost Binding.Mode poskytuje další informace o režimech vazby a příklad toho, jak určit směr vazby.

Co aktivuje aktualizace zdroje

Vazby, které jsou TwoWay nebo OneWayToSource, naslouchají změnám ve vlastnosti cíle a šíří je zpět do zdroje, což se označuje jako aktualizace zdroje. Můžete například upravit text textového pole, aby se změnila podkladová hodnota zdroje.

Aktualizuje se ale hodnota vašeho zdroje v průběhu vašich úprav textu nebo po dokončení vašich úprav textu, když ovládací prvek ztratí fokus? Vlastnost Binding.UpdateSourceTrigger určuje, co aktivuje aktualizaci zdroje. Tečky šipek vpravo na následujícím obrázku znázorňují roli vlastnosti Binding.UpdateSourceTrigger.

Diagram that shows the role of the UpdateSourceTrigger property.

V případě, že hodnota UpdateSourceTrigger je UpdateSourceTrigger.PropertyChanged, pak hodnota odkazovaná šipkou vpravo TwoWay nebo vazby OneWayToSource se aktualizuje, jakmile se vlastnost cíle změní. V případě, že hodnota UpdateSourceTrigger je ale LostFocus, potom se daná hodnota aktualizuje novou hodnotou, pouze když hodnota cíle ztratí fokus.

Podobně jako u vlastnosti Mode mají různé vlastnosti závislostí různé výchozí hodnoty UpdateSourceTrigger. Výchozí hodnota pro většinu vlastností závislostí je PropertyChanged, což způsobí, že hodnota vlastnosti zdroje se okamžitě změní, když se změní hodnota vlastnosti cíle. Okamžité změny jsou vhodné pro CheckBox a další jednoduché ovládací prvky. V případě textových polí ale aktualizace po každém stisknutí klávesy může snížit výkon a odepřít uživateli obvyklou příležitost vymazat a opravit chyby při psaní, než novou hodnotu potvrdí. Například vlastnost TextBox.Text je ve výchozím nastavení UpdateSourceTrigger s hodnotou LostFocus, což způsobí, že hodnota zdroje se změní pouze v případě, že ovládací prvek ztratí fokus, nikoli při změně vlastnosti TextBox.Text. Informace o tom, jak najít výchozí hodnotu vlastnosti závislosti, najdete na stránce vlastnosti UpdateSourceTrigger.

Následující tabulka poskytuje ukázkový scénář pro každou hodnotu UpdateSourceTrigger používající TextBox jako příklad.

Hodnota UpdateSourceTrigger Když se hodnota zdroje aktualizuje Ukázkový scénář pro TextBox (textové pole)
LostFocus (výchozí pro TextBox.Text) Když ovládací prvek TextBox ztratí fokus. TextBox, který je přidružený ověřovací logice (viz Ověření dat níže).
PropertyChanged Při psaní do TextBox. Ovládací prvky TextBox v okně chatovací místnosti.
Explicit Když aplikace volá UpdateSource. Ovládací prvky TextBox v upravitelném formuláři (aktualizuje hodnoty zdroje pouze tehdy, když uživatel stiskne tlačítko odeslat).

Příklad najdete v tématu Postupy: Určení, kdy dojde k aktualizaci textu TextBox ve zdroji (.NET Framework).

Příklad datové vazby

Příklad datové vazby najdete v následujícím uživatelském rozhraní aplikace z ukázky datové vazby, která zobrazuje seznam položek aukce.

Data binding sample screenshot

Aplikace ukazuje následující funkce datové vazby:

  • Obsah ovládacího prvku ListBox je vázán na kolekci objektů AuctionItem. Objekt AuctionItem má vlastnosti, jako je Description, StartPrice, StartDate, Category a SpecialFeatures.

  • Data (objekty AuctionItem) zobrazená v ListBox mají šablonu, aby se popis a aktuální cena zobrazovaly pro každou položku. Šablona se vytvoří pomocí DataTemplate. Kromě toho vzhled jednotlivých položek závisí na hodnotě SpecialFeatures zobrazeného objektu AuctionItem. Pokud je hodnota SpecialFeatures objektu AuctionItemColor, položka má modré ohraničení. Pokud je hodnota Highlight, má položka oranžové ohraničení a hvězdičku. Část Šablonování dat poskytuje informace o šablonách dat.

  • Uživatel může data seskupit, filtrovat nebo řadit pomocí zadaného CheckBoxes. Na obrázku výše jsou vybrány možnosti Seskupit podle kategorie a Řadit podle kategorie a dataCheckBoxes. Možná jste si všimli, že se data seskupují podle kategorie produktu a názvu kategorie je v abecedním pořadí. Na obrázku je to hůře vidět, ale položky se také v jednotlivých kategoriích řadí podle počátečního data. Řazení se provádí pomocí zobrazení kolekce. Část Vazby na kolekce popisuje zobrazení kolekcí.

  • Když uživatel vybere položku, ContentControl zobrazí se podrobnosti o vybrané položce. Toto prostředí se nazývá scénář seznam-podrobnosti. Část se scénářem seznam-podrobnosti obsahuje informace o tomto typu vazby.

  • Typ vlastnosti StartDate je DateTime a vrací datum, které zahrnuje čas v milisekundách. V této aplikaci byl použit vlastní převaděč, aby se zobrazil kratší řetězec kalendářního data. Část Převod dat obsahuje informace o převaděčích.

Když uživatel vybere tlačítko Přidat produkt, zobrazí se následující formulář.

Add Product Listing page

Uživatel může upravit pole ve formuláři, zobrazit náhled výpisu produktů pomocí krátkých nebo podrobných podoken náhledu a vybrat Submit pro přidání nového výpisu produktů. Všechna existující nastavení seskupení, filtrování a řazení se použijí pro novou položku. V tomto konkrétním případě se položka zadaná na obrázku výše zobrazí jako druhá položka v kategorii Počítač.

Na tomto obrázku se nezobrazuje ověřovací logika zadaná v počátečním datuTextBox. Pokud uživatel zadá neplatné datum (neplatný formát nebo datum v minulosti), zobrazí se uživateli oznámení s ToolTip červeným vykřičníkem vedle TextBox. Část Ověření dat popisuje, jak vytvořit logiku ověřování.

Než se začneme věnovat různým funkcím datových vazeb uvedených výše, podíváme se nejprve na základní koncepty, které jsou důležité pro pochopení datové vazby WPF.

Vytvoření vazby

Zopakujme si některé koncepty probrané v předchozích částech, vazbu vytváříte pomocí objektu Binding a každá vazba má obvykle čtyři součásti: cíl vazby, vlastnost cíle, zdroj vazby a cestu ke zdrojové hodnotě, kterou chcete použít. Tato část popisuje, jak nastavit vazbu.

Zdroje vazeb jsou svázané s aktivním DataContext pro daný element. Elementy automaticky dědí své DataContext, pokud je nemají explicitně definované.

Představte si následující příklad, ve kterém je objekt zdroje vazby třída MyData definovaná v oboru názvů SDKSample. Pro demonstrační účely má třída MyData vlastnost řetězce s názvem ColorName, jejíž hodnota je nastavena na „Red“. Tento příklad tedy vygeneruje tlačítko s červeným pozadím.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <DockPanel.DataContext>
        <Binding Source="{StaticResource myDataSource}"/>
    </DockPanel.DataContext>
    <Button Background="{Binding Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

Další informace o syntaxi deklarace vazby a příkladech nastavení vazby v kódu najdete v Přehledu deklarací vazeb.

Pokud tento příklad použijeme u našeho základního diagramu, bude výsledný obrázek vypadat takto. Tento obrázek popisuje vazbu OneWay, protože vlastnost Background ve výchozím nastavení podporuje vazbu OneWay.

Diagram that shows the data binding Background property.

Možná vás zajímá, proč tato vazba funguje, i když je vlastnost ColorName typu řetězec, zatímco vlastnost Background je typu Brush. Tato vazba používá výchozí převod typů, který je popsán v části Převod dat.

Určení zdroje vazby

Všimněte si, že v předchozím příkladu je zdroj vazby určen nastavením vlastnosti DockPanel.DataContext. Button potom zdědí hodnotu DataContext ze svého nadřazeného prvku DockPanel. Zopakujme si, že objekt zdroje vazby je jednou ze čtyř nezbytných součástí vazby. Bez zadaného objektu zdroje vazby by vazba nic neudělala.

Existuje několik způsobů, jak určit objekt zdroje vazby. Použití vlastnosti DataContext u nadřazeného elementu je užitečné, když vytváříte vazby více vlastností na stejný zdroj. Někdy ale může být vhodnější určit zdroj vazby u jednotlivých deklarací vazeb. V předchozím příkladu můžete místo použití vlastnosti DataContext určit zdroj vazby nastavením vlastnosti Binding.Source přímo u deklarace vazby tlačítka jako v následujícím příkladu.

<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:c="clr-namespace:SDKSample">
    <DockPanel.Resources>
        <c:MyData x:Key="myDataSource"/>
    </DockPanel.Resources>
    <Button Background="{Binding Source={StaticResource myDataSource}, Path=ColorName}"
            Width="150" Height="30">
        I am bound to be RED!
    </Button>
</DockPanel>

Kromě nastavení vlastnosti DataContext přímo u elementu, zdědění hodnoty DataContext z nadřazeného objektu (například tlačítka v prvním příkladu) a explicitního určení zdroje vazby nastavením vlastnosti Binding.Source u vazby (například tlačítko poslední příklad), můžete k určení zdroje vazby použít také vlastnost Binding.ElementName nebo vlastnost Binding.RelativeSource. Vlastnost ElementName je užitečná, když vytváříte vazbu na jiné elementy v aplikaci, například když používáte posuvník k úpravě šířky tlačítka. Vlastnost RelativeSource je užitečná, když je vazba zadána v ControlTemplate nebo Style. Další informace najdete v přehledu zdrojů vazeb.

Určení cesty k hodnotě

Pokud je zdrojem vazby objekt, použijete vlastnost Binding.Path, abyste určili hodnotu, která se má použít pro vaši vazbu. Pokud vytváříte vazbu na data XML, použijete vlastnost Binding.XPath, abyste určili hodnotu. V některých případech se může použít vlastnost Path i pro data XML. Pokud například chcete získat přístup k vlastnosti Name vráceného XmlNode (výsledek dotazu XPath), měli byste kromě vlastnosti XPath použít také vlastnost Path.

Pokud chcete získat další informace, podívejte se na vlastnosti Path a XPath.

Ačkoli jsme zdůraznili, že Path k hodnotě, kterou chcete použít, je jednou ze čtyř nezbytných součástí vazby, ve scénářích, kde chcete vytvořit vazbu na celý objekt, bude použitá hodnota stejná jako objekt zdroje vazby. V takových případě je možné nespecifikovat Path. Představte si následující příklad.

<ListBox ItemsSource="{Binding}"
         IsSynchronizedWithCurrentItem="true"/>

Výše uvedený příklad používá prázdnou syntaxi vazby: {Binding}. V tomto případě ListBox dědí DataContext z nadřazeného elementu DockPanel (v tomto příkladu není znázorněn). Pokud cesta není zadaná, vytvoří se ve výchozím nastavením vazba na celý objekt. V tomto příkladu byla tedy cesta vynechána, protože vytváříme vazbu vlastnosti ItemsSource na celý objekt. (Podrobné informace najdete v části Vazby na kolekce.)

Kromě vazby na kolekci je tento scénář užitečný také v případě, že chcete vytvořit vazbu na celý objekt, nikoli pouze na jednu vlastnost objektu. Pokud je například zdrojový objekt typu String, můžete jednoduše chtít vytvořit vazbu na samotný řetězec. Dalším běžným scénářem je, že chcete vytvořit vazbu elementu na objekt s několika vlastnostmi.

Možná budete muset použít vlastní logiku, aby data byla pro vlastnost vašeho cíle smysluplná. Pokud neexistuje výchozí převod typu, může být vlastní logika ve formě vlastního převaděče. Informace o převaděčích najdete v tématu Převod dat.

Vazby a BindingExpression

Než se seznámíte s dalšími funkcemi a použitím datové vazby, je vhodné představit třídu BindingExpression. Jak jste viděli v předchozích částech, třída Binding je třída vysoké úrovně pro deklaraci vazby, poskytuje mnoho vlastností, které umožňují určit vlastnosti vazby. Související třída BindingExpression je podkladový objekt, který udržuje spojení mezi zdrojem a cílem. Vazba obsahuje všechny informace, které lze sdílet napříč několika výrazy vazby. BindingExpression je výraz instance, který nelze sdílet a který obsahuje všechny informace o instanci pro Binding.

Vezměte v úvahu následující příklad, kde myDataObject je instance třídy MyData, myBinding je objekt zdroje Binding a MyData je definovaná třída, která obsahuje vlastnost řetězce s názvem ColorName. Tento příklad vytvoří vazbu textového obsahu myText, instance TextBlock, na ColorName.

// Make a new source
var myDataObject = new MyData();
var myBinding = new Binding("ColorName")
{
    Source = myDataObject
};

// Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding);
' Make a New source
Dim myDataObject As New MyData
Dim myBinding As New Binding("ColorName")
myBinding.Source = myDataObject

' Bind the data source to the TextBox control's Text dependency property
myText.SetBinding(TextBlock.TextProperty, myBinding)

Pomocí stejného objektu myBinding můžete vytvořit další vazby. Objekt myBinding můžete například použít k vytvoření vazby textového obsahu zaškrtávacího políčka na ColorName. V tomto scénáři budou existovat dvě instance BindingExpression sdílejícího objekt myBinding.

Objekt BindingExpression je vrácen voláním GetBindingExpression u objektu vázaného na data. Následující články představují některá použití třídy BindingExpression:

Převod dat

V části Vytvoření vazby je tlačítko červené, protože jeho vlastnost Background je vázána na vlastnost řetězce s hodnotou „Red“. Tato hodnota řetězce funguje, protože převaděč typů je u typu Brush přítomen, aby hodnotu řetězce převedl na Brush.

Přidání těchto informací na obrázek v části Vytvoření vazby vypadá takto.

Diagram that shows the data binding Default property.

Co když ale váš objekt zdroje vazby nemá vlastnost typu řetězec a jeho vlastnost Color je typu Color? Aby v takovém případě vazba fungovala, musíte nejprve změnit hodnotu vlastnosti Color na něco, co vlastnost Background přijímá. Budete muset vytvořit vlastní převaděč prostřednictvím implementace rozhraní IValueConverter, jako v následujícím příkladu.

[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}
<ValueConversion(GetType(Color), GetType(SolidColorBrush))>
Public Class ColorBrushConverter
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        Dim color As Color = CType(value, Color)
        Return New SolidColorBrush(color)
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Return Nothing
    End Function
End Class

Další informace naleznete v tématu IValueConverter.

Vlastní převaděč se teď používá místo výchozího převodu a náš diagram vypadá takto.

Diagram that shows the data binding custom converter.

Zopakujme si, že výchozí převody mohou být dostupné díky přítomnosti převaděčů typů v typu, ke kterému se vazba vytváří. Toto chování bude záviset na tom, které převaděče typů jsou v cíli k dispozici. Pokud máte pochybnosti, vytvořte si vlastní převaděč.

Tady jsou některé typické scénáře, ve kterých je implementace převaděče data vhodná:

  • Vaše data by se měla v závislosti na jazykové verzi zobrazovat odlišně. Můžete například chtít implementovat převaděč měny nebo převaděč kalendářního data a času na základě konvencí používaných v určité jazykové verzi.

  • Použitá data nemusí nutně změnit textovou hodnotu vlastnosti, ale jsou určená k tomu, aby změnila jinou hodnotu, například zdroj obrázku nebo barvy či styl zobrazovaného textu. Převaděče lze v této instanci použít prostřednictvím převodu vazby vlastnosti, která se nemusí zdát vhodná, například vytvořením vazby textového pole na vlastnost Background buňky tabulky.

  • Více než jeden ovládací prvek nebo více vlastností ovládacích prvků je vázáno na stejná data. V tomto případě může primární vazba jenom zobrazit text, zatímco jiné vazby zpracovávají konkrétní problémy se zobrazením, ale přitom jako zdrojové informace stále používají stejnou vazbu.

  • Vlastnost cíle má kolekci vazeb, které se označují jako MultiBinding. Pro MultiBinding použijete vlastní IMultiValueConverter, abyste vytvořili konečnou hodnotu z hodnot vazeb. Například barvu lze vypočítat z červených, modrých a zelených hodnot, což můžou být hodnoty ze stejných nebo jiných objektů zdroje vazby. Informace a příklady najdete v části MultiBinding.

Vazby na kolekce

Objekt zdroje vazby lze považovat za jeden objekt, jehož vlastnosti obsahují data, nebo za kolekci dat polymorfních objektů, které jsou často seskupeny dohromady (například výsledek dotazu do databáze). Zatím jsme se bavili pouze o vytváření vazby na jednotlivé objekty. Vazba na kolekci dat je ale běžným scénářem. Běžným scénářem je například použití ItemsControl typu ListBox, ListView nebo TreeView k zobrazení kolekce dat, například v aplikaci uvedené v části Co je datová vazba.

Naštěstí stále platí náš základní diagram. Pokud vytváříte vazbu ItemsControl na kolekci, diagram vypadá takto.

Diagram that shows the data binding ItemsControl object.

Jak je znázorněno v tomto diagramu, pokud chcete vytvořit vazbu ItemsControl na objekt kolekce, musíte použít vlastnost ItemsControl.ItemsSource. ItemsSource si můžete představit jako obsah ItemsControl. Vazba je OneWay, protože vlastnost ItemsSource ve výchozím nastavení podporuje vazbu OneWay.

Postup implementace kolekcí

Můžete vytvořit výčet všech kolekcí, které implementují rozhraní IEnumerable. Pokud ale chcete nastavit dynamické vazby tak, aby vložení nebo odstranění v kolekci automaticky aktualizovalo uživatelské rozhraní, musí kolekce implementovat rozhraní INotifyCollectionChanged. Toto rozhraní zveřejňuje událost, která by měla být vyvolána při každé změně podkladové kolekce.

WPF poskytuje třídu ObservableCollection<T>, což je integrovaná implementace kolekce dat, která zveřejňuje rozhraní INotifyCollectionChanged. Aby bylo možné plně podporovat přenos hodnot dat ze zdrojových objektů do cílů, musí každý objekt v kolekci, který podporuje vlastnosti s možností vazeb, implementovat také rozhraní INotifyPropertyChanged. Další informace najdete v přehledu zdrojů vazeb.

Před implementací vlastní kolekce zvažte použití ObservableCollection<T> nebo některé ze stávajících tříd kolekcí, například List<T>, Collection<T>, BindingList<T> a dalších. Pokud máte pokročilý scénář a chcete implementovat vlastní kolekci, zvažte použití IList, který poskytuje neobecnou kolekci objektů, ke kterým může index přistupovat jednotlivě a poskytuje tak nejlepší výkon.

Zobrazení kolekcí

Jakmile je objekt ItemsControl vázaný na kolekci dat, můžete chtít data řadit, filtrovat nebo seskupit. K tomu použijete zobrazení kolekcí, což jsou třídy, které implementují rozhraní ICollectionView.

Co jsou zobrazení kolekcí?

Zobrazení kolekce je vrstva nad kolekcí zdrojů vazby, která umožňuje procházet a zobrazovat kolekci zdrojů na základě řazení, filtrování a seskupení dotazů, aniž byste museli měnit samotnou podkladovou kolekci zdrojů. Zobrazení kolekce také udržuje ukazatel na aktuální položce v kolekci. Pokud kolekce zdrojů implementuje rozhraní INotifyCollectionChanged, změny vyvolané událostí CollectionChanged se rozšíří do zobrazení.

Vzhledem k tomu, že zobrazení nemění podkladové kolekce zdrojů, může mít každá kolekce zdrojů přidruženo více zobrazení. Můžete mít například kolekci objektů Task. Pomocí zobrazení můžete stejná data zobrazit různými způsoby. Na levé straně stránky můžete například chtít zobrazit úkoly seřazené podle priority a na pravé straně seskupené podle oblasti.

Postup vytvoření zobrazení

Jedním ze způsobů, jak vytvořit a použít zobrazení, je vytvořit přímo instanci objektu zobrazení a pak ji použít jako zdroj vazby. Představte si například ukázkové aplikace datové vazby zobrazenou v části Co je datová vazba. Aplikace je implementovaná tak, aby ListBox vytvořil vazbu na zobrazení kolekce dat, nikoli přímo na kolekci dat. Následující příklad se extrahuje z ukázkové aplikace datové vazby. Třída CollectionViewSource je XAML proxy třídy, která dědí z CollectionView. V tomto konkrétním příkladu je Source zobrazení vázaný na kolekci AuctionItems (typu ObservableCollection<T>) aktuálního objektu aplikace.

<Window.Resources>
    <CollectionViewSource 
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />
</Window.Resources>

listingDataView prostředku pak slouží jako zdroj vazby pro elementy v aplikaci, jako je například ListBox.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />

Pokud chcete vytvořit další zobrazení pro stejnou kolekci, můžete vytvořit jinou instanci CollectionViewSource a dát jí jiný název x:Key.

Následující tabulka ukazuje, jaké datové typy zobrazení se vytvářejí jako výchozí zobrazení kolekce nebo podle CollectionViewSource na základě typu kolekce zdrojů.

Typ kolekce zdrojů Typ zobrazení kolekce Notes
IEnumerable Interní typ založený na CollectionView Položky nelze seskupit.
IList ListCollectionView Nejrychlejší.
IBindingList BindingListCollectionView

Použití výchozího zobrazení

Určení zobrazení kolekce jako zdroje vazeb je jedním ze způsobů, jak vytvořit a použít zobrazení kolekce. WPF také vytvoří výchozí zobrazení kolekce pro každou kolekci používanou jako zdroj vazby. Pokud vytvoříte vazbu přímo na kolekci, WPF vytvoří vazbu na její výchozí zobrazení. Toto výchozí zobrazení je sdíleno všemi vazbami na stejnou kolekci, takže změna provedené ve výchozím zobrazení jedním vázaným ovládacím prvkem nebo kódem (například řazením nebo změnou ukazatele aktuální položky, viz další části) se projeví ve všech ostatních vazbách na stejnou kolekci.

Pokud chcete získat výchozí zobrazení, použijte metodu GetDefaultView. Příklad najdete v tématu Získání výchozího zobrazení kolekce dat (.NET Framework).

Zobrazení kolekcí s datovými tabulkami ADO.NET

Za účelem zlepšení výkonu delegují zobrazení kolekce pro ADO.NET DataTable nebo objektyDataView řazení a filtrování na DataView, což způsobí, že se řazení a filtrování sdílí napříč všemi zobrazeními kolekcí zdroje dat. Pokud chcete každému zobrazení kolekce povolit nezávislé řazení a filtrování, inicializujte každé zobrazení kolekce s jeho vlastním objektem DataView.

Třídění

Jak už bylo zmíněno dříve, zobrazení mohou u kolekce použít pořadí řazení. Vzhledem k tomu, že existují v podkladové kolekci, vaše data mohou a nemusí mít relevantní základní pořadí. Zobrazení kolekce umožňuje vynutit pořadí, nebo změnit výchozí pořadí, na základě vámi zadaných kritérií porovnání. Vzhledem k tomu, že se jedná o zobrazení dat na základě klienta, je běžným scénářem, že uživatel může chtít seřadit sloupce tabulkových dat podle hodnoty, které sloupec odpovídá. Pomocí zobrazení lze toto uživatelem řízené řazení použít, aniž byste museli provádět změny v podkladové kolekci nebo se dokonce museli znovu dotazovat na obsah kolekce. Příklad najdete v tématu Řazení sloupce GridView při kliknutí na záhlaví (.NET Framework).

Následující příklad ukazuje logiku řazení „Řadit podle kategorie a data“ CheckBox uživatelského rozhraní aplikace v části Co je datová vazba.

private void AddSortCheckBox_Checked(object sender, RoutedEventArgs e)
{
    // Sort the items first by Category and then by StartDate
    listingDataView.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(new SortDescription("StartDate", ListSortDirection.Ascending));
}
Private Sub AddSortCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    ' Sort the items first by Category And then by StartDate
    listingDataView.SortDescriptions.Add(New SortDescription("Category", ListSortDirection.Ascending))
    listingDataView.SortDescriptions.Add(New SortDescription("StartDate", ListSortDirection.Ascending))
End Sub

Filtrování

Zobrazení dále mohou na kolekci použít filtr, tak aby zobrazení zobrazovalo pouze určitou podmnožinu celé kolekce. V datech můžete filtrovat pomocí podmínky. Jak to například dělá aplikace v části Co je datová vazba. „Show only bargains“ CheckBox obsahuje logiku pro filtrování položek, které stojí 25 USD nebo více. Následující kód se spustí za účelem nastavení ShowOnlyBargainsFilter jako obslužné rutiny události Filter při výběru CheckBox.

private void AddFilteringCheckBox_Checked(object sender, RoutedEventArgs e)
{
    if (((CheckBox)sender).IsChecked == true)
        listingDataView.Filter += ListingDataView_Filter;
    else
        listingDataView.Filter -= ListingDataView_Filter;
}
Private Sub AddFilteringCheckBox_Checked(sender As Object, e As RoutedEventArgs)
    Dim checkBox = DirectCast(sender, CheckBox)

    If checkBox.IsChecked = True Then
        AddHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    Else
        RemoveHandler listingDataView.Filter, AddressOf ListingDataView_Filter
    End If
End Sub

Obslužná rutina události ShowOnlyBargainsFilter má následující implementaci.

private void ListingDataView_Filter(object sender, FilterEventArgs e)
{
    // Start with everything excluded
    e.Accepted = false;

    // Only inlcude items with a price less than 25
    if (e.Item is AuctionItem product && product.CurrentPrice < 25)
        e.Accepted = true;
}
Private Sub ListingDataView_Filter(sender As Object, e As FilterEventArgs)

    ' Start with everything excluded
    e.Accepted = False

    Dim product As AuctionItem = TryCast(e.Item, AuctionItem)

    If product IsNot Nothing Then

        ' Only include products with prices lower than 25
        If product.CurrentPrice < 25 Then e.Accepted = True

    End If

End Sub

Pokud používáte přímo některou ze tříd CollectionView místo CollectionViewSource, měli byste k určení zpětného volání použít vlastnost Filter. Příklad najdete v tématu Filtrování dat v zobrazení (.NET Framework).

Seskupení

Kromě interní třídy, která zobrazuje kolekci IEnumerable, podporují všechna zobrazení kolekcí seskupení, což uživateli umožňuje rozdělit kolekci v zobrazení kolekce do logických skupin. Skupiny můžou být explicitní, kde uživatel poskytne seznam skupin, nebo implicitní, kde se skupiny generují dynamicky v závislosti na datech.

Následující příklad ukazuje logiku „seskupení podle kategorie“ CheckBox.

// This groups the items in the view by the property "Category"
var groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);
' This groups the items in the view by the property "Category"
Dim groupDescription = New PropertyGroupDescription()
groupDescription.PropertyName = "Category"
listingDataView.GroupDescriptions.Add(groupDescription)

Další příklad seskupení najdete v tématu Seskupení položek v objektu ListView s implementací GridView (.NET Framework).

Ukazatele aktuální položky

Zobrazení také podporují pojem aktuální položky. Objekty můžete procházet v zobrazení kolekce. Při procházení přesouváte ukazatel položky, který umožňuje načíst objekt existující v tom daném umístění v kolekci. Příklad najdete v tématu Navigace v objektech v datovém zobrazení CollectionView (.NET Framework).

Vzhledem k tomu, že WPF vytvoří vazbu na kolekci pouze pomocí zobrazení (buď vámi zadaného zobrazení, nebo výchozího zobrazení kolekce), mají všechny vazby na kolekce ukazatel aktuální položky. Při vytváření vazby na zobrazení určuje znak lomítka („/“) v hodnotě Path aktuální položku zobrazení. V následujícím příkladu je kontext dat zobrazení kolekce. První řádek vytvoří vazbu na kolekci. Druhý řádek tvoří vazba na aktuální položku v kolekci. Třetí řádek je vazbou na vlastnost Description aktuální položky v kolekci.

<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />

Syntaxi lomítka a vlastnosti lze také poskládat tak, aby procházela hierarchii kolekcí. Následující příklad vytvoří vazbu na aktuální položku kolekce s názvem Offices, což je vlastnost aktuální položky kolekce zdrojů.

<Button Content="{Binding /Offices/}" />

Ukazatel aktuální položky může být ovlivněn jakýmkoli řazením nebo filtrováním, které je v kolekci použito. Řazení zachová ukazatel aktuální položky na poslední vybrané položce, ale zobrazení kolekce se teď kolem něj znovu uspořádá. (Možná, že dříve byla vybraná položka na začátku seznamu, ale teď může být vybraná položka někde uprostřed.) Filtrování vybranou položku zachová, pokud tento výběr zůstane v zobrazení i po filtrování. V opačném případě je ukazatel aktuální položky nastavený na první položku filtrovaného zobrazení kolekce.

Scénář vazby seznam-podrobnosti

Pojem aktuální položky je užitečný nejen pro procházení položek v kolekci, ale také pro scénář vazby seznam-podrobnosti. Zamyslete se znovu nad uživatelským rozhraním aplikace v části Co je datová vazba. V této aplikaci výběr v rámci ListBox určuje obsah zobrazený v ContentControl. Jinak řečeno, když je vybraná položka ListBox, ContentControl zobrazí podrobnosti vybrané položky.

Scénář seznam-podrobnosti můžete implementovat jednoduše tak, že máte dva nebo více ovládacích prvků vázaných na stejné zobrazení. Následující příklad z ukázky datové vazby ukazuje kód ListBox a ContentControl v uživatelském rozhraní aplikace v části Co je datová vazba.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8" 
         ItemsSource="{Binding Source={StaticResource listingDataView}}" />
<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3"
                Content="{Binding Source={StaticResource listingDataView}}"
                ContentTemplate="{StaticResource detailsProductListingTemplate}" 
                Margin="9,0,0,0"/>

Všimněte si, že oba ovládací prvky jsou vázány na stejný zdroj, statický prostředek listingDataView (podívejte se na definici tohoto prostředku v části Postup vytvoření zobrazení). Tato vazba funguje, protože když je jeden objekt (v tomto případě ContentControl) vázán na zobrazení kolekce, automaticky je vázán na CurrentItem zobrazení. Objekty CollectionViewSource automaticky synchronizují měnu a výběr. Pokud ovládací prvek seznamu není vázán na objekt CollectionViewSource jako v tomto příkladu, budete muset nastavit jeho vlastnost IsSynchronizedWithCurrentItem tak, aby true fungoval.

Další příklady najdete v tématu Vazba na kolekci a zobrazení informací podle výběru (.NET Framework) a Použití vzoru seznam-podrobnosti s hierarchickými daty (.NET Framework).

Možná jste si všimli, že výše uvedený příklad používá šablonu. Ve skutečnosti by se data nezobrazovala tak, jak chceme, bez použití šablon (té, kterou explicitně používá ContentControl, a té, kterou implicitně používá ListBox). Šablonováním dat se budeme zabývat v další části.

Vytváření šablon dat

Bez použití šablon dat by uživatelské rozhraní aplikace v části Příklad datové vazby vypadalo takto:

Data Binding Demo without Data Templates

Jak je znázorněno v příkladu v předchozí části, ovládací prvek ListBox i ContentControl jsou vázány na celý objekt kolekce (nebo konkrétněji na zobrazení objektu kolekce) objektů AuctionItem. Bez konkrétních pokynů pro zobrazení kolekce dat zobrazí ListBox řetězcovou reprezentaci každého objektu v podkladové kolekci a ContentControl zobrazí řetězcovou reprezentaci objektu, na který je vázána.

Aby aplikace tento problém vyřešila, definuje DataTemplates. Jak je znázorněno v příkladu v předchozí části, ContentControl explicitně používá šablonu dat detailsProductListingTemplate. Ovládací prvek ListBox implicitně používá následující šablonu dat při zobrazování objektů AuctionItem v kolekci.

<DataTemplate DataType="{x:Type src:AuctionItem}">
    <Border BorderThickness="1" BorderBrush="Gray"
            Padding="7" Name="border" Margin="3" Width="500">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20"/>
                <ColumnDefinition Width="86"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
                     Fill="Yellow" Stroke="Black" StrokeThickness="1"
                     StrokeLineJoin="Round" Width="20" Height="20"
                     Stretch="Fill"
                     Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
                     Visibility="Hidden" Name="star"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
                       Name="descriptionTitle"
                       Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
            
            <TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2"
                       Text="{Binding Path=Description}"
                       Style="{StaticResource textStyleTextBlock}"/>

            <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
                       Name="currentPriceTitle"
                       Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
            
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
                <TextBlock Name="CurrentPriceDTDataType"
                           Text="{Binding Path=CurrentPrice}" 
                           Style="{StaticResource textStyleTextBlock}"/>
            </StackPanel>
        </Grid>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Color</src:SpecialFeatures>
            </DataTrigger.Value>
            <DataTrigger.Setters>
                <Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
                <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
                <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
                <Setter Property="BorderThickness" Value="3" TargetName="border" />
                <Setter Property="Padding" Value="5" TargetName="border" />
            </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Highlight</src:SpecialFeatures>
            </DataTrigger.Value>
            <Setter Property="BorderBrush" Value="Orange" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="Visibility" Value="Visible" TargetName="star" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Výsledkem použití těchto dvou DataTemplates je uživatelské rozhraní zobrazené v části Co je datová vazba. Jak vidíte na tomto snímku obrazovky, kromě toho, že vám umožní umístit data do ovládacích prvků, vám DataTemplates umožní definovat působivé vizuály pro vaše data. Například DataTrigger se ve výše uvedeném DataTemplate používají, aby se AuctionItem s hodnotou SpecialFeaturesHighLight zobrazovaly s oranžovým ohraničením a hvězdičkou.

Další informace o šablonách dat najdete v tématu Přehled šablon dat (.NET Framework).

Ověření dat

Většina aplikací, které přebírají vstup uživatele, musí mít logiku ověření, aby se zajistilo, že uživatel zadal očekávané informace. Kontroly ověření mohou být založené na typu, rozsahu, formátu nebo jiných požadavcích specifických pro aplikaci. Tato část popisuje, jak funguje ověřování dat v WPF.

Přidružení pravidel ověřování k vazbě

Model datové vazby WPF umožňuje přidružit ValidationRules k objektu Binding. Následující příklad vytvoří vazbu TextBox na vlastnost s názvem StartPrice a přidá objekt ExceptionValidationRule do vlastnosti Binding.ValidationRules.

<TextBox Name="StartPriceEntryForm" Grid.Row="2"
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Objekt ValidationRule kontroluje, jestli je hodnota vlastnosti platná. WPF má dva typy integrovaných objektů ValidationRule:

Vlastní ověřovací pravidlo můžete také vytvořit odvozením ze třídy ValidationRule a implementací metody Validate. Následující příklad ukazuje pravidlo používané přidáním výpisu produktů „Počáteční datum“ TextBox z části Co je datová vazba.

public class FutureDateRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        // Test if date is valid
        if (DateTime.TryParse(value.ToString(), out DateTime date))
        {
            // Date is not in the future, fail
            if (DateTime.Now > date)
                return new ValidationResult(false, "Please enter a date in the future.");
        }
        else
        {
            // Date is not a valid date, fail
            return new ValidationResult(false, "Value is not a valid date.");
        }

        // Date is valid and in the future, pass
        return ValidationResult.ValidResult;
    }
}
Public Class FutureDateRule
    Inherits ValidationRule

    Public Overrides Function Validate(value As Object, cultureInfo As CultureInfo) As ValidationResult

        Dim inputDate As Date

        ' Test if date is valid
        If Date.TryParse(value.ToString, inputDate) Then

            ' Date is not in the future, fail
            If Date.Now > inputDate Then
                Return New ValidationResult(False, "Please enter a date in the future.")
            End If

        Else
            ' // Date Is Not a valid date, fail
            Return New ValidationResult(False, "Value is not a valid date.")
        End If

        ' Date is valid and in the future, pass
        Return ValidationResult.ValidResult

    End Function

End Class

StartDateEntryFormTextBox používá toto FutureDateRule, jak je vidět v následujícím příkladu.

<TextBox Name="StartDateEntryForm" Grid.Row="3"
         Validation.ErrorTemplate="{StaticResource validationTemplate}" 
         Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5" Grid.ColumnSpan="2">
    <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged" 
                 Converter="{StaticResource dateConverter}" >
            <Binding.ValidationRules>
                <src:FutureDateRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Vzhledem k tomu, že hodnota UpdateSourceTrigger je PropertyChanged, modul vazeb aktualizuje hodnotu zdroje při každém stisknutí kláves, což znamená, že při každém stisknutí kláves také kontroluje každé pravidlo v kolekci ValidationRules. Tento postup si probereme později v části Proces ověřování.

Poskytnutí vizuální zpětné vazby

Pokud uživatel zadá neplatnou hodnotu, můžete chtít poskytnout zpětnou vazbu týkající se chyby v uživatelském rozhraní aplikace. Jedním ze způsobů, jak takovou zpětnou vazbu poskytnout, je nastavit přidruženou vlastnost Validation.ErrorTemplate na vlastní ControlTemplate. Jak je znázorněno v předchozí dílčí části, StartDateEntryFormTextBox používá ErrorTemplate s názvem validationTemplate. Následující příklad ukazuje definici validationTemplate.

<ControlTemplate x:Key="validationTemplate">
    <DockPanel>
        <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
        <AdornedElementPlaceholder/>
    </DockPanel>
</ControlTemplate>

Element AdornedElementPlaceholder určuje, kam má být označený ovládací prvek umístěn.

Kromě toho můžete také použít ToolTip k zobrazení chybové zprávy. StartDateEntryForm a StartPriceEntryFormTextBox používají styl textStyleTextBox, který vytvoří ToolTip zobrazující chybovou zprávu. Následující příklad ukazuje definici textStyleTextBox. Přidružená vlastnost Validation.HasError je true, když je jedna nebo více vazeb na vlastnosti vázaného prvku chybná.

<Style x:Key="textStyleTextBox" TargetType="TextBox">
    <Setter Property="Foreground" Value="#333333" />
    <Setter Property="MaxLength" Value="40" />
    <Setter Property="Width" Value="392" />
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" 
                    Value="{Binding (Validation.Errors).CurrentItem.ErrorContent, RelativeSource={RelativeSource Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

S vlastní ErrorTemplate a ToolTip bude StartPriceEntryFormTextBox vypadat při chybě ověřování jako následující rozhraní.

Data binding validation error for date

Pokud má Binding přidružená pravidla ověřování a vy nezadáte ErrorTemplate u vázaného ovládacího prvku, použije se k upozornění uživatelů na chybu ověřování výchozí ErrorTemplate. Výchozí ErrorTemplate je šablona ovládacího prvku, která definuje červené ohraničení ve vrstvě doplňku pro úpravy. S výchozí ErrorTemplate a ToolTip bude uživatelské rozhraní StartPriceEntryFormTextBox vypadat při chybě ověřování jako následující rozhraní.

Data binding validation error for price

Příklad poskytnutí logiky pro ověření všech ovládacích prvků v dialogovém okně najdete v části Vlastní dialogová okna v Přehledu dialogových oken.

Proces ověření

K ověření obvykle dochází, když je hodnota cíle přenesena do vlastnosti zdroje vazby. K tomuto přenosu dochází u vazeb TwoWay a OneWayToSource. Zopakujme si, že to, co způsobuje aktualizaci zdroje, závisí na hodnotě vlastnosti UpdateSourceTrigger, jak je popsáno v části Co aktivuje aktualizace zdroje.

Následující položky popisují proces ověření. Pokud kdykoli během tohoto procesu dojde k chybě ověření nebo jinému typu chyby, proces se zastaví:

  1. Modul vazeb zkontroluje, jestli jsou definovány nějaké vlastní objekty ValidationRule, jejichž ValidationStep je nastaven na RawProposedValue pro tuto Binding. V takovém případě volá metodu Validate pro každé ValidationRule, dokud u některého z nich nedojde k chybě nebo dokud všechny úspěšně neprojdou.

  2. Modul vazeb pak zavolá převaděč, pokud existuje.

  3. Pokud je převaděč úspěšný, modul vazeb zkontroluje, jestli jsou definovány nějaké vlastní objekty ValidationRule, jejichž ValidationStep je nastaven na ConvertedProposedValue pro tuto Binding. V takovém případě volá metodu Validate pro každé ValidationRule, které má ValidationStep nastavený na ConvertedProposedValue, dokud u některého z nich nedojde k chybě nebo dokud všechny úspěšně neprojdou.

  4. Modul vazeb nastaví vlastnost zdroje.

  5. Modul vazeb zkontroluje, jestli jsou definovány nějaké vlastní objekty ValidationRule, jejichž ValidationStep je nastaven na UpdatedValue pro tuto Binding. V takovém případě volá metodu Validate pro každé ValidationRule, které má ValidationStep nastavený na UpdatedValue, dokud u některého z nich nedojde k chybě nebo dokud všechny úspěšně neprojdou. Pokud je DataErrorValidationRule přidružené vazbě a jeho ValidationStep je nastavený na výchozí, UpdatedValue, bude se v tomto okamžiku kontrolovat DataErrorValidationRule. V tomto okamžiku se kontrolují všechny vazby, které mají ValidatesOnDataErrors nastavené na true.

  6. Modul vazeb zkontroluje, jestli jsou definovány nějaké vlastní objekty ValidationRule, jejichž ValidationStep je nastaven na CommittedValue pro tuto Binding. V takovém případě volá metodu Validate pro každé ValidationRule, které má ValidationStep nastavený na CommittedValue, dokud u některého z nich nedojde k chybě nebo dokud všechny úspěšně neprojdou.

Pokud ValidationRule během tohoto procesu neproběhne úspěšně, vytvoří modul vazby objekt ValidationError a přidá ho do kolekce Validation.Errors vázaného elementu. Dříve, než modul vazby spustí objekty ValidationRule v libovolném kroku, odebere všechny ValidationError přidané do přidružené vlastnosti Validation.Errors vázaného prvku během tohoto kroku. Pokud například ValidationRule, jehož ValidationStep je nastaven na UpdatedValue, selhalo, odebere modul vazeb před příštím procesem ověření tuto ValidationError bezprostředně před voláním libovolného ValidationRule, které má ValidationStep nastavený na UpdatedValue.

Pokud není Validation.Errors prázdná, nastaví se přidružená vlastnost Validation.HasError elementu na true. Pokud je vlastnost NotifyOnValidationError objektu Binding nastavená na true, pak modul vazby vyvolá u elementu přidruženou událost Validation.Error.

Všimněte si také, že platný přenos hodnot v obou směrech (cíl na zdroj nebo zdroj na cíl) vymaže přidruženou vlastnost Validation.Errors.

Pokud má vazba přidružené ExceptionValidationRule nebo měla vlastnost ValidatesOnExceptions nastavenou na true a při nastavování zdroje modul vazeb vyvolá výjimku, zkontroluje modul vazeb, jestli existuje UpdateSourceExceptionFilter. Zpětné volání UpdateSourceExceptionFilter můžete použít k poskytnutí vlastní obslužné rutiny pro zpracování výjimek. Pokud není určen UpdateSourceExceptionFilter pro Binding, modul vazby vytvoří ValidationError s výjimkou a přidá ji do kolekce Validation.Errors vázaného prvku.

Mechanismus ladění

Přidruženou vlastnost PresentationTraceSources.TraceLevel můžete nastavit u objektu souvisejícího s vazbou, abyste získali informace o stavu konkrétní vazby.

Viz také