wpf
Inleiding tot WPF-gegevensbinding
Zoeken…
Syntaxis
- {Binding PropertyName} is gelijk aan {Binding Path = PropertyName}
- {Binding Path = SomeProperty.SomeOtherProperty.YetAnotherProperty}
- {Binding Path = SomeListProperty [1]}
parameters
Parameter | Details |
---|---|
Pad | Hiermee geeft u het pad op waaraan u wilt binden. Indien niet gespecificeerd, bindt aan de DataContext zelf. |
UpdateSourceTrigger | Geeft aan wanneer de waarde van de bindende bron wordt bijgewerkt. Standaard ingesteld op LostFocus . De meest gebruikte waarde is PropertyChanged . |
mode | Meestal OneWay of TwoWay . Indien niet gespecificeerd door de binding, wordt standaard OneWay tenzij het bindende doel vraagt om TwoWay . Er treedt een fout op wanneer TwoWay wordt gebruikt om een readonly-eigenschap te binden, bijv. OneWay moet expliciet worden ingesteld bij het binden van een readonly- TextBox.Text aan TextBox.Text . |
Bron | Staat het gebruik van een StaticResource als een bindende bron in plaats van de huidige DataContext. |
RelativeSource | Staat het gebruik van een ander XAML-element toe als een bindende bron in plaats van de huidige DataContext. |
ElementName | Staat het gebruik van een benoemd XAML-element toe als een bindende bron in plaats van de huidige DataContext. |
FallbackValue | Als de binding mislukt, wordt deze waarde aan het bindende doel gegeven. |
TargetNullValue | Als de bindende bronwaarde null , wordt deze waarde aan het bindende doel verstrekt. |
omvormer | Hiermee geeft u de converter StaticResource die wordt gebruikt om de waarde van de binding te converteren, bijvoorbeeld een Boolean naar een Enum-item voor Visibility omzetten. |
ConverterParameter | Hiermee geeft u een optionele parameter op die aan de converter moet worden verstrekt. Deze waarde moet statisch zijn en kan niet worden gebonden. |
StringFormat | Hiermee geeft u een opmaakreeks op die moet worden gebruikt bij het weergeven van de gebonden waarde. |
Vertraging | (WPF 4.5+) Geeft een vertraging in milliseconds voor de binding om de BindingSource in het ViewModel . Dit moet worden gebruikt met Mode=TwoWay en UpdateSourceTrigger=PropertyChanged om van kracht te worden. |
Opmerkingen
UpdateSourceTrigger
Standaard werkt WPF de bindende bron bij wanneer het besturingselement de focus verliest. Als er echter maar één besturingselement is dat focus kan krijgen - iets dat gebruikelijk is in voorbeelden - moet u UpdateSourceTrigger=PropertyChanged
opgeven om de updates te laten werken.
U zult PropertyChanged
willen gebruiken als de trigger voor veel tweerichtingsbindingen, tenzij het bijwerken van de bindende bron bij elke toetsaanslag duur is of live gegevensvalidatie ongewenst is.
Het gebruik van LostFocus
heeft een ongelukkig neveneffect: als u op Enter drukt om een formulier in te dienen met de knop IsDefault
wordt de eigenschap die uw binding ondersteunt niet bijgewerkt en worden uw wijzigingen effectief ongedaan gemaakt. Gelukkig bestaan er enkele oplossingen .
Houd er ook rekening mee dat, in tegenstelling tot UWP, WPF (4.5+) ook de eigenschap Delay
in bindingen heeft, wat misschien net genoeg is voor sommige bindingen met alleen lokale of eenvoudige, kleine intelligentie-instellingen, zoals sommige TextBox
validaties.
Converteer een Booleaanse waarde naar zichtbaarheidswaarde
In dit voorbeeld wordt het rode vakje (rand) verborgen als het selectievakje niet is ingeschakeld met behulp van een IValueConverter
.
Opmerking: De BooleanToVisibilityConverter
die in het onderstaande voorbeeld wordt gebruikt, is een ingebouwde BooleanToVisibilityConverter
in de naamruimte System.Windows.Controls.
XAML:
<Window x:Class="StackOverflowDataBindingExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="VisibleIfTrueConverter" />
</Window.Resources>
<StackPanel>
<CheckBox x:Name="MyCheckBox"
IsChecked="True" />
<Border Background="Red" Width="20" Height="20"
Visibility="{Binding Path=IsChecked,ElementName=MyCheckBox, Converter={StaticResource VisibleIfTrueConverter}}" />
</StackPanel>
</Window>
Gegevenscontext definiëren
Om met bindingen in WPF te werken, moet u een DataContext definiëren. De DataContext vertelt bindingen waar ze hun gegevens standaard vandaan kunnen halen.
<Window x:Class="StackOverflowDataBindingExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:StackOverflowDataBindingExample"
xmlns:vm="clr-namespace:StackOverflowDataBindingExample.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:HelloWorldViewModel />
</Window.DataContext>
...
</Window>
U kunt de DataContext ook instellen via code-behind, maar het is vermeldenswaard dat XAML IntelliSense ietwat kieskeurig is: een sterk getypte DataContext moet in XAML worden ingesteld voor IntelliSense om eigenschappen te suggereren die beschikbaar zijn voor binding.
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new HelloWorldViewModel();
}
}
Hoewel er frameworks zijn om u te helpen uw DataContext op een flexibelere manier te definiëren (bijv. MVVM Light heeft een viewmodel-locator die inversie van controle gebruikt ), gebruiken we de snelle en vuile methode voor de doeleinden van deze tutorial.
U kunt een DataContext definiëren voor vrijwel elk visueel element in WPF. De DataContext wordt over het algemeen geërfd van voorouders in de visuele structuur, tenzij deze expliciet is overschreven, bijvoorbeeld in een ContentPresenter.
Implementeren van INotifyPropertyChanged
INotifyPropertyChanged
is een interface die wordt gebruikt door bindende bronnen (dat wil zeggen de DataContext) om de gebruikersinterface of andere componenten te laten weten dat een eigenschap is gewijzigd. WPF werkt de gebruikersinterface automatisch voor u bij wanneer de PropertyChanged
gebeurtenis wordt verhoogd. Het is wenselijk om deze interface te implementeren op een basisklasse waarvan al uw viewmodels kunnen erven.
In C # 6 is dit alles wat je nodig hebt:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Dit stelt u in staat om NotifyPropertyChanged
op twee verschillende manieren aan te roepen:
-
NotifyPropertyChanged()
, waardoor het evenement wordtNotifyPropertyChanged()
voor de setter die het oproept, dankzij het kenmerk CallerMemberName . -
NotifyPropertyChanged(nameof(SomeOtherProperty))
, waardoor het evenement voor SomeOtherProperty wordt verhoogd.
Voor .NET 4.5 en hoger met C # 5.0 kan dit in plaats daarvan worden gebruikt:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string name = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
In versies van .NET die ouder zijn dan 4.5, moet u genoegen nemen met eigenschapsnamen als tekenreeksconstanten of een oplossing met expressies .
Opmerking: Het is mogelijk om te binden aan een eigenschap van een "gewoon oud C # object" (POCO) dat INotifyPropertyChanged
niet implementeert en te observeren dat de bindingen beter werken dan verwacht. Dit is een verborgen functie in .NET en moet waarschijnlijk worden vermeden. Vooral omdat het geheugenlekken veroorzaakt wanneer de Mode
de binding niet OneTime
(zie hier ).
Waarom wordt de bindende update uitgevoerd zonder INotifyPropertyChanged te implementeren?
Binden aan eigenschap van een ander benoemd element
U kunt binden aan een eigenschap van een benoemd element, maar het genoemde element moet binnen bereik zijn.
<StackPanel>
<CheckBox x:Name="MyCheckBox" IsChecked="True" />
<TextBlock Text="{Binding IsChecked, ElementName=MyCheckBox}" />
</StackPanel>
Binden aan eigendom van een voorouder
U kunt binden aan een eigenschap van een voorouder in de visuele structuur met behulp van een RelativeSource
binding. Het dichtstbijzijnde besturingselement hoger in de visuele structuur dat hetzelfde type heeft of is afgeleid van het type dat u opgeeft, wordt gebruikt als de bron van de binding:
<Grid Background="Blue">
<Grid Background="Gray" Margin="10">
<Border Background="Red" Margin="20">
<StackPanel Background="White" Margin="20">
<Button Margin="10" Content="Button1" Background="{Binding Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}}}" />
<Button Margin="10" Content="Button2" Background="{Binding Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type FrameworkElement}}}" />
</StackPanel>
</Border>
</Grid>
</Grid>
In dit voorbeeld heeft Button1 een grijze achtergrond omdat de dichtstbijzijnde Grid
voorouder een grijze achtergrond heeft. Button2 heeft een witte achtergrond omdat de dichtstbijzijnde voorouder afgeleid van FrameworkElement
de witte StackPanel
.
Meerdere waarden binden met een MultiBinding
Met MultiBinding kunnen meerdere waarden aan dezelfde eigenschap worden gekoppeld. In het volgende voorbeeld zijn meerdere waarden gebonden aan de eigenschap Text van een Textbox en opgemaakt met de eigenschap StringFormat.
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="User.Forename"/>
<Binding Path="User.Surname"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Naast StringFormat
kan een IMultiValueConverter
ook worden gebruikt om de waarden van de bindingen om te zetten in één waarde voor het doel van de MultiBinding.
MultiBindings kunnen echter niet worden genest.