WPF数据绑定简介
{Binding PropertyName} 相当于 {Binding Path = PropertyName}
{Binding Path = SomeProperty.SomeOtherProperty.YetAnotherProperty}
{Binding Path = SomeListProperty [1]}
参数 |
细节 |
Path路径 |
指定要绑定的路径。如果未指定,则绑定到DataContext本身。 |
UpdateSourceTrigger |
指定绑定源何时更新其值。默认为LostFocus 。最常用的值是PropertyChanged 。 |
Mode模式 |
通常是OneWay或TwoWay 。如果绑定未指定,则默认为OneWay除非绑定目标请求它为TwoWay 。当TwoWay用于绑定到readonly属性时发生错误,例如,在将readonly字符串属性绑定到TextBox.Text时必须显式设置OneWay 。 |
Source资源 |
允许使用StaticResource作为绑定源而不是当前的DataContext。 |
RelativeSource |
允许使用另一个XAML元素作为绑定源而不是当前的DataContext。 |
ElementName |
允许使用命名的XAML元素作为绑定源而不是当前的DataContext。 |
FallbackValue |
如果绑定失败,则将此值提供给绑定目标。 |
TargetNullValue |
如果绑定源值为null ,则将此值提供给绑定目标。 |
Converter |
指定用于转换绑定值的转换器StaticResource ,例如将布尔值转换为Visibility枚举项。 |
ConverterParameter |
指定要提供给转换器的可选参数。该值必须是静态的,不能绑定。 |
StringFormat |
指定显示绑定值时要使用的格式字符串。 |
Delay延迟 |
(WPF 4.5+)指定绑定更新ViewModel BindingSource的延迟(以milliseconds为milliseconds )。这必须与Mode=TwoWay和UpdateSourceTrigger=PropertyChanged一起使用才能生效。 |
默认情况下,WPF在控件失去焦点时更新绑定源。但是,如果只有一个控件可以获得焦点 - 这在示例中很常见 - 你需要指定UpdateSourceTrigger=PropertyChanged才能使更新UpdateSourceTrigger=PropertyChanged 。
你将希望使用PropertyChanged作为许多双向绑定的触发器,除非在每次击键时更新绑定源代价高昂或者不希望进行实时数据验证。
使用LostFocus有一个不幸的副作用:按Enter键以使用标记为IsDefault的按钮提交表单不会更新支持绑定的属性,从而有效地撤消你的更改。幸运的是, 存在一些变通方法 。
还请注意,与UWP不同,WPF(4.5+)在绑定中也具有Delay属性,对于某些具有本地或简单的次要智能设置的绑定可能就足够了,例如一些TextBox验证。
wpf使用MultiBinding绑定多个值
MultiBinding允许将多个值绑定到同一属性。在以下示例中,多个值绑定到Textbox的Text属性,并使用StringFormat属性进行格式化。
<TextBlock> <TextBlock.Text> <MultiBinding StringFormat="{}{0} {1}"> <Binding Path="User.Forename"/> <Binding Path="User.Surname"/> </MultiBinding> </TextBlock.Text> </TextBlock>
除了StringFormat之外,还可以使用IMultiValueConverter将Bindings中的值转换为MultiBinding目标的一个值。
但是,MultiBindings不能嵌套。
wpf定义DataContext
要在WPF中使用绑定,你需要定义DataContext 。 DataContext告诉绑定默认情况下从哪里获取数据。
<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>
你也可以通过代码隐藏设置DataContext,但值得注意的是XAML IntelliSense有点挑剔:必须在XAML中为IntelliSense设置强类型DataContext,以建议可用于绑定的属性。
/// <summary> ///MainWindow.xaml的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new HelloWorldViewModel(); } }
有一些框架可以帮助你以更灵活的方式定义DataContext(例如MVVM Light有一个使用控制反转的 viewmodel定位器)。
你可以为WPF中的几乎任何可视元素定义DataContext。 DataContext通常从可视树中的祖先继承,除非它已被显式覆盖,例如在ContentPresenter中。
实现INotifyPropertyChanged
INotifyPropertyChanged是绑定源(即DataContext)使用的接口,以使用户界面或其他组件知道属性已更改。当WPF看到引发的PropertyChanged事件时,它会自动为你更新UI。希望在所有视图模型都可以继承的基类上实现此接口。
在C#6中:
public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged([CallerMemberName] string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } }
这允许你以两种不同的方式调用NotifyPropertyChanged :
- NotifyPropertyChanged() ,它将引发调用它的setter的事件,这要归功于CallerMemberName属性。
- NotifyPropertyChanged(nameof(SomeOtherProperty)) ,它将为SomeOtherProperty引发事件。
对于使用C:5.0的.NET 4.5及更高版本:
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)); } } }
在4.5之前的.NET版本中,你必须将属性名称解析为字符串常量或使用表达式的解决方案 。
注意:可以绑定到未实现INotifyPropertyChanged的“plain old C# object”(POCO)的属性,并观察绑定是否比预期更好。这是.NET中的一个隐藏功能,应该可以避免。特别是当绑定的Mode不是OneTime时会导致内存泄漏(见这里 )。
为什么绑定更新没有实现INotifyPropertyChanged?
将布尔值转换为可见性值
如果未使用IValueConverter检查复选框,则此示例隐藏红色框(边框)。
注意:以下BooleanToVisibilityConverter使用的BooleanToVisibilityConverter是一个内置值转换器,位于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>
绑定到另一个命名元素的属性
你可以绑定到命名元素上的属性,但命名元素必须在范围内。
<StackPanel> <CheckBox x:Name="MyCheckBox" IsChecked="True" /> <TextBlock Text="{Binding IsChecked, ElementName=MyCheckBox}" /> </StackPanel>
绑定祖先的财产
你可以使用RelativeSource绑定绑定到可视树中的祖先的属性。可视树中具有相同类型或从你指定的类型派生的最近控件将用作绑定的源:
<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>
在此示例中, Button1具有灰色背景,因为最近的Grid祖先具有灰色背景。 Button2具有白色背景,因为从FrameworkElement派生的最近的祖先是白色StackPanel 。