DataContext
WPF控件均有DataContext属性,可把DataContext理解为控件的数据源。
Binding给人的错觉是当前控件没有设置DataContext属性时会自动向UI元素树一路向树的根部找过去,直到找到已设置DataContext属性的控件,并把此控件的DataContext作为自身的DataContext,Binding并没有这么智能。因为DataContext是依赖属性,当没有为控件的依赖属性显示赋值值,控件会把自己的容器控件的属性值当做自己的属性值。实际上是属性值沿着UI元素树向下传递了。
有两种方式设置控件的DataContext
//在Xaml设置
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
//或在窗体的.cs文件中设置
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
Binding声明
展示层和逻辑层的数据沟通通过DataBinding实现。数据绑定由四个部分组成:
绑定目标:目标控件
目标属性:控件中哪个属性要绑定源中某个属性
绑定源:源默认是DataContext
源路径:要绑定源的属性名称
<TextBlock Text="{Binding}" />
//等价于
<TextBlock Text="{Binding .}" />
//等价于
<TextBlock Text="{Binding RelativeSource={RelativeSource self}, Path=DataContext}" />
-
Binding表达式默认第一个参数是Path,所以可以直接把Path去掉
-
上述代码行Binding后面什么不写或写一个点表示绑定的是控件本身的DataContext,如果未设置控件绑定的 DataContext 属性,则将检查父控件的 DataContext 属性,依此类推,直到 XAML 对象树的根。如果父元素存在DataContext则该控件自动继承父控件的DataContext。
-
要绑定的必须是属性,不能是字段
-
DataContext实现IPropertyChanged主要是为了当后台属性值发生变化时通知前端,即绑定的源发生变化后通知目标
-
默认前端控件输入值后控件失去焦点时会通知源
-
控件默认绑定的Source是DataContext
Source
控件中需要指名Source时,Source一般与<XX.Resources> 或资源字典一起使用,还有枚举、静态变量、常量等
<Window.Resources>
<sys:String x:Key="str" >Hello World</sys:String>
<local:MyResource x:Key="myres"/>
</Window.Resources>
<StackPanel>
<TextBlock Text="{Binding Source={StaticResource str}}" />
<TextBlock Text="{StaticResource str}"/>
//上面两种方式一样
<TextBlock Text="{DynamicResource str}"/>
//当str发生变化时,上面TextBox显示的内容同样变化
<TextBlock Text="{Binding Source={StaticResource myres}, Path=Message}" />
//静态的变量或枚举使用x:Static绑定
<TextBlock Text="{Binding Source={x:Static local:MyResource.ConstString}}" />
<TextBlock Text="{Binding Source={x:Static local:MyResource.StaticString}}" />
//可简写为:
<TextBlock Text="{x:Static local:MyResource.ConstString}" />
<TextBlock Text="{x:Static local:MyResource.StaticString}" />
<TextBlock Text="{Binding Source={x:Static HorizontalAlignment.Center} }"/>
</StackPanel>
//后台MyResource类
public class MyResource
{
public string Message { get; set; } = "Public Property";
public static string StaticString = "StaticString";
public const string ConstString = "ConstString";
}
ElementName
ElementName指定的是绑定其他控件的名称,比如某些控件访问不到UI元素树上的控件,可通过x:Reference实现
<TextBox Name="txt" />
<TextBlock Text="{Binding ElementName=txt, Path=Text}" ToolTip="{Binding ElementName=txt, Path=Text}" />
//下面ToolTip找不到txt,在可视化树上发现tooltip没有出现在WIndow上,而是出现在PopupRoot上,
属于弹出来得一个东西,脱离了WIndow
<TextBlock Text="{Binding ElementName=txt, Path=Text}">
<TextBlock.ToolTip>
<TextBlock Text="{Binding ElementName=txt, Path=Text}" />
</TextBlock.ToolTip>
</TextBlock>
//可以通过x:Reference实现,会在Xmal中查找txt
<TextBlock Text="{Binding ElementName=txt, Path=Text}">
<TextBlock.ToolTip>
<TextBlock Text="{Binding Source={x:Reference Name=txt}, Path=Text}" />
</TextBlock.ToolTip>
</TextBlock>
//DataGridTextColumn不是控件,所以也不能通过ElementName实现
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="{Binding Source={x:Reference Name=txt}, Path=Text}" />
</DataGrid.Columns>
</DataGrid>
RelativeSource
======FindAncestor
//FindAncestor找父级控件,默认是此项
<StackPanel>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=Top}" />
</StackPanel>
//AncestorLevel默认是离本身最近的控件
<StackPanel Tag="Level 3">
<StackPanel Tag="Level 2">
<StackPanel Tag="Level 1">
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel, AncestorLevel=2}, Path=Tag}" />
</StackPanel>
</StackPanel>
</StackPanel>
=========Self
//绑定控件本身属性的写法
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=Self}, Path=ActualWidth}" />
等价于
<TextBlock Text="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=ActualWidth}" />
<ListBox>
//每个TextBlock是ListBoxItem的内容
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" />
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" />
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" />
</ListBox>
=====TemplatedParent
<Label Width="300" Height="50" Margin="10" Content="Hello World">
<Label.Template>
<ControlTemplate TargetType="Label">
<Border Padding="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Margin}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Label.Template>
</Label>
<Label Width="300" Height="50" Margin="10" Content="Hello World">
<Label.Template>
<ControlTemplate TargetType="Label">
<Border>
<ContentPresenter Margin="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Padding,Mode=OneWay}" />
//等价于下面行
<ContentPresenter Margin="{TemplateBinding Padding}" />
</Border>
</ControlTemplate>
</Label.Template>
</Label>
StringFormat
<Grid>
<StackPanel>
//显示窗体Top属性值 102
<TextBlock Text="{Binding ElementName=window, Path=Top}" />
//按照特定格式显示窗体top属性值 Top:102.231
<TextBlock Text="{Binding ElementName=window, Path=Top, StringFormat='Top:{0:F3}'}" />
<TextBlock Text="{Binding ElementName=window, Path=Left, StringFormat='Left:{0:F3}'}" />
//多个绑定 Pos:102.231 202.222
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="Pos:{0} {1}">
<Binding ElementName="window" Path="Top" />
<Binding ElementName="window" Path="Left" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
//label使用ContentStringFormat约定格式
<Label Content="{Binding ElementName=window, Path=Top}" ContentStringFormat="Top:{0:F3}" />
<Label Content="{Binding ElementName=window, Path=Left}" ContentStringFormat="Left:{0:F3}" />
</StackPanel>
</Grid>
TargetNullValue和*FallbackValue*
//当Message为空时,显示的文本是Empty
<TextBlock Text="{Binding Message, TargetNullValue=Empty}" />
//当Hello属性没有时或绑定错误时显示Empty 针对无法绑定(给定的Path不存在)时使用的默认值,
<TextBlock Text="{Binding Hello, FallbackValue=Empty}" />
在B站上看完一些视频后记录的。