前言
在WPF中,Binding是一种强大的机制,用于将数据源与目标对象(通常是UI控件)进行绑定。
以下是一些常用的Binding属性及其说明:
Source: 指定数据源对象,可以是一个对象实例、属性路径、静态资源或者父级元素的属性。
1 | <TextBox Text="{Binding Path=UserName, Source={StaticResource myDataSource}}" /> |
Path: 指定数据源中的属性路径,告诉绑定系统从哪里获取数据。
1 | <TextBox Text="{Binding Path=UserName}" /> |
Mode: 指定绑定的方向和方式,例如单向绑定、双向绑定、或单向到源的绑定。
1 | <TextBox Text="{Binding Path=UserName, Mode=TwoWay}" /> |
Converter: 提供一个转换器,用于在绑定源和绑定目标之间进行转换。
1 | <TextBox Text="{Binding Path=OrderTotal, Converter={StaticResource priceConverter}}" /> |
UpdateSourceTrigger: 指定何时更新数据源的值,例如在属性更改时立即更新,或在控件失去焦点时更新。
1 | <TextBox Text="{Binding Path=UserName, UpdateSourceTrigger=PropertyChanged}" /> |
FallbackValue: 当绑定的值无法获取时,提供一个备用的默认值。
1 | <TextBox Text="{Binding Path=OrderTotal, FallbackValue=0}" /> |
TargetNullValue: 当目标绑定值为null时显示的值。
1 | <TextBox Text="{Binding Path=OrderTotal, TargetNullValue=Not available}" /> |
NotifyOnSourceUpdated 和 NotifyOnTargetUpdated: 用于指定在绑定源或绑定目标更新时是否发出通知。
1 | <TextBox Text="{Binding Path=UserName, NotifyOnTargetUpdated=True}" /> |
IsAsync: 指定是否异步处理绑定,适用于长时间运行的操作。
1 | <TextBox Text="{Binding Path=UserName, IsAsync=True}" /> |
ValidatesOnDataErrors, ValidatesOnExceptions 和 NotifyOnValidationError: 用于指定在数据验证失败时是否发出通知,并且如何处理数据验证和异常。
1 | <TextBox Text="{Binding Path=UserName, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" /> |
这些属性可以按照需要组合使用,以便精确地控制WPF中的数据绑定行为。
作用域
当你在XAML中使用Binding时,你可以指定目标属性的作用域,通常有以下几种情况:
默认作用域:
如果你没有显式指定作用域,Binding 将在当前 DataContext 的上下文中进行搜索。
这意味着它会尝试在父元素的 DataContext 中查找绑定的属性。
ElementName 绑定:
通过设置 Binding 的 ElementName 属性,你可以指定一个具体的元素来作为绑定的目标,Binding 将在该元素的上下文中搜索属性。
RelativeSource 绑定:
通过设置 Binding 的 RelativeSource 属性,你可以指定绑定相对于其他元素的位置,比如指定为父元素或者某个特定类型的元素。
Source 绑定:
通过设置 Binding 的 Source 属性,你可以直接指定一个数据源对象,Binding 将在该对象的上下文中搜索属性。
默认作用域
如果你没有显式指定作用域,Binding 将在当前 DataContext 的上下文中进行搜索。
这意味着它会尝试在父元素的 DataContext 中查找绑定的属性。
1 | <TextBlock |
ElementName 绑定
简单示例
1 | <Rectangle Fill="Red" Name="rectangle" |
或者
1 | <StackPanel> |
自定义组件
自定义组件没有显式指定作用域时,默认作用域不是DataContext。
1 | <DataTemplate x:Key="BlackbordTpl" DataType="page:SubmitUserItem"> |
RelativeSource 绑定
绑定自身属性
正方形
1 | <Rectangle Fill="Red" Height="100" |
绑定祖先元素属性
没有直接绑定父组件的,绑定父组件使用这种方法实现。
如果想让图片上下填充满,左右等比自适应,可以使用下面的方式实现:
1 | <Border BorderBrush="#434343" BorderThickness="1"> |
这里
设置图片的高度后缩放方式设置为
Uniform
,这样图片高度就固定了,宽度会等比缩放,再设置水平居中,就实现了这个效果。
其中
RelativeSource={RelativeSource AncestorType={x:Type Grid}}
是用来指定查找祖先中最近类型为Grid的元素。
TemplatedParent
此模式允许将给定的 ControlTemplate 属性绑定到应用 ControlTemplate 的控件的属性。
为了更好地理解这里的问题,下面是一个示例
1 | <Window.Resources> |
如果我想应用给定控件的属性到它的控件模板,那么我可以使用TemplatedParent模式。
TemplateBinding一般用于绑定控件模板内的属性,而TemplatedParent用于在控件模板内访问父元素的属性。
TemplateBinding
在 WPF 中, TemplateBinding 用于在控件模板中绑定到控件的属性。这可以让模板基于控件的属性值更改其视觉体验。
这里是一个简单示例:MainWindow.xaml
1 | <Window x:Class="TemplateBindingDemo.MainWindow" |
MyButton.xaml
1 | <ResourceDictionary |
MyButton.cs
1 | public class MyButton : Button |
在这里,我们为 MyButton
定义了一个 ControlTemplate。
在模板中,我们使用 {TemplateBinding Background}
和 {TemplateBinding Content}
来绑定到控件的 Background
和 Content
属性。
所以模板会随着控件属性的改变而改变。
运行这个示例,你会看到一个蓝色的按钮,上面写着 “Button”。如果你改变 MyButton
的 Background
或 Content
属性,模板会相应更新。
Source 绑定
1 | <!-- MainWindow.xaml --> |
定义的ViewModel
1 | // MyViewModel.cs |
可绑定的值
可用来绑定的两种类型
- 使用依赖属性
- 实现INotifyPropertyChanged接口
依赖属性
示例1
1 | public partial class UcBlackbord |
使用
1 | Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UcBlackbord}}, Path=ShowToolbar, Converter={StaticResource BooleanToVisibilityConverter}}" |
示例2
1 | public partial class UcTimeNum : UserControl |
页面
1 | <TextBlock |
实现INotifyPropertyChanged接口
1 | public class MyViewModel : INotifyPropertyChanged |
绑定属性的查找机制
数据上下文(DataContext):如果在自定义控件内部使用数据绑定,默认情况下,绑定会从控件的
DataContext
开始查找。如果控件的
DataContext
设置了某个对象,那么绑定就会尝试在这个对象上查找绑定的属性。绑定路径(Binding Path):绑定路径决定了属性查找的方式。
比如,如果你在自定义控件中这样设置绑定:
1
<TextBlock Text="{Binding MyProperty}" />
这里的
MyProperty
会在控件的DataContext
中查找。RelativeSource:有时候,绑定可能需要在控件的容器或其它上下文中查找属性。
可以通过
RelativeSource
来实现这一点。例如:
1
<TextBlock Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=Page}}" />
这个绑定会在当前页面的
DataContext
中查找MyProperty
。
控件绑定属性从内部查找的原因
默认的 DataContext:
如果自定义控件的 DataContext
已经被设置为某个对象,并且控件内部的绑定没有指定路径的源,那么绑定会在控件的 DataContext
中查找。
属性的优先级:
控件的 DataContext
优先于控件容器的 DataContext
,除非你明确指定了绑定的来源。
解决方案
明确指定绑定源:如果希望绑定从控件的容器中查找属性,可以使用 RelativeSource
或 ElementName
来明确指定绑定的源。
例如:
1 | <TextBlock Text="{Binding ElementName=MyPage, Path=DataContext.MyProperty}" /> |
数据绑定注意点
- 在给组件赋值的时候我们只能在UI线程中处理,同样也只能在UI线程中对
DataContext
已绑定的对象进行操作。 - 文本组件可以绑定int和double类型,不用必须为string类型。
另外提一下
我们在做JSON转换的时候,数字类型的值可能为空的时候,直接转对象必须属性为可空类型。
有时候为了方便处理,我们可以把属性定义为string,这样也是能成功转换的。
设置上下文
组件中
在WPF中,通过d:DataContext
属性可以为设计时数据绑定设置数据上下文,这在开发和设计阶段非常有用,可以代码提醒和点击跳转到对应属性中。
指定数据上下文类:
如果你有一个名为 YourViewModel
的类作为数据上下文,可以在 d:DataContext
中引用它,以便在设计时绑定界面元素的数据。
示例:
1 | <Window ... |
注意事项:
- 命名空间:确保在使用
d:DataContext
时正确引用你的ViewModel类的命名空间。 - 仅限设计时:
d:DataContext
仅用于设计时数据绑定,不影响运行时的实际数据绑定和行为。 - 设计时数据源:这允许在没有运行时数据源的情况下预览和调试界面。
DataTemplate中
在DataTemplate中使用DataType指定
1 | <DataTemplate x:Key="TjItemDt" |
数据绑定
定义基类
1 | using System.ComponentModel; |
定义数据源的类
1 | public class ToolbarModel : ZNotifyModel |
上面例子中我们可以看到,
如果我们要在数据改变时通知页面改变的属性都要在Set
方法中调用OnPropertyChanged
而列表不再用List,而是使用ObservableCollection
ObservableCollection
注意ObservableCollection中调用Add方法会触发列表刷新,但是如果直接更换了对象就不会刷新了。
方式1
如下示例
1 | int pageSize = 15; |
其中pageList
必须调用OnPropertyChanged
方法才会刷新
1 | public class JianDaDetailPageData : ZNotifyModel |
方式2
如果pageList
保持原样那么赋值只能使用Add
方法
1 | this._pageData.pageList.Clear(); |
对应
1 | public class JianDaDetailPageData : ZNotifyModel |
设置数据源
代码中也要进行数据源的设置
1 | pageData.IsRight = true; |
上面设置整个页面的数据,当然也可以设置某个组件的数据源
1 | this.toolbar_list.DataContext = mydata; |
页面中绑定值
1 | <Window.Resources> |
双向绑定
1 | <TextBox Grid.Row="0" Text="{Binding Title,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"/> |
关键属性 UpdateSourceTrigger=PropertyChanged,Mode=TwoWay
转换器
https://www.psvmc.cn/article/2019-12-30-wpf-start-03-converter.html
自定义代码片段
上面属性写的时候比较麻烦,建议使用自定义代码片段。
Visual Studio创建
在任意地方创建一个文件夹,最好是你不去经常移动的地方,文件夹是用来存放你自定义的代码块的文件夹,
我就创建了一个名称:csharp_snippet
的文件夹
新建代码块文件zprop.snippet
1 |
|
保存啦,然后依然是去 工具–>代码段管理器 –>选择Visual C#语言 –>选择下方的添加 –>浏览到你自定义的那个放代码块的文件夹就OK啦。
重启开发工具。
此时要我在项目中打出zprop
按两次Tab 那我的数据访问层的代码就全部出来啦,当然还要添加一些引用就可以啦
ReSharper插件创建
安装ReSharper插件后,所有自定义的代码段都失效了,是因为
安装ReSharper插件后,它会自动导入代码段,但是后来的代码段是不会被导入的,只能我们自己添加。
配置步骤查看:
https://www.psvmc.cn/article/2019-12-27-resharper-config.html