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}"/>简化了很多, 它们表示的意思是一样的。

posted @   非法关键字  阅读(1249)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
历史上的今天:
2017-11-01 简单实现一个Unity3d的Timer
点击右上角即可分享
微信分享提示