前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2019-7-24-为啥在Code Behind进行RelativeSource的binding会丢失

2019-7-24-为啥在Code Behind进行RelativeSource的binding会丢失

作者头像
黄腾霄
发布2020-06-10 14:31:48
6000
发布2020-06-10 14:31:48
举报
文章被收录于专栏:黄腾霄的博客黄腾霄的博客

不知道是不是有小伙伴遇到过这样的情况。当我们尝试将一个RelativeSource从xaml转到code behind时,原本好好的binding表达式居然出现binding错误。


我们来做个实验,我们创建了一个UserControl里面有一个TextBlock,其Text属性绑定了类型为Window的父元素的Title。而我们的window里面的title写了”黄腾霄瘦瘦瘦”这个美好的祝愿。(下面的代码删掉了xmlns)

代码语言:javascript
复制
<UserControl x:Class="WpfApp1.UserControl1">
    <Grid>
        <TextBlock Name="TextBlock" Text="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=Title}" />
    </Grid>
</UserControl>
代码语言:javascript
复制
<Window x:Class="WpfApp1.MainWindow"
        Title="黄腾霄瘦瘦瘦" Height="450" Width="800">
    <Grid x:Name="Grid">
        
    </Grid>
</Window>

下面是关键点,我们没有按照通常的写法,通过xaml添加usercontrol1,而是在code behind的构造函数中添加

代码语言:javascript
复制
        public MainWindow()
        {
            InitializeComponent();
            var userControl1 = new UserControl1();
            Grid.Children.Add(userControl1);
        }

启动运行后一切正常

1563966301790
1563966301790

此时我们将textblock的binding改到code behind中

代码语言:javascript
复制
        public UserControl1()
        {
            InitializeComponent();
            var binding = new Binding()
            {
                Source = new RelativeSource
                {
                    AncestorType = typeof(Window),
                    Mode = RelativeSourceMode.FindAncestor,
                    AncestorLevel = 1,
                },
                Path = new PropertyPath("Title"),
                Mode = BindingMode.OneWay
            };
            TextBlock.SetBinding(TextBlock.TextProperty, binding);
        }

惊,textblock的文字没有啦,而且我们看到一个绑定错误。

1563966844746
1563966844746
1563966937197
1563966937197

我勒个去,明明就是等价的代码嘛

我们试试添加一些调试信息(参考德熙的博客WPF 如何调试 binding

代码语言:javascript
复制
        public UserControl1()
        {
            InitializeComponent();
            var binding = new Binding()
            {
                Source = new RelativeSource
                {
                    AncestorType = typeof(Window),
                    Mode = RelativeSourceMode.FindAncestor,
                    AncestorLevel = ,
                },
                Path = new PropertyPath("Title"),
                Mode = BindingMode.OneWay
            };
            PresentationTraceSources.SetTraceLevel(binding, PresentationTraceLevel.High);
            TextBlock.SetBinding(TextBlock.TextProperty, binding);
        }

输出如下,我们看到在执行到TextBlock.SetBinding(TextBlock.TextProperty, binding);时,WPF就开始寻找RelativeSource,而此时我们的控件还未被加入到逻辑树,所以他根本找不到父元素

1563967377152
1563967377152

那么为啥写在xaml里面就没事呢?我们对其添加调试输出看看

代码语言:javascript
复制
<UserControl x:Class="WpfApp1.UserControl1">
    <Grid>
        <TextBlock Name="TextBlock"
                   Text="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=Title,PresentationTraceSources.TraceLevel=High}" />
    </Grid>
</UserControl>

在执行InitializeComponent时,WPF进行了绑定。但是此时发现RelativeSource (FindAncestor) requires tree context,我们在查找RelativeSource 时,需要有上下文树,而此时条件不满足,我们的绑定就被延迟了

1563968181190
1563968181190

而在之后,WPF再次解析绑定Source,通过视觉树找到了window,所以绑定能够成功

1563968364137
1563968364137

所以下次想要对在后台生成的对象进行Relative绑定时,需要注意咯

参考链接:


本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/%E4%B8%BA%E5%95%A5%E5%9C%A8Code-Behind%E8%BF%9B%E8%A1%8CRelativeSource%E7%9A%84binding%E4%BC%9A%E4%B8%A2%E5%A4%B1.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-07-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档