wpf
Wprowadzenie do powiązania danych WPF
Szukaj…
Składnia
- {Binding PropertyName} odpowiada {Binding Path = PropertyName}
- {Binding Path = SomeProperty.SomeOtherProperty.YetAnotherProperty}
- {Binding Path = SomeListProperty [1]}
Parametry
Parametr | Detale |
---|---|
Ścieżka | Określa ścieżkę do powiązania. Jeśli nie jest określony, wiąże się z samym DataContext. |
UpdateSourceTrigger | Określa, kiedy wartość źródła wiązania ma zostać zaktualizowana. Domyślnie LostFocus . Najczęściej używana wartość to PropertyChanged . |
Tryb | Zazwyczaj OneWay lub TwoWay . Jeśli nie jest określone przez powiązanie, domyślnie jest ustawione na OneWay chyba że cel powiązania zażąda, aby to był TwoWay . Błąd występuje, gdy TwoWay jest używany do wiązania właściwości tylko do odczytu, np. OneWay musi być jawnie ustawiony podczas wiązania właściwości ciągu tylko do odczytu z TextBox.Text . |
Źródło | Pozwala na użycie StaticResource jako źródła powiązania zamiast bieżącego DataContext. |
RelativeSource | Pozwala na użycie innego elementu XAML jako źródła wiązania zamiast bieżącego DataContext. |
ElementName | Pozwala na użycie nazwanego elementu XAML jako źródła powiązania zamiast bieżącego DataContext. |
FallbackValue | Jeśli powiązanie nie powiedzie się, ta wartość jest przekazywana do celu powiązania. |
TargetNullValue | Jeśli wartość źródła wiązania jest równa null , wartość ta jest przekazywana do obiektu docelowego wiązania. |
Przetwornik | Określa konwerter StaticResource który służy do konwersji wartości powiązania, np. Przekształca wartość logiczną na element wyliczający Visibility . |
ConverterParameter | Określa opcjonalny parametr, który należy podać konwerterowi. Ta wartość musi być statyczna i nie może być powiązana. |
StringFormat | Określa ciąg formatu, który będzie używany podczas wyświetlania powiązanej wartości. |
Opóźnienie | (WPF 4.5+) Określa opóźnienie w milliseconds dla powiązania w celu zaktualizowania BindingSource w ViewModel . Musi być użyte z Mode=TwoWay i UpdateSourceTrigger=PropertyChanged aby UpdateSourceTrigger=PropertyChanged . |
Uwagi
UpdateSourceTrigger
Domyślnie WPF aktualizuje źródło wiązania, gdy formant traci fokus. Jeśli jednak istnieje tylko jeden element sterujący, na którym można skupić się - co jest powszechne w przykładach - konieczne będzie określenie UpdateSourceTrigger=PropertyChanged
aby aktualizacje działały.
Będziesz chciał użyć PropertyChanged
jako wyzwalacza w wielu powiązaniach dwukierunkowych, chyba że aktualizacja źródła powiązania przy każdym naciśnięciu klawisza jest kosztowna lub sprawdzanie poprawności danych na żywo jest niepożądane.
Korzystanie z LostFocus
ma niefortunny efekt uboczny: naciśnięcie Enter, aby przesłać formularz za pomocą przycisku oznaczonego IsDefault
, nie aktualizuje właściwości wspierającej wiązanie, skutecznie cofając zmiany. Na szczęście istnieją pewne obejścia .
Należy również pamiętać, że w przeciwieństwie do UWP, WPF (4.5+) ma również właściwość Delay
w powiązaniach, która może być wystarczająca dla niektórych powiązań z lokalnymi lub prostymi niewielkimi ustawieniami inteligencji, takimi jak niektóre weryfikacje TextBox
.
Konwertuj wartość logiczną na wartość widoczności
Ten przykład ukrywa czerwone pole (ramkę), jeśli pole wyboru nie jest zaznaczone przy użyciu IValueConverter
.
Uwaga: BooleanToVisibilityConverter
zastosowany w poniższym przykładzie jest wbudowanym konwerterem wartości, znajdującym się w przestrzeni nazw 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>
Definiowanie DataContext
Aby pracować z powiązaniami w WPF, musisz zdefiniować DataContext . DataContext domyślnie informuje powiązania, skąd mają pobierać swoje dane.
<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>
Możesz również ustawić DataContext za pomocą kodu, ale warto zauważyć, że XAML IntelliSense jest nieco wybredny: silnie typowany DataContext musi być ustawiony w XAML dla IntelliSense, aby zasugerować właściwości dostępne do wiązania.
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new HelloWorldViewModel();
}
}
Chociaż istnieją ramy pomagające zdefiniować DataContext w bardziej elastyczny sposób (np. MVVM Light ma lokalizator viewmodel, który wykorzystuje odwrócenie kontroli ), do celów tego samouczka używamy szybkiej i brudnej metody.
Możesz zdefiniować DataContext dla praktycznie dowolnego elementu wizualnego w WPF. DataContext jest generalnie dziedziczony od przodków w drzewie wizualnym, chyba że został wyraźnie zastąpiony, np. W ContentPresenter.
Implementowanie INotifyPropertyChanged
INotifyPropertyChanged
to interfejs używany przez źródła powiązań (tj. DataContext), aby powiadomić interfejs użytkownika lub inne komponenty o zmianie właściwości. WPF automatycznie aktualizuje interfejs użytkownika, gdy zobaczy podniesione zdarzenie PropertyChanged
. Pożądane jest, aby interfejs ten został zaimplementowany w klasie bazowej, z której wszystkie twoje modele viewmode mogą dziedziczyć.
W C # 6 to wszystko, czego potrzebujesz:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Umożliwia to wywołanie NotifyPropertyChanged
na dwa różne sposoby:
-
NotifyPropertyChanged()
, który wywoła zdarzenie dlaNotifyPropertyChanged()
, który je wywołuje, dzięki atrybutowi CallerMemberName . -
NotifyPropertyChanged(nameof(SomeOtherProperty))
, który wywoła zdarzenie dla SomeOtherProperty.
W przypadku .NET 4.5 i nowszych przy użyciu C # 5.0 można tego użyć:
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));
}
}
}
W wersjach .NET wcześniejszych niż 4.5, musisz zadowolić się nazwami właściwości jako stałymi łańcuchowymi lub rozwiązaniem wykorzystującym wyrażenia .
Uwaga: Możliwe jest powiązanie z właściwością „zwykłego starego obiektu C #” (POCO), który nie implementuje INotifyPropertyChanged
i zaobserwowanie, że wiązania działają lepiej niż oczekiwano. Jest to ukryta funkcja w .NET i prawdopodobnie należy jej unikać. Zwłaszcza, że spowoduje to wycieki pamięci, gdy Mode
powiązania nie jest OneTime
(patrz tutaj ).
Dlaczego wiązanie aktualizuje się bez implementacji INotifyPropertyChanged?
Powiązanie z właściwością innego nazwanego elementu
Można powiązać z właściwością na nazwanym elemencie, ale nazwany element musi mieć zasięg.
<StackPanel>
<CheckBox x:Name="MyCheckBox" IsChecked="True" />
<TextBlock Text="{Binding IsChecked, ElementName=MyCheckBox}" />
</StackPanel>
Powiązanie z własnością przodka
Można powiązać z właściwością przodka w drzewie wizualnym za pomocą powiązania RelativeSource
. Najbliższa kontrolka wyżej w drzewie wizualnym, która ma ten sam typ lub pochodzi od określonego typu, zostanie użyta jako źródło powiązania:
<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>
W tym przykładzie Button1 ma szare tło, ponieważ najbliższy przodek Grid
ma szare tło. Button2 ma białe tło, ponieważ najbliższym przodkiem pochodzącym z FrameworkElement
jest biały StackPanel
.
Wiązanie wielu wartości za pomocą funkcji MultiBinding
Funkcja MultiBinding umożliwia powiązanie wielu wartości z tą samą właściwością. W poniższym przykładzie wiele wartości jest powiązanych z właściwością Text pola tekstowego i sformatowanych przy użyciu właściwości StringFormat.
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="User.Forename"/>
<Binding Path="User.Surname"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Oprócz StringFormat
An IMultiValueConverter
może być również stosowany do przeliczenia wartości z wiązaniami do jednej wartości dla realizacji celu MultiBinding użytkownika.
Jednak MultiBindings nie może być zagnieżdżony.