Översikt över databindning (WPF .NET)
Databindning i Windows Presentation Foundation (WPF) är ett enkelt och konsekvent sätt för appar att presentera och interagera med data. Element kan bindas till data från olika typer av datakällor i form av .NET-objekt och XML. Alla ContentControl som Button och alla ItemsControl, till exempel ListBox och ListView, har inbyggda funktioner för att möjliggöra flexibel formatering av enskilda dataobjekt eller samlingar av dataobjekt. Sorterings-, filter- och gruppvyer kan genereras ovanpå data.
Databindningen i WPF har flera fördelar jämfört med traditionella modeller, inklusive inbyggt stöd för databindning med ett brett utbud av egenskaper, flexibel UI-representation av data och ren separation av affärslogik från användargränssnittet.
Den här artikeln beskriver först begrepp som är grundläggande för WPF-databindning och beskriver sedan användningen av Binding-klassen och andra funktioner i databindning.
Vad är databindning?
Databindning är den process som upprättar en anslutning mellan appens användargränssnitt och de data som visas. När datan ändrar sitt värde, om bindningen har rätt inställningar och datan ger rätt meddelanden, reflekterar de element som är bundna till datan förändringarna automatiskt. Databindning kan också innebära att om en yttre representation av data i ett element ändras kan underliggande data uppdateras automatiskt för att återspegla ändringen. Om användaren till exempel redigerar värdet i ett TextBox
-element uppdateras det underliggande datavärdet automatiskt för att återspegla ändringen.
En vanlig användning av databindning är att placera server- eller lokala konfigurationsdata i formulär eller andra användargränssnittskontroller. I WPF utökas det här konceptet till att omfatta bindning av ett brett utbud av egenskaper till olika typer av datakällor. I WPF kan beroendeegenskaper för element bindas till .NET-objekt (inklusive ADO.NET objekt eller objekt som är associerade med webbtjänster och webbegenskaper) och XML-data.
Grundläggande databindningsbegrepp
Oavsett vilket element du binder och vilken typ av datakälla du har följer varje bindning alltid den modell som illustreras av följande bild.
Som bilden visar är databindningen i princip bryggan mellan bindningsmålet och bindningskällan. Bilden visar följande grundläggande WPF-databindningsbegrepp:
Vanligtvis har varje bindning fyra komponenter:
- Ett bindande mål.
- En målfastighet.
- En bindningskälla.
- En sökväg till värdet i bindningskällan som ska användas.
Om du till exempel har bundit innehållet i en
TextBox
till egenskapenEmployee.Name
konfigurerar du bindningen som i följande tabell:Inställning Värde Mål TextBox Målfastighet Text Källobjekt Employee
Värdesökväg för källobjekt Name
Egenskapen som ska ändras måste vara en beroendeegenskap.
De flesta UIElement-egenskaper är beroendeegenskaper, och de flesta beroendeegenskaper, förutom de som är skrivskyddade, stöder databindning som standard. Endast typer som härleds från DependencyObject kan definiera beroendeegenskaper. Alla UIElement typer härleds från
DependencyObject
.Bindningskällor är inte begränsade till anpassade .NET-objekt.
Även om det inte visas i bilden bör det noteras att bindningskällans objekt inte är begränsat till att vara ett anpassat .NET-objekt. WPF-databindning stöder data i form av .NET-objekt, XML och till och med XAML-elementobjekt. Om du vill ange några exempel kan din bindningskälla vara en UIElement, ett listobjekt, ett ADO.NET- eller Web Services-objekt eller en XmlNode som innehåller dina XML-data. Mer information finns i översikten över bindningskällor.
Det är viktigt att komma ihåg att när du skapar en bindning, binder du ett bindningsmål till en bindningskälla. Om du till exempel visar underliggande XML-data i en ListBox med hjälp av databindning, binder du din ListBox
till XML-data.
Om du vill upprätta en bindning använder du objektet Binding. I resten av den här artikeln beskrivs många av de begrepp som är associerade med och några av egenskaperna och användningen av Binding
-objektet.
Datakontext
När databindning deklareras för XAML-element löser de databindningen genom att titta på deras omedelbara DataContext egenskap. Datakontexten är vanligtvis DataContext
för objektet som är värd för bindningen inte har angetts kontrolleras det överordnade elementets DataContext
-egenskap och så vidare fram till roten i XAML-objektträdet. Kort sagt, ärvs datakontexten som används för att lösa bindningen från föräldraobjektet om det inte uttryckligen anges på objektet.
Bindningar kan konfigureras för att matcha med ett specifikt objekt, i stället för att använda datakontexten för bindningsmatchning. Att ange ett källobjekt direkt används när du till exempel binder förgrundsfärgen för ett objekt till bakgrundsfärgen för ett annat objekt. Datakontext behövs inte eftersom bindningen löses mellan dessa två objekt. Omvänt använder bindningar som inte är bundna till specifika källobjekt datakontextmatchning.
När egenskapen DataContext
ändras utvärderas alla bindningar som kan påverkas av datakontexten.
Dataflödets riktning
Enligt pilen i föregående bild kan dataflödet för en bindning gå från bindningsmålet till bindningskällan (till exempel ändras källvärdet när en användare redigerar värdet för en TextBox
) och/eller från bindningskällan till bindningsmålet (till exempel uppdateras ditt TextBox
innehåll med ändringar i bindningskällan) om bindningskällan ger rätt meddelanden.
Du kanske vill att din app ska göra det möjligt för användare att ändra data och sprida dem tillbaka till källobjektet. Eller så kanske du inte vill att användarna ska kunna uppdatera källdata. Du kan styra dataflödet genom att ange Binding.Mode.
Den här bilden illustrerar de olika typerna av dataflöde:
OneWay bindning gör att ändringar i källegenskapen uppdaterar målegenskapen automatiskt, men ändringar i målegenskapen sprids inte tillbaka till källegenskapen. Den här typen av bindning är lämplig om kontrollen som binds är implicit skrivskyddad. Du kan till exempel binda till en källa som en aktieticker, eller så kan det hända att målegenskapen inte har något kontrollgränssnitt för att göra ändringar, till exempel en databunden bakgrundsfärg för en tabell. Om det inte finns något behov av att övervaka ändringarna av målegenskapen undviks belastningen genom att använda OneWay bindningsläget istället för TwoWay bindningsläget.
TwoWay bindning gör att ändringar i källegenskapen eller målegenskapen automatiskt uppdaterar den andra. Den här typen av bindning är lämplig för redigerbara formulär eller andra helt interaktiva användargränssnittsscenarier. De flesta egenskaper är som standard OneWay bindning, men vissa beroendeegenskaper (vanligtvis egenskaper för användarredigerbara kontroller som TextBox.Text och CheckBox.IsChecked standard för TwoWay bindning.
Ett programmatiskt sätt att avgöra om en beroendeegenskap binder enkelriktat eller dubbelriktat som standard är att hämta egenskapsmetadata med DependencyProperty.GetMetadata. Returtypen för den här metoden är PropertyMetadata, som inte innehåller några metadata om bindning. Men om den här typen kan omvandlas till den härledda FrameworkPropertyMetadatakan du kontrollera det booleska värdet för egenskapen FrameworkPropertyMetadata.BindsTwoWayByDefault. Följande kodexempel visar hur du hämtar metadata för egenskapen TextBox.Text:
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 är motsatsen till OneWay bindning. den uppdaterar källegenskapen när målegenskapen ändras. Ett exempelscenario är om du bara behöver omvärdera källvärdet från användargränssnittet.
Vad som inte visas på bilden är OneTime-bindningen, vilket gör att källegenskapen initierar målegenskapen men inte sprider vidare efterföljande ändringar. Om datakontexten ändras, eller om objektet i datakontexten ändras, återspeglas förändringen inte i målegenskapen. Den här typen av bindning är lämplig om antingen en ögonblicksbild av det aktuella tillståndet är lämplig eller om data verkligen är statiska. Den här typen av bindning är också användbar om du vill initiera målegenskapen med ett värde från en källegenskap och datakontexten inte är känd i förväg. Det här läget är i grunden en enklare form av OneWay bindning som ger bättre prestanda i fall där källvärdet inte ändras.
För att identifiera källändringar (gäller för OneWay- och TwoWay-bindningar) måste källan implementera en lämplig meddelandemekanism för egenskapsändring, till exempel INotifyPropertyChanged. Se How to: Implement property change notification (.NET Framework) för ett exempel på en INotifyPropertyChanged implementering.
Egenskapen Binding.Mode innehåller mer information om bindningslägen och ett exempel på hur du anger riktningen för en bindning.
Vad utlöser källuppdateringar
Bindningar som TwoWay eller OneWayToSource lyssnar efter ändringar i målegenskapen och sprider dem tillbaka till källan, vilket kallas för att uppdatera källan. Du kan till exempel redigera texten i en textruta för att ändra det underliggande källvärdet.
Men uppdateras källvärdet när du redigerar texten eller när du har redigerat texten och kontrollen tappar fokus? Egenskapen Binding.UpdateSourceTrigger avgör vad som utlöser uppdateringen av källan. Punkterna på pilarnas högra sida i följande bild illustrerar rollen för egenskapen Binding.UpdateSourceTrigger.
Om värdet för UpdateSourceTrigger
är UpdateSourceTrigger.PropertyChanged, uppdateras värdet som pekas ut med högerpilen på TwoWay eller OneWayToSource bindningarna så snart mål-egenskapen ändras. Men om värdet på UpdateSourceTrigger
är LostFocus, uppdateras endast värdet med det nya värdet när målegenskapen förlorar fokus.
På samma sätt som egenskapen Mode har olika beroendeegenskaper olika standardvärden UpdateSourceTrigger. Standardvärdet för de flesta beroendeegenskaper är PropertyChanged, vilket gör att källegenskapens värde omedelbart ändras när målegenskapsvärdet ändras. Omedelbara ändringar är bra för CheckBox och andra enkla kontroller. För textfält kan dock uppdatering efter varje tangenttryckning minska prestandan och neka användaren den vanliga möjligheten att backspace och åtgärda skrivfel innan du checkar in det nya värdet. Egenskapen TextBox.Text
är till exempel standardvärdet UpdateSourceTrigger
för LostFocus, vilket gör att källvärdet bara ändras när kontrollelementet förlorar fokus, inte när egenskapen TextBox.Text
ändras. Mer information om hur du hittar standardvärdet för en beroendeegenskap finns på egenskapssidan för UpdateSourceTrigger.
Följande tabell innehåller ett exempelscenario för varje UpdateSourceTrigger värde med hjälp av TextBox som exempel.
UpdateSourceTrigger-värde | När källvärdet uppdateras | Exempelscenario för TextBox |
---|---|---|
LostFocus (standard för TextBox.Text) |
När TextBox-kontrollen förlorar fokus. | En textruta som är associerad med valideringslogik (se dataverifiering nedan). |
PropertyChanged |
När du skriver in i TextBox. | Textbox-kontroller i ett chattrumsfönster. |
Explicit |
När appen anropar UpdateSource. | Textbox-kontroller i ett redigerbart formulär (uppdaterar endast källvärdena när användaren trycker på knappen Skicka). |
För ett exempel, se Hur man: Kontrollerar när TextBox-texten uppdaterar källan (.NET Framework).
Exempel på databindning
Ett exempel på databindning finns i följande appgränssnitt från demoappen Data Binding Demo, som visar en lista över auktionsobjekt.
skärmbild
Appen visar följande funktioner för databindning:
Innehållet i ListBox är bundet till en samling AuctionItem objekt. Ett AuctionItem- objekt har egenskaper som Description, StartPrice, StartDate, Categoryoch SpecialFeatures.
Data (AuctionItem objekt) som visas i
ListBox
mallas så att beskrivningen och det aktuella priset visas för varje objekt. Mallen skapas med hjälp av en DataTemplate. Dessutom beror utseendet på varje objekt på värdet på SpecialFeatures för det visade AuctionItem. Om SpecialFeatures värdet för AuctionItem är Colorhar objektet en blå kantlinje. Om värdet är Markerahar objektet en orange kantlinje och en stjärna. Avsnittet Data templating innehåller information om data templating.Användaren kan gruppera, filtrera eller sortera data med hjälp av
CheckBoxes
som tillhandahålls. På bilden ovan har Gruppera efter kategori och Sortera efter kategori och datumCheckBoxes
valts. Du kanske har märkt att data grupperas baserat på produktkategorin och att kategorinamnet är i alfabetisk ordning. Det är svårt att märka från bilden, men objekten sorteras också efter startdatumet i varje kategori. Sortering görs med hjälp av en samlingsvy. Avsnittet Bindning till samlingar beskriver samlingsvyer.När användaren väljer ett objekt visar ContentControl information om det markerade objektet. Den här upplevelsen kallas för "Master-detail scenario". Avsnittet Master-detail scenario innehåller information om den här typen av bindning.
Typen av egenskapen StartDate är DateTime, som returnerar ett datum som inkluderar tiden till millisekunderna. I den här appen har en anpassad konverterare använts så att en kortare datumsträng visas. Avsnittet Datakonvertering innehåller information om konverterare.
När användaren väljer knappen Lägg till produkt visas följande formulär.
Användaren kan redigera fälten i formuläret, förhandsgranska produktlistan med hjälp av de korta eller detaljerade förhandsgranskningsrutorna och välja Submit
för att lägga till den nya produktlistan. Alla befintliga inställningar för gruppering, filtrering och sortering gäller för den nya posten. I det här fallet visas objektet som anges i bilden ovan som det andra objektet i kategorin Dator.
Det som inte visas i den här bilden är den angivna valideringslogiken i StartdatumTextBox. Om användaren anger ett ogiltigt datum (ogiltig formatering eller ett tidigare datum) meddelas användaren med en ToolTip och ett rött utropstecken bredvid TextBox. I avsnittet dataverifiering beskrivs hur du skapar valideringslogik.
Innan vi går in på de olika funktionerna i databindningen som beskrivs ovan ska vi först diskutera de grundläggande begrepp som är viktiga för att förstå WPF-databindning.
Skapa en bindning
Om du vill upprepa några av de begrepp som beskrivs i föregående avsnitt upprättar du en bindning med hjälp av Binding-objektet, och varje bindning har vanligtvis fyra komponenter: ett bindningsmål, en målegenskap, en bindningskälla och en sökväg till det källvärde som ska användas. I det här avsnittet beskrivs hur du konfigurerar en bindning.
Bindningskällor är kopplade till den aktiva DataContext för elementet. Elementen ärver automatiskt sitt DataContext
om de inte uttryckligen har definierat ett.
Tänk på följande exempel, där bindningskällobjektet är en klass med namnet MyData som definieras i SDKSample namnområde. I demonstrationssyfte har MyData en strängegenskap med namnet ColorName vars värde är inställt på "Red". Det här exemplet genererar därför en knapp med röd bakgrund.
<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>
Mer information om syntaxen för bindningsdeklarationen och exempel på hur du konfigurerar en bindning i kod finns i Översikt över bindningsdeklarationer.
Om vi använder det här exemplet på vårt grundläggande diagram ser den resulterande figuren ut så här. Den här bilden beskriver en OneWay bindning eftersom egenskapen Background stöder OneWay bindning som standard.
Du kanske undrar varför den här bindningen fungerar även om egenskapen ColorName är av typen string medan egenskapen Background är av typen Brush. Den här bindningen använder standardtypkonvertering, som beskrivs i avsnittet Datakonvertering.
Ange bindningskällan
Observera att i föregående exempel anges bindningskällan genom att ange egenskapen DockPanel.DataContext. Button ärver sedan värdet DataContext från DockPanel, som är dess överordnade element. För att upprepa är bindningskällan ett av de fyra nödvändiga komponenterna i en bindning. Så utan att bindningskällans objekt har angetts skulle bindningen inte göra någonting.
Det finns flera sätt att ange bindningskällans objekt. Det är användbart att använda egenskapen DataContext på ett överordnat element när du binder flera egenskaper till samma källa. Ibland kan det dock vara lämpligare att ange bindningskällan för enskilda bindningsdeklarationer. I föregående exempel kan du i stället för att använda egenskapen DataContext ange bindningskällan genom att ange egenskapen Binding.Source direkt på knappens bindningsdeklaration, som i följande exempel.
<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>
Förutom att ange egenskapen DataContext på ett element direkt, ärver DataContext värdet från en överordnad (till exempel knappen i det första exemplet) och uttryckligen anger bindningskällan genom att ange egenskapen Binding.Source på bindningen (till exempel knappen i det sista exemplet), kan du också använda egenskapen Binding.ElementName eller egenskapen Binding.RelativeSource för att ange bindningskällan. Egenskapen ElementName är användbar när du binder till andra element i din app, till exempel när du använder ett skjutreglage för att justera bredden på en knapp. Egenskapen RelativeSource är användbar när bindningen anges i en ControlTemplate eller en Style. Mer information finns i översikten över bindningskällor.
Ange sökvägen till värdet
Om bindningskällan är ett objekt använder du egenskapen Binding.Path för att ange det värde som ska användas för bindningen. Om du binder till XML-data använder du egenskapen Binding.XPath för att ange värdet. I vissa fall kan det vara tillämpligt att använda egenskapen Path även när dina data är XML. Om du till exempel vill komma åt egenskapen Name för en returnerad XmlNode (som ett resultat av en XPath-fråga) bör du använda egenskapen Path utöver egenskapen XPath.
Mer information finns i egenskaperna Path och XPath.
Även om vi har betonat att Path till det värde som ska användas är en av de fyra nödvändiga komponenterna i en bindning, skulle värdet som ska användas i de scenarier som du vill binda till ett helt objekt vara detsamma som bindningskällans objekt. I dessa fall gäller det att inte ange en Path. Tänk dig följande exempel.
<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="true"/>
I exemplet ovan används den tomma bindningssyntaxen: {Binding}. I det här fallet ärver ListBox DataContext från ett överordnat DockPanel-element (visas inte i det här exemplet). När sökvägen inte har angetts är standardvärdet att binda till hela objektet. Med andra ord har sökvägen utelämnats i det här exemplet eftersom vi binder egenskapen ItemsSource till hela objektet. (I avsnittet Bindning till samlingar finns en ingående diskussion.)
Förutom bindning till en samling är det här scenariot också användbart när du vill binda till ett helt objekt i stället för bara en enda egenskap för ett objekt. Om källobjektet till exempel är av typen Stringkanske du helt enkelt vill binda till själva strängen. Ett annat vanligt scenario är när du vill binda ett element till ett objekt med flera egenskaper.
Du kan behöva tillämpa anpassad logik så att data är meningsfull för din bundna målparameter. Den anpassade logiken kan vara i form av en anpassad konverterare om standardtypkonvertering inte finns. Se Datakonvertering för information om konverterare.
Bindning och Bindningsuttryck
Innan du går in på andra funktioner och användningar av databindning är det bra att introducera klassen BindingExpression. Som du har sett i föregående avsnitt är klassen Binding högnivåklassen för deklarationen av en bindning. Den innehåller många egenskaper som gör att du kan ange egenskaperna för en bindning. En relaterad klass, BindingExpression, är det underliggande objektet som underhåller anslutningen mellan källan och målet. En bindning innehåller all information som kan delas mellan flera bindningsuttryck. En BindingExpression är ett instansuttryck som inte kan delas och som innehåller all instansinformation för Binding.
Tänk på följande exempel, där myDataObject
är en instans av klassen MyData
, myBinding
är källobjektet Binding och MyData
är en definierad klass som innehåller en strängegenskap med namnet ColorName
. Det här exemplet binder textinnehållet i myText
, en instans av TextBlock, till 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)
Du kan använda samma myBinding--objekt för att skapa andra bindningar. Du kan till exempel använda objektet myBinding för att binda textinnehållet i en kryssruta till ColorName-. I det scenariot finns det två instanser av BindingExpression som delar objektet myBinding.
Ett BindingExpression objekt returneras genom att anropa GetBindingExpression på ett databundet objekt. Följande artiklar visar några av användningarna för klassen BindingExpression:
- Hämta bindningsobjektet från en bunden mål-egenskap (.NET Framework)
- Kontroll När textrutan uppdaterar källan (.NET Framework)
Datakonvertering
I avsnittet Skapa en bindning är knappen röd eftersom dess Background egenskap är bunden till en strängegenskap med värdet "Red". Det här strängvärdet fungerar eftersom en typkonverterare finns på den Brush typen för att konvertera strängvärdet till en Brush.
Att lägga till den här informationen i bilden i avsnittet Skapa en bindning ser ut så här.
Men tänk om bindningskällans objekt i stället för att ha en egenskap av typen string har en Color-egenskap av typen Color? För att bindningen ska fungera måste du i så fall först omvandla egenskapsvärdet Color till något som Background-egenskapen accepterar. Du skulle behöva skapa en anpassad konverterare genom att implementera IValueConverter-gränssnittet, som i följande exempel.
[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
Mer information finns i IValueConverter.
Nu används den anpassade konverteraren i stället för standardkonvertering, och vårt diagram ser ut så här.
För att upprepa kan standardkonverteringar vara tillgängliga på grund av typkonverterare som finns i den typ som man är bunden till. Det här beteendet kommer att bero på vilka typomvandlare som är tillgängliga i målmiljön. Om du är osäker skapar du en egen konverterare.
Följande är några typiska scenarier där det är klokt att implementera en datakonverterare:
Dina data bör visas på olika sätt, beroende på kultur. Du kanske till exempel vill implementera en valutakonverterare eller en kalenderdatum/tid-konverterare baserat på de konventioner som används i en viss kultur.
De data som används är inte nödvändigtvis avsedda att ändra textvärdet för en egenskap, utan är i stället avsett att ändra något annat värde, till exempel källan för en bild eller visningstextens färg eller format. Konverterare kan användas i den här instansen genom att konvertera bindningen av en egenskap som kanske inte verkar vara lämplig, till exempel att binda ett textfält till egenskapen Background i en tabellcell.
Mer än en kontroll eller flera egenskaper för kontroller är bundna till samma data. I det här fallet kan den primära bindningen bara visa texten, medan andra bindningar hanterar specifika visningsproblem men fortfarande använder samma bindning som källinformation.
En målegenskap har en uppsättning bindningar, som kallas MultiBinding. För MultiBindinganvänder du en anpassad IMultiValueConverter för att skapa ett slutligt värde från värdena för bindningarna. Färg kan till exempel beräknas från röda, blå och gröna värden, som kan vara värden från samma eller olika bindningskällans objekt. Exempel och information finns i MultiBinding.
Bindning till samlingar
Ett bindningskällobjekt kan antingen behandlas som ett enskilt objekt vars egenskaper innehåller data eller som en datainsamling av polymorfa objekt som ofta grupperas tillsammans (till exempel resultatet av en fråga till en databas). Hittills har vi bara diskuterat bindning till enskilda objekt. Bindning till en datainsamling är dock ett vanligt scenario. Ett vanligt scenario är till exempel att använda en ItemsControl som en ListBox, ListVieweller TreeView för att visa en datainsamling, till exempel i appen som visas i avsnittet Vad är databindning.
Som tur är gäller vårt grundläggande diagram fortfarande. Om du binder en ItemsControl till en samling ser diagrammet ut så här.
Som du ser i det här diagrammet, för att binda en ItemsControl till ett samlingsobjekt, är ItemsControl.ItemsSource egenskap den egenskap som ska användas. Du kan se ItemsSource
som innehållet i ItemsControl. Bindningen är OneWay eftersom egenskapen ItemsSource
stöder OneWay
bindning som standard.
Så här implementerar du samlingar
Du kan räkna upp alla samlingar som implementerar IEnumerable-gränssnittet. Men för att konfigurera dynamiska bindningar så att infogningar eller borttagningar i samlingen uppdaterar användargränssnittet automatiskt måste samlingen implementera INotifyCollectionChanged-gränssnittet. Det här gränssnittet exponerar en händelse som ska aktiveras när den underliggande samlingen ändras.
WPF tillhandahåller klassen ObservableCollection<T>, som är en inbyggd implementering av en datainsamling som exponerar INotifyCollectionChanged-gränssnittet. För att fullständigt stödja överföring av datavärden från källobjekt till mål måste varje objekt i samlingen som stöder bindbara egenskaper även implementera INotifyPropertyChanged-gränssnittet. Mer information finns i översikten över bindningskällor.
Innan du implementerar din egen samling bör du överväga att använda ObservableCollection<T> eller någon av de befintliga samlingsklasserna, till exempel List<T>, Collection<T>och BindingList<T>, bland många andra. Om du har ett avancerat scenario och vill implementera din egen samling bör du överväga att använda IList, som tillhandahåller en icke-allmän samling objekt som kan nås individuellt av indexet och därmed ger bästa möjliga prestanda.
Samlingsvyer
När din ItemsControl är bunden till en datainsamling kanske du vill sortera, filtrera eller gruppera data. För att göra det använder du samlingsvyer, som är klasser som implementerar ICollectionView-gränssnittet.
Vad är samlingsvyer?
En samlingsvy är ett lager ovanpå en bindningskälla som gör att du kan navigera och visa källsamlingen baserat på sorterings-, filter- och gruppfrågor, utan att behöva ändra själva den underliggande källsamlingen. En samlingsvy behåller också en pekare till det aktuella objektet i samlingen. Om källsamlingen implementerar INotifyCollectionChanged-gränssnittet sprids ändringarna som genereras av händelsen CollectionChanged till vyerna.
Eftersom vyer inte ändrar de underliggande källsamlingarna kan varje källsamling ha flera vyer associerade med den. Till exempel kan du ha en samling av uppgift objekt. Med hjälp av vyer kan du visa samma data på olika sätt. Till exempel kanske du till vänster på sidan vill visa aktiviteter sorterade efter prioritet, och till höger grupperade efter område.
Så här skapar du en vy
Ett sätt att skapa och använda en vy är att instansiera visningsobjektet direkt och sedan använda det som bindningskälla. Överväg till exempel demoappen Databindningsdemoapp som visas i avsnittet Vad är databindning. Appen implementeras så att ListBox binder till en vy över datainsamlingen i stället för datainsamlingen direkt. Följande exempel har extraherats från demo för databindning app. Klassen CollectionViewSource är XAML-proxyn för en klass som ärver från CollectionView. I det här exemplet är vyns Source bunden till samlingen AuctionItems (av typen ObservableCollection<T>) för det aktuella appobjektet.
<Window.Resources>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"
x:Key="listingDataView" />
</Window.Resources>
Resursen listingDataView fungerar sedan som bindningskälla för element i appen, till exempel ListBox.
<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
ItemsSource="{Binding Source={StaticResource listingDataView}}" />
Om du vill skapa en annan vy för samma samling kan du skapa en annan CollectionViewSource instans och ge den ett annat x:Key
namn.
I följande tabell visas vilka vydatatyper som skapas som standardsamlingsvy eller av CollectionViewSource baserat på källsamlingstypen.
Typ av källsamling | Samlingsvytyp | Anteckningar |
---|---|---|
IEnumerable | En intern typ baserad på CollectionView | Det går inte att gruppera objekt. |
IList | ListCollectionView | Snabbast. |
IBindingList | BindingListCollectionView |
Använda en standardvy
Att ange en samlingsvy som bindningskälla är ett sätt att skapa och använda en samlingsvy. WPF skapar också en standardsamlingsvy för varje samling som används som bindningskälla. Om du binder direkt till en samling binder WPF till standardvyn. Den här standardvyn delas av alla bindningar till samma samling, så en ändring som görs i en standardvy av en bunden kontroll eller kod (till exempel sortering eller en ändring av den aktuella objektpekaren, som beskrivs senare) återspeglas i alla andra bindningar till samma samling.
Om du vill hämta standardvyn använder du metoden GetDefaultView. Ett exempel finns i Hämta standardvyn för en datainsamling (.NET Framework).
Samlingsvyer med ADO.NET DataTables
För att förbättra prestanda delegerar samlingsvyer för ADO.NET DataTable eller DataView objekt sortering och filtrering till DataView, vilket gör att sortering och filtrering delas i alla samlingsvyer i datakällan. Om du vill att varje samlingsvy ska kunna sortera och filtrera separat initierar du varje samlingsvy med ett eget DataView objekt.
Sortering
Som tidigare nämnts kan vyer tillämpa en sorteringsordning på en samling. Eftersom dina data finns i den underliggande samlingen kan de ha eller sakna en relevant, inbyggd ordning. Med vyn över samlingen kan du införa en order eller ändra standardordningen baserat på de jämförelsevillkor som du anger. Eftersom det är en klientbaserad vy av data är ett vanligt scenario att användaren kanske vill sortera kolumner med tabelldata efter det värde som kolumnen motsvarar. Med hjälp av vyer kan den här användardrivna sorteringen tillämpas igen utan att göra några ändringar i den underliggande samlingen eller ens behöva fråga efter samlingsinnehållet. Ett exempel finns i Sortera en GridView-kolumn när en rubrik klickas på (.NET Framework).
I följande exempel visas sorteringslogik i avsnittet "Sortera efter kategori och datum" CheckBox i appgränssnittet i avsnittet Vad är databindning.
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
Filtrering
Vyer kan också använda ett filter för en samling, så att vyn endast visar en viss delmängd av den fullständiga samlingen. Du kan filtrera efter ett villkor i data. Som appen gör i avsnittet Vad är databindning, innehåller “Visa endast fynd” CheckBox logik för att filtrera bort artiklar som kostar 25 USD eller mer. Följande kod körs för att ange ShowOnlyBargainsFilter som Filter händelsehanterare när den CheckBox har valts.
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
Händelsehanteraren ShowOnlyBargainsFilter har följande implementering.
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
Om du använder någon av de CollectionView klasserna direkt i stället för CollectionViewSourceanvänder du egenskapen Filter för att ange ett återanrop. Ett exempel finns i Filtrera data i en vy (.NET Framework).
Gruppering
Förutom den interna klass som visar en IEnumerable samling stöder alla samlingsvyer gruppering, vilket gör att användaren kan partitionera samlingen i samlingsvyn i logiska grupper. Grupperna kan vara explicita, där användaren tillhandahåller en lista över grupper, eller implicit, där grupperna genereras dynamiskt beroende på data.
I följande exempel visas logiken i "Gruppera efter kategori" 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)
Ett annat grupperingsexempel finns i Gruppobjekt i en ListView som implementerar en GridView(.NET Framework).
Aktuella objektpekare
Vyer stöder också idén om ett aktuellt objekt. Du kan navigera genom objekten i en samlingsvy. När du navigerar flyttar du en objektpekare som gör att du kan hämta objektet som finns på den specifika platsen i samlingen. Ett exempel finns i Navigera genom objekten i en datainsamlingsvy (.NET Framework).
Eftersom WPF endast binder till en samling med hjälp av en vy (antingen en vy som du anger eller samlingens standardvy) har alla bindningar till samlingar en aktuell objektpekare. Vid bindning till en vy anger snedstreckstecknet ("/") i ett Path
värde det aktuella objektet i vyn. I följande exempel är datakontexten en samlingsvy. Den första raden kopplas till samlingen. Den andra raden binder till det aktuella objektet i samlingen. Den tredje raden binder till egenskapen Description
för det aktuella objektet i samlingen.
<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" />
Snedstrecks- och egenskapssyntaxen kan också staplas för att traversera en hierarki av samlingar. Följande exempel binder till det aktuella objektet i en samling med namnet Offices
, som är en egenskap för det aktuella objektet i källsamlingen.
<Button Content="{Binding /Offices/}" />
Den aktuella objektpekaren kan påverkas av sortering eller filtrering som tillämpas på samlingen. Sortering bevarar den aktuella objektpekaren på det senaste markerade objektet, men samlingsvyn är nu omstrukturerad runt den. (Det markerade objektet kanske var i början av listan tidigare, men nu kan det markerade objektet finnas någonstans i mitten.) Filtreringen bevarar det markerade objektet om markeringen förblir i vyn efter filtreringen. Annars är den aktuella objektpekaren inställd på det första objektet i den filtrerade samlingsvyn.
Scenario för huvudinformationsbindning
Begreppet aktuellt objekt är användbart inte bara för navigering av objekt i en samling, utan även för huvudinformationsbindningsscenariot. Titta på appgränssnittet i avsnittet Vad är databindning igen. I den appen avgör markeringen i ListBox innehållet som visas i ContentControl. Om du vill uttrycka det på ett annat sätt, när ett ListBox objekt är markerat, visar ContentControl information om det markerade objektet.
Du kan enkelt implementera ett master-detail-scenario genom att ha två eller flera kontroller bundna till samma vy. Följande exempel från demoappen Databindningsdemo visar markeringen av ListBox och de ContentControl du ser i appgränssnittet i avsnittet Vad är Databindning.
<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"/>
Observera att båda kontrollerna är bundna till samma källa, listingDataView statisk resurs (se definitionen av den här resursen i Skapa ett vyavsnitt). Den här bindningen fungerar eftersom när ett objekt (ContentControl i det här fallet) är bundet till en samlingsvy, binder det automatiskt till vyns CurrentItem. De CollectionViewSource objekten synkroniserar automatiskt valuta och val. Om listkontrollen inte är bunden till ett CollectionViewSource objekt som i det här exemplet måste du ange dess IsSynchronizedWithCurrentItem egenskap till true
för att detta ska fungera.
Andra exempel finns i Bind till en samling och visa information baserat på val (.NET Framework) och Använd master-detail-mönstret med hierarkiska data (.NET Framework).
Du kanske har märkt att exemplet ovan använder en mall. I själva verket skulle data inte visas som vi vill utan användning av mallar (den som uttryckligen används av ContentControl och den som implicit används av ListBox). Vi går nu över till data templating i nästa avsnitt.
Data templating
Utan användning av datamallar skulle vårt appgränssnitt i avsnittet Exempel på databindning se ut så här:
Som du ser i exemplet i föregående avsnitt är både ListBox-kontrollen och ContentControl bundna till hela samlingsobjektet (eller mer specifikt vyn över samlingsobjektet) för AuctionItems. Utan specifika instruktioner för hur du visar datainsamlingen visar ListBox strängrepresentationen av varje objekt i den underliggande samlingen, och ContentControl visar strängrepresentationen av objektet som det är bundet till.
För att lösa problemet definierar appen DataTemplates. Som visas i exemplet i föregående avsnitt använder ContentControl explicit detailsProductListingTemplate datamall. ListBox-kontrollen använder implicit följande datamall när AuctionItem objekt i samlingen visas.
<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>
Med hjälp av dessa två DataTemplates är det resulterande användargränssnittet det som visas i avsnittet Vad är databindning. Som du kan se från skärmbilden kan du, förutom att låta dig placera data i dina kontroller, använda DataTemplates för att definiera övertygande visuella objekt för dina data. Till exempel används DataTriggers i ovanstående DataTemplate så att AuctionItems med SpecialFeatures värdet HighLight visas med en orange kantlinje och en stjärna.
Mer information om datamallar finns i Översikt över datamallar (.NET Framework).
Datavalidering
De flesta appar som använder användarindata måste ha valideringslogik för att säkerställa att användaren har angett den förväntade informationen. Verifieringskontrollerna kan baseras på typ, intervall, format eller andra appspecifika krav. I det här avsnittet beskrivs hur dataverifiering fungerar i WPF.
Associera verifieringsregler med en bindning
Med WPF-databindningsmodellen kan du associera ValidationRules med ditt Binding-objekt. Följande exempel binder till exempel en TextBox till en egenskap med namnet StartPrice
och lägger till ett ExceptionValidationRule objekt i egenskapen 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>
Ett ValidationRule-objekt kontrollerar om värdet för en egenskap är giltigt. WPF har två typer av inbyggda ValidationRule objekt:
En ExceptionValidationRule söker efter undantag som utlöses vid uppdatering av bindningskällans egenskap. I föregående exempel är
StartPrice
av typen heltal. När användaren anger ett värde som inte kan konverteras till ett heltal utlöses ett undantag, vilket gör att bindningen markeras som ogiltig. En alternativ syntax för att uttryckligen ange ExceptionValidationRule är att ange egenskapen ValidatesOnExceptions tilltrue
på ditt Binding- eller MultiBinding-objekt.Ett DataErrorValidationRule objekt söker efter fel som genereras av objekt som implementerar IDataErrorInfo-gränssnittet. Mer information om hur du använder den här verifieringsregeln finns i DataErrorValidationRule. En alternativ syntax för att uttryckligen ange DataErrorValidationRule är att ange egenskapen ValidatesOnDataErrors till
true
på ditt Binding- eller MultiBinding-objekt.
Du kan också skapa en egen valideringsregel genom att härleda från klassen ValidationRule och implementera metoden Validate. I följande exempel visas regeln som används av Lägg till produktlista "Startdatum" TextBox från avsnittet Vad är databindning.
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 använder den här FutureDateRule, som du ser i följande exempel.
<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>
Eftersom UpdateSourceTrigger värdet är PropertyChangeduppdaterar bindningsmotorn källvärdet vid varje tangenttryckning, vilket innebär att den även kontrollerar varje regel i samlingen ValidationRules på varje tangenttryckning. Vi diskuterar detta ytterligare i avsnittet Valideringsprocess.
Erbjud visuell feedback
Om användaren anger ett ogiltigt värde kanske du vill ge feedback om felet i appens användargränssnitt. Ett sätt att ge sådan feedback är att ange den kopplade egenskapen Validation.ErrorTemplate till en anpassad ControlTemplate. Som du ser i föregående underavsnitt använder StartDateEntryFormTextBox en ErrorTemplate med namnet validationTemplate. I följande exempel visas definitionen av validationTemplate.
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
Elementet AdornedElementPlaceholder anger var den dekorerade kontrollen ska placeras.
Dessutom kan du också använda en ToolTip för att visa felmeddelandet. Både StartDateEntryForm och StartPriceEntryFormTextBoxes använder formatet textStyleTextBox, som skapar en ToolTip som visar felmeddelandet. I följande exempel visas definitionen av textStyleTextBox. Den bifogade egenskapen Validation.HasError är true
när en eller flera bindningar för egenskaperna för det bundna elementet är fel.
<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>
Med den anpassade ErrorTemplate och ToolTipser StartDateEntryFormTextBox ut så här när det finns ett valideringsfel.
Om din Binding har associerade valideringsregler men du inte anger någon ErrorTemplate på den bundna kontrollen används en standard ErrorTemplate för att meddela användarna när det finns ett verifieringsfel. Standard ErrorTemplate är en kontrollmall som definierar en röd kantlinje i utsmyckningsskiktet. Med standard-ErrorTemplate och ToolTipser användargränssnittet för StartPriceEntryFormTextBox ut som följande när det finns ett verifieringsfel.
Ett exempel på hur du tillhandahåller logik för att verifiera alla kontroller i en dialogruta finns i avsnittet Anpassade dialogrutor i Dialogrutor översikt.
Valideringsprocessen
Verifiering sker vanligtvis när värdet för ett mål överförs till bindningskällans egenskap. Den här överföringen sker på TwoWay- och OneWayToSource bindningar. Vad som orsakar en källuppdatering beror på värdet för egenskapen UpdateSourceTrigger, enligt beskrivningen i avsnittet Vad utlöser källuppdateringar.
Följande punkter beskriver valideringens process. Om ett valideringsfel eller någon annan typ av fel inträffar när som helst under den här processen stoppas processen:
Bindningsmotorn kontrollerar om det finns några anpassade ValidationRule objekt definierade vars ValidationStep är inställd på RawProposedValue för den Binding, i vilket fall den anropar metoden Validate på varje ValidationRule tills en av dem får ett fel eller tills alla passerar.
Bindningsmotorn anropar sedan konverteraren, om det finns en sådan.
Om konverteraren lyckas kontrollerar bindningsmotorn om det finns några anpassade ValidationRule objekt som har definierats vars ValidationStep är inställd på ConvertedProposedValue för den Binding, i vilket fall den anropar metoden Validate på varje ValidationRule som har ValidationStep inställd på ConvertedProposedValue tills någon av dem stöter på ett fel eller tills alla passerar.
Bindningsmotorn anger källegenskapen.
Bindningsmotorn kontrollerar om det finns några anpassade ValidationRule objekt som definierats vars ValidationStep är inställd på UpdatedValue för den Binding, i vilket fall den anropar metoden Validate på varje ValidationRule som har ValidationStep inställd på UpdatedValue tills en av dem stöter på ett fel eller tills alla passerar. Om en DataErrorValidationRule är associerad med en bindning och dess ValidationStep är inställd på standardvärdet UpdatedValuekontrolleras DataErrorValidationRule nu. Nu kontrolleras alla bindningar som har ValidatesOnDataErrors inställda på
true
.Bindningsmotorn kontrollerar om det finns några anpassade ValidationRule objekt som definierats vars ValidationStep är inställd på CommittedValue för den Binding, i vilket fall den anropar metoden Validate på varje ValidationRule som har ValidationStep inställd på CommittedValue tills en av dem stöter på ett fel eller tills alla passerar.
Om en ValidationRule inte passerar vid någon tidpunkt genom hela denna process, skapar bindningsmotorn ett ValidationError-objekt och lägger till det i den Validation.Errors-samlingen av det bundna elementets. Innan bindningsmotorn kör ValidationRule-objekten i ett visst steg, tar den bort alla ValidationError som lades till i den Validation.Errors-anslutna egenskapen för det bundna elementet under det steget. Om till exempel en ValidationRule vars ValidationStep är inställd på UpdatedValue misslyckades tar bindningsmotorn bort den ValidationError omedelbart innan den anropar någon ValidationRule som har ValidationStep inställd på UpdatedValue.
När Validation.Errors inte är tom anges elementets Validation.HasError anslutna egenskap till true
. Om egenskapen NotifyOnValidationError för Binding är inställd på true
genererar bindningsmotorn dessutom den Validation.Error kopplade händelsen för elementet.
Observera också att en giltig värdeöverföring i endera riktningen (mål till källa eller källa till mål) rensar den egenskap som är kopplad till Validation.Errors.
Om bindningen antingen har en ExceptionValidationRule associerad med den eller om egenskapen ValidatesOnExceptions är inställd på true
och ett undantag utlöses när bindningsmotorn anger källan, kontrollerar bindningsmotorn om det finns en UpdateSourceExceptionFilter. Du kan använda UpdateSourceExceptionFilter återanrop för att tillhandahålla en anpassad hanterare för hantering av undantag. Om en UpdateSourceExceptionFilter inte anges i Bindingskapar bindningsmotorn en ValidationError med undantaget och lägger till den i den Validation.Errors samlingen av det bundna elementet.
Felsökningsmekanism
Du kan ange den bifogade egenskapen PresentationTraceSources.TraceLevel på ett bindningsrelaterat objekt för att ta emot information om status för en specifik bindning.
Se även
- Databindningsdemo
- översikt över bindningsdeklarationer
- översikt av bindningskällor
- DataErrorValidationRule
.NET Desktop feedback