数据绑定、依赖属性、路由事件、命令系统、绘图动画等方面的内容。
1. 数据绑定模型
1.1 使源实现通知能力
让数据源的类实现INotifyPropertyChanged接口,在属性的set块中激发一个PropertyChange事件。
当我们绑定到这个数据源上时,Binding对象就会侦听来自这个接口的PropertyChange事件。
public class emy : INotifyPropertyChanged
{
string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
//无订阅判断,避免激发空引用产生异常
if (this.PropertyChanged !=null)
{
//在属性值变更后,激发PropertyChange事件。
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
}
PropertyChanged既是委托,也是事件。委托是一种类型,用于定义方法的签名,可以将方法作为参数传递或赋值给变量。
事件是一种特殊的委托,用于在某种情况发生时通知其他对象。(类似:回调函数)
public partial class MainWindow : Window
{
emy emy = new emy() { Name="Default Name"};
public MainWindow()
{
InitializeComponent();
//实例化binding对象
//Binding binding = new Binding("Name") { Source=emy};
//指定Bingding对象的源和路径
//binding.Source = emy;
//binding.Path = new PropertyPath(emy.Name);
//使用BindingOperations.setBinding实现绑定,传入目标的目标名称、依赖属性、传入Binding对象
//this.Input.SetBinding(TextBox.TextProperty, binding);
this.Input.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = emy});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.OutPut.Text = "MyName"+emy.Name;
}
实现数据绑定的基本过程:
- 准备数据源和目标
- 使数据源实现通知Binding属性值变更的能力
- 声明Binding对象,设置数据源(对象)和路径(属性)
- 使用SetBinding()实现源和目标的连接,指定目标的名称和依赖属性,传入Binding对象。
1.2 数据流的方向
可以通过设置Binding.Model来控制数据流:
- OneWay,对源属性的更改会自动更新目标属性,但对目标属性的更改不会传播回源属性。
- TwoWay,更改源属性或目标属性时会自动更新另一方,也就是双向数据绑定。
- OneWayToSource,当目标属性更改时,它会更新源属性,反之则不会。
- OneTime,会使源属性初始化目标属性,但不传播后续更改。
![](https://ichistudio.cn/wp-content/uploads/2023/11/image.png)
public MainWindow()
{
InitializeComponent();
//实例化binding对象
//Binding binding = new Binding("Name") { Source=emy};
//指定Bingding对象的源和路径
//binding.Source = emy;
//binding.Path = new PropertyPath(emy.Name);
//使用BindingOperations.setBinding实现绑定,传入目标的目标名称、依赖属性、传入Binding对象
//this.Input.SetBinding(TextBox.TextProperty, binding);
this.Input.SetBinding(TextBox.TextProperty, new Binding("Name")
{
Source = emy,
//设置数据流方向
Mode = BindingMode.OneWay,
});
}
1.3 触发源更新的因素
Binding的Binding.UpdateSourceTrigger属性用于确定触发源更新的因素。
- 如果UpdateSourceTrigger值为UpdateSourceTigger.PropertyChanged,则目标属性更改后,TwoWay或OneWayToSource绑定的右箭头指向的值会立即更新。
- 如果UpdateSourceTrigger值为LostFocus,则仅当目标属性失去焦点时才会使用新值更新该值。
![](https://ichistudio.cn/wp-content/uploads/2023/11/image-1.png)
Binding的Binding.UpdateSourceTrigger的默认值
- 大多数依赖属性的默认值为PropertyChanged,这将导致源属性的值在目标属性值更改时立即更改。
- 对于文本字段,若是每输入一个按键后都进行更新会降低性能,用户也没有机会在提交新值之前使用Backspace键修改键入错误。
2. Binding源的指定
一个对象只要通过属性公开自己的数据,就可以作为Binding的源。
作为Binding源的对象需要实现INotifyPropertyChanged接口并激发PropertyChange事件才能使属性具有自动通知Binding发生了变化的能力。
除了使用自定义类作为源以外,还有其他不同的形式。
2.1 将控件作为数据源
<Grid>
<TextBox x:Name="textBox" Text="{Binding Path=Value,ElementName=slider}" HorizontalAlignment="Center" Margin="0,55,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="780"/>
<Slider x:Name="slider" HorizontalAlignment="Center" Margin="0,199,0,0" VerticalAlignment="Top" Width="780" />
</Grid>
2.2使用DataContext作为源
在XAML界面声明绑定DataContext
<Window.DataContext>
<local:emy/>
</Window.DataContext>
DataContext是数据上下文,它用于将数据模型与UI元素进行绑定。每个UI元素都有一个DataContext属性,它指定了该元素的数据模型。
当我们使用了DataContext作为数据源时,它会自动将UI元素的数据源设定为当前窗口或用户控件的DataContext属性。
所以在XAML文件中使用{Binding}语法来绑定时而无需指定任何其他源,因此这种形式也称作无源数据绑定。
标记扩展声明绑定,指定路径
<Window.DataContext>
<local:emy Name="ICHI"/>
</Window.DataContext>
<Grid>
<TextBox x:Name="Input" HorizontalAlignment="Left" Margin="230,125,0,0" TextWrapping="Wrap" Text="{Binding Path=Name}" VerticalAlignment="Top" Width="380"/>
<Button x:Name="MyOutPut" Content="OutPut" HorizontalAlignment="Left" Margin="230,0,0,0" VerticalAlignment="Center" Width="380" Click="Button_Click"/>
<TextBox x:Name="OutPut" HorizontalAlignment="Left" Margin="230,295,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="380"/>
</Grid>
声明显示转换
emy emy = (emy)this.DataContext;
2.3 将集合对象作为列表控件的ItemsSource
![](https://ichistudio.cn/wp-content/uploads/2023/11/图片.png)
我们一般会考虑使用ObservableCollection<T> 来代替List<T>使用,因为该类实现了INotifyCollectionChanged和INotifyPropertyChanged接口,能够实现把集合的变化立即通知到显示它们的列表控件中。
2.4 ADO.NET对象作为Binding源
在,NET项目开发时,我们使用ADO.NET类对数据进行操作。
常见的形式是从数据库把数据读到DataTable中,再把DataTable展示在UI列表控件里。
在实际项目中,我们一般会先把DataTable转换为自定义的集合类型,不过WPF的Binding仍然也会支持列表控件直接与DataTable之间直接建立Binding。
2.5 使用DataSourceProvider类的子类成员作为数据源
DataSourceProvider类是一个抽象基类,它定义了一些公共属性和方法,用来执行某些查询,生成可以用作绑定源的单个对象或对象列表。
DataSourceProvider类支持标准的Windows窗体数据绑定模型,可以处理不同类型的数据源,例如SQL数据库,XML文档,数组集合等。
DataSourceProvider类还实现了INotifyPropertyChanged和ISupportInitialize接口,用来提供对绑定和初始化的支持。
XmlDataProvider
将XML作为Binding源
.NET提供了DOM文档对象模型类库用于处理XML数据,包括XmlDoucument、XmlElement等类,是传统的功能强大的类库。
一般来说涉及数据传输的程序是离不开XML的,因为大多数数据传输都基于SOAP简单对象访问协议相关的协议,而SOAP又是通过将对象序列化为XML文本进行传输
ObjectDataProvider
ObjectDataProvider能够将对象进行包装,作为数据源提供给Binding。
被包装的对象作为ObjectDataProvider的ObjectInstance属性,我们可以通过MethodName属性来指明要调用的方法,使用MethodParameters属性来传入参数,其是一个集合,因此可以使用下标形式指定和访问。运行方法的结果会保存在Data属性中。
![](https://ichistudio.cn/wp-content/uploads/2023/11/图片-1.png)
我们通常把数据的来源定为Binding的Source,去往哪里定位Target
- 非依赖属性不能作为Binding的目标;
- 数据驱动的理念要求应尽可能选择数据对象作为源,UI元素作为目标。
2.6 LinQ查询结果作为数据源
LINQ的查询结果是可以作为数据绑定的数据源的,这是因为其查询结果是一个IEnumerable<T>对象,所以可以作为列表控件的ItemSource使用
2.7 RelativeSource
当我们不能确定源对象的名称,但是能知道其与目标对象在UI布局上有层级关系时,可以使用RelativeSource属性。
RelativeSource属性数据类型为RelativeSource类,其Mode属性的类型是RelativeSourceMode枚举,其取值有PreviousData、TemplateParent、Self和FindAncestor。
对于前三个枚举值还有同名的静态属性,它们的类型是RelativeSource类。
- PreviousData 绑定到集合中当前项的前一项的数据
- TemplateParent 绑定到应用了控件模板的控件的属性
- Self 绑定到绑定目标本身的属性
- FindAncestor 绑定到绑定目标的某个祖先元素(结点或根元素)的属性
3. Binding路径的指定
- Binding支持多级路径
- 无需指定路径时,在XAML代码中绑定时可以省略不写,但在C#代码中需要使用”.”代替
4. 数据校验与转换
若在数据传输的过程中,需要对数据进行限制,可以在Binding上设置数据校验;
若要对数据类型进行转换,可以在Binding上设置数据转换,编写数据转换器。
WPF在Binding上提供了Validation进行校验,Converter进行转换。
4.1 数据校验
Binding用于数据校验的属性是ValidationRules,在WPF中,要实现Binding的数据校验,需要以下几个步骤:
- 定义一个继承自ValidationRule类,重写其Validate方法,根据自己的逻辑和条件,返回一个ValidationResult对象,表示数据是否有效,以及无效时的错误信息
- 在Binding对象中添加一个或多个ValidationRule对象,表示要对数据应用哪些校验规则。可以设置ValidationRules属性的ValidatesOnTargetUpdated属性为true,表示要在目标更新时也进行校验。
- 在Binding对象中设置NotifyOnValidationError属性为true,表示当数据校验失败时,要通知绑定目标或其祖先元素。
- 编写校验失败时执行的事件处理器(方法),获取事件参数,获取错误信息,编写显示错误信息的代码或其它执行内容。
- 在目标或祖先元素上添加一个事件处理器,用于处理Validation.ErrorEvent事件,传入委托并指定方法。
简化下来就这样的:
- 编写校验规则类
- 为Binding添加校验规则
- 打开校验通知开关
- 编写校验失败事件处理器
- 处理校验失败事件
![](https://ichistudio.cn/wp-content/uploads/2023/11/图片-2-1024x512.png)
为何要使用WPF数据校验:
使用WPF数据校验可以提高代码的质量和效率,增强用户体验和交互性
- WPF数据校验将校验功能独立出来,可以将验证逻辑和视图逻辑分离,使得代码更加清晰和可维护。
- 利用Binding对象的各种属性和事件,实现数据的自动更新和错误的通知。
- 数据校验可以使用不同的验证规则,实现不同的验证需求,使用预定好的类来编写验证功能更加规范和方便。也可以为一个绑定添加多个规则,实现复用。
- WPF数据校验可以使用ErrorTemplate属性,实现对验证失败时的视觉反馈,例如显示红色的感叹号或边框等。
总之WPF数据校验是一种规范方便可以实现分离和复用的数据验证方式
4.2 数据转换
WPF提供了两种数据转换的方式:
- 值转换器方法(Converter),绑定后,触发转换器,转换器负责把值转换成需要的内容。转换器需要实现System.Windows.Data命名空间的IValueConverter接口或IMultiValueConverter接口,分别用于单值转换和多值转换。转换器可以在XAML中定义为资源,并在绑定中引用。
- DataTigger方法,直接在XAML里面对数据进行处理,展示所需要的内容。DataTrigger可以根据绑定的数据的值或条件来改变控件的属性或样式。DataTrigger可以在Style或ControlTemlate中定义。
Converter
当我们遇到需要自行编写转换器的情况时,我们需要创建一个类并让这个类实现IValueConverter接口,该接口定义如下,当数据从源流向目标时,Convert()方法会被调用,反之调用ConvertBack()。
//
// 摘要:
// Provides a way to apply custom logic to a binding.
public interface IValueConverter
{
//
// 摘要:
// Converts a value.
//
// 参数:
// value:
// The value produced by the binding source.
//
// targetType:
// The type of the binding target property.
//
// parameter:
// The converter parameter to use.
//
// culture:
// The culture to use in the converter.
//
// 返回结果:
// A converted value. If the method returns null, the valid null value is used.
object Convert(object value, Type targetType, object parameter, CultureInfo culture);
//
// 摘要:
// Converts a value.
//
// 参数:
// value:
// The value that is produced by the binding target.
//
// targetType:
// The type to convert to.
//
// parameter:
// The converter parameter to use.
//
// culture:
// The culture to use in the converter.
//
// 返回结果:
// A converted value. If the method returns null, the valid null value is used.
object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
}
DataTigger
在Style中找到DataTigger触发
<Style x:Name="addButton" TargetType="Button">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger>
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
两种数据转换方式的优势和使用场景:
- Converter说一种通过实现IValueConverter接口来实现来自自定义数据转换逻辑的方法,它可以将数据从一种类型或格式转换为另一种类型或格式,其优势是可以实现更灵活和复杂的数据转换,像是自定义类数据转换为其他数据等。
- DataTrigger是一种通过在XAML中定义触发器来根据数据的不同值改变控件属性的方法,它可以实现一些简单的数据转换。DataTrigger的优势是可以直接在XAML中定义数据转换,无需编写额外的代码,而且可以结合样式和动画来实现更丰富的效果。
5. MultiBinding
WPF的MultiBinding是一种数据绑定的方法,它可以将多个数据源的值绑定到一个目标属性上,也就是说需要显示的信息不止由一个数据源来决定,例如将多个字符串拼接成一个文本,或者将多个布尔值进行逻辑运算得到一个可见性值。