【avalonia教程】15Binding的其他属性(2)
接下来将Binding的最后三个属性:
- ElementName: 通过元素名称引用同一视图中的其他控件作为绑定源
- Source: 显式指定绑定的数据源对象
- RelativeSource: 指定相对绑定源,如 RelativeSource.Self 或 RelativeSource.FindAncestor
先说ElementName。我们之前讲过,把一个控件的属性绑定到另一个控件上,可以这么写:
<TextBlock Text="{Binding #DemoBox.Text}"></TextBlock>
<TextBox Name="DemoBox"></TextBox>
通过ElementName属性指定其他控件后,可以省略#DemoBox,代码如下:
<TextBlock Text="{Binding Text, ElementName=DemoBox}"></TextBlock>
<TextBox Name="DemoBox"></TextBox>
然后是Source属性,用来显视指定控件绑定的数据源。之前控件的数据源,默认都是DataContext,现在可以显示指定成其他对象。
<TextBox Name="DemoBox" Text="{Binding Name, Source={x:Static vm:Student.Instance}}"></TextBox>
最后是RelativeSource
。官方的说法是:RelativeSource 是 Avalonia 中一种绑定源指定方式,允许元素相对于自身或其他元素建立绑定关系,而无需显式命名元素。
是不是有点难理解?没关系我们点进去看一下,会发现有以下4中枚举值可选:
DataContext
TemplatedParent
Self
FindAncestor
是不是能稍微看出点意思来?其实就是指定数据源,之前我们讲的大部分场景,都是指定的控件找到的DataContext,接下来我们看下指定Self的示例:
<TextBox Name="DemoBox" Text="{Binding Name, RelativeSource={RelativeSource Self}}"></TextBox>
运行后你会发现,Text和它自身的Name属性做了绑定。
TemplatedParent是用在ControlTemplate的内部,这块我们暂时还理解不了,等讲解了ControlTemplate后再来讲解。
最后我们再来看下FindAncestor
。官方的说法是:FindAncestor 是 RelativeSource 的一种模式,用于在视觉树中向上查找指定类型的祖先元素作为绑定源。
很明显,这个是用来将指定类型的父组件作为绑定源。使用代码如下:
<TextBox Name="DemoBox" Text="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel,AncestorLevel=2}}"/>
AncestorType指的父组件的类型,AncestorLevel指定第几层的父组件,默认为1。这里定义为2,按理说就相当于我们之前写的$parent[1],但实际情况不是这样,同时我们看源码自己写的测试用例,也应该是这么用的。合理怀疑这是个bug。
相信大家这里看到了另一种在axaml文件中定义对象的方法:
RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel,AncestorLevel=2}
为什么这么写,就可以定义一个RelativeSource的对象了呢?点进去代码,发现里面有一段逻辑:
public RelativeSource ProvideValue(IServiceProvider serviceProvider){return new RelativeSource(){Mode = this.Mode,AncestorType = this.AncestorType,AncestorLevel = this.AncestorLevel,Tree = this.Tree};}
依葫芦画瓢,我们仿着写一下:
public class MainWindowViewModel
{public MainWindowViewModel ProvideValue(IServiceProvider serviceProvider){return new MainWindowViewModel(){PageDesc = this.PageDesc};}public string? PageDesc { get; set; }
}
<TextBox Name="DemoBox1" DataContext="{vm:MainWindowViewModel PageDesc=test2233}" Text="{Binding #DemoBox1.DataContext.PageDesc}"/>
会发现可以实现一样的效果。
有些同学可能也会问:为什么第一个Mode属性不用写名字呢?因为RelativeSource有一个带Mode参数的构造函数