RelativeSource详解
在进行Binding的时候,如果能够明确到数据源属性的Path(Name),就可用Source、ElementName进行指定,但是有时候需要绑定的数据源可能没有明确的Path(Name),此时就需要利用Binding对象的RelativeSource属性来进行绑定源属性的指定,说白了,就是指定当前绑定目标与绑定源树状结构下的层级关系,
relative to the position of the binding target.
Binding对象的RelativeSource的属性是RelativeSource类型的,不妨看看它的定义
// relative to the position of the binding target. [MarkupExtensionReturnType(typeof(RelativeSource))] public class RelativeSource : MarkupExtension, ISupportInitialize { // Initializes a new instance of the System.Windows.Data.RelativeSource class. public RelativeSource(); // One of the System.Windows.Data.RelativeSourceMode values. public RelativeSource(RelativeSourceMode mode); // One of the System.Windows.Data.RelativeSourceMode values. For this signature // to be relevant, this should be System.Windows.Data.RelativeSourceMode.FindAncestor. // // ancestorType: // The System.Type of ancestor to look for. // // ancestorLevel: // The ordinal position of the desired ancestor among all ancestors of the given // type. public RelativeSource(RelativeSourceMode mode, Type ancestorType, int ancestorLevel); // A static System.Windows.Data.RelativeSource. public static RelativeSource PreviousData { get; } // A static System.Windows.Data.RelativeSource. public static RelativeSource Self { get; } // A static System.Windows.Data.RelativeSource. public static RelativeSource TemplatedParent { get; } // The ancestor level. Use 1 to indicate the one nearest to the binding target element. public int AncestorLevel { get; set; } // 摘要: // Gets or sets the type of ancestor to look for. // // 返回结果: // The type of ancestor. The default value is null. public Type AncestorType { get; set; } // Gets or sets a System.Windows.Data.RelativeSourceMode value that describes the // location of the binding source relative to the position of the binding target. [ConstructorArgument("mode")] public RelativeSourceMode Mode { get; set; } // Returns an object that should be set as the value on the target object's property // for this markup extension. For System.Windows.Data.RelativeSource, this is another // System.Windows.Data.RelativeSource, using the appropriate source for the specified public override object ProvideValue(IServiceProvider serviceProvider); // true if the property value has changed from its default; otherwise, false. [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeAncestorLevel(); // true if the property value has changed from its default; otherwise, false. [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeAncestorType(); }
1. 绑定源是自己 Self
说明绑定源是自身,需要完成的需求是将自身的一个属性绑定到另外一个属性,绑定源与绑定目标都是自己,使用中的绑定目标属性必须是DependencyProperty,重要的事情多说一次,虽然WPF控件中的属性基本都是依赖属性,但是也不排除我们自己开发了控件有些属性不是依赖属性的情况。
比如我们将textBox2的目标属性TextProperty绑定到它的Name属性(源属性)
-
后台代码的方式
textBox2.SetBinding(TextBox.TextProperty, new Binding() { RelativeSource = new RelativeSource() { Mode = RelativeSourceMode.Self, }, Path = new PropertyPath("Name") }); -
前台xaml的方式
<TextBox x:Name="textBox2" Text="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Name}" MinWidth="120" Margin="5"/> 哦豁,有没有发现后台代码中是和绑定定义的4个元素(绑定源、绑定源属性、绑定目标、绑定目标属性)是一致的TextBox.TextProperty这个依赖属性,那么为什么xaml中绑定目标属性的写法的Text呢?猜想这个与
MarkupExtension
有关,这里的内容就不讲解了。思考:通过xaml的绑定写法发现"{}"这个花括号代表了一个对象实例而紧随"{"的是类型名,"{Binding RelativeSource={RelativeSource Mode=Self}, Path=Name}"其中没有加粗的RelatvieSource是Binding对象的属性名,这里是通过观察到的现象,当然清楚这个原理写xaml的时候也会是一个有据可循的依据。
2. 绑定源是父级容器 FindAncestor
说明绑定源是当前绑定目标的父级容器,可以指定明确的父容器的类型或层级关系,查找的顺序是由近到远。
-
后台代码的方式
tbFindAncestor.SetBinding(TextBox.TextProperty, new Binding() { RelativeSource = new RelativeSource() { Mode = RelativeSourceMode.FindAncestor, AncestorType = typeof(WrapPanel), //AncestorLevel = 1 }, Path = new PropertyPath("Name") }); -
前台xaml的方式
<WrapPanel x:Name="WrapPanelName"> <Border BorderThickness="1" BorderBrush="Blue"> <TextBox x:Name="tbFindAncestor" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type WrapPanel}}, Path=Name}" MinWidth="120"/> </Border> </WrapPanel> 可以看到AncestorLevel的值可以不需要提供,没有指定它会使用节点上最近的对象作为绑定源,另外通过后台代码与xaml的比较,xaml的可视化会更好一些,在vs中可以展示绑定完成后的数据展示,但是代码的方式有助于对概念上的理解以及对于MarkupExtension的理解。
注意:AncestorLevel指的是以Bingding目标控件为起点的层级偏移量,Border的偏移量是1,WrapPanel的偏移量是2, AncestorType指的是要找的目标对象的类型,AncestorLevel必须参考AncestorType使用,比如设置了AncestorType={x:Type WrapPanel},则Bingding在寻找时会忽略非WrapPanel的控件,此时WrapPanel的偏移量是1,Border不是WrapPanel类型所以被忽略
3. 绑定源是先前的对象 PreviousData
PreviousData使用较少,它用于特定的情况;这里先不做详细的介绍,后面碰到合适的用例时再补充。
4. 绑定源是关联的模板属性 TemplatedParent
注意我们这里说的是ControlTemplate不是DataTemplate,这里讲解"{Binding RelativeSource={RelativeSource Mode=TemplatedParent}",主要的用途是我们编写了一个ControlTemplate需要在这个ControlTemplate中使用应用这个ControlTemplate的控件的属性值的情况,比如在ControlTemplate将应用模板的控件作为绑定源给ControlTemplate中的设计元素的属性绑定,可以是原始已经有的属性,也可以是没有需要去扩展控件的;另外也可以通过代码的方式获取ControlTemplate中设计的控件元素(var control = YourControl.Template.FindName("elementControl", YourControl
);所以这里对于控件自定义、或者对已控件做形态表现上的修改是很重要的。
-
下面为Button定义一个圆形的ControlTemplate,使用TemplatedParent的绑定让ControlTemplate中的Ellipse的FillProptery等于使用这个ControlTemplate的Background属性作为绑定源的属性
<Button Width="64" Height="64" Content="Ellipse" Background="OrangeRed"> <Button.Template> <ControlTemplate TargetType="Button"> <Grid> <Ellipse Fill="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Background}"/> <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/> </Grid> </ControlTemplate> </Button.Template> </Button>
注意:这里是为了讲解TemplatedParent的使用方式,实际上处理上述需要可以简单的写成
<Ellipse Fill="{TemplateBinding Background}"/>
;是不是比<Ellipse Fill="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Background}"/>
简化了很多, 它们表示的意思是一样的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
2017-11-01 简单实现一个Unity3d的Timer