惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
云风的 BLOG
云风的 BLOG
G
Google Developers Blog
S
SegmentFault 最新的问题
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
美团技术团队
爱范儿
爱范儿
博客园 - 三生石上(FineUI控件)
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
DataBreaches.Net
F
Fortinet All Blogs
TaoSecurity Blog
TaoSecurity Blog
D
Docker
C
Cybersecurity and Infrastructure Security Agency CISA
K
Kaspersky official blog
宝玉的分享
宝玉的分享
腾讯CDC
Google Online Security Blog
Google Online Security Blog
Recorded Future
Recorded Future
T
The Exploit Database - CXSecurity.com
T
The Blog of Author Tim Ferriss
V
V2EX
S
Securelist
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
C
CERT Recently Published Vulnerability Notes
A
Arctic Wolf
Scott Helme
Scott Helme
L
LINUX DO - 热门话题
Y
Y Combinator Blog
P
Proofpoint News Feed
T
Tor Project blog
AWS News Blog
AWS News Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The Last Watchdog
The Last Watchdog
博客园 - 聂微东
T
Threat Research - Cisco Blogs
B
Blog
Attack and Defense Labs
Attack and Defense Labs
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
阮一峰的网络日志
阮一峰的网络日志
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
N
News and Events Feed by Topic
博客园 - 司徒正美
H
Help Net Security
C
Cisco Blogs
C
Check Point Blog
S
Secure Thoughts

博客园 - idior

每日代码 - 7/1 减小方法参数的依赖 每日代码 - 6/29 读写分离 每日代码 - 6/28 代码逻辑分组 每日代码 - 6/26 lambda表达式 每日代码 - 6/27 避免创建非法对象 Resume Covariance and Contravariance How does ElementName Binding work? Weird behavior of DataContext Inheritance How does ElementName Binding work – Part 3 InheritanceContext How does ElementName Binding work – Part 2 BindingExpression Logical Tree & Visual Tree Memory leak caused by EventHandle - weak event Resources on Debugging/Tracing WPF Inside WCF Runtime 有谁准备参加10月份的MVP聚会? MVP聚会 Practical .NET2 and C#2 翻译样章 Be evil or not?
How does ElementName Binding work? - Part 1 Logical Tree & NameScope
idior · 2010-05-28 · via 博客园 - idior

Most developers have used {Binding ElementName= myControl , Path = myProperty} in their WPF projects, however you may find it didn’t work for you in certain cases, especially when you were building a complex control in which lots of controls nested.

To understand the ElementName binding, we must understand NameScope first, since ElementName binding will use NameScope.FindName to find control internally.

I don’t want to talk about the concept of NameScope too much in this article; MSDN already gave a good description for it. It comes into play when you give a name to the control defined in the template, see following codes:

<Window>

    <Window.Resources>

        <ControlTemplate x:Key="template"  TargetType="{x:Type Button}">

            <Rectangle Name="foo"/>

        </ControlTemplate

    </Window.Resources>

            <Button Template="{StaticResource template}"/>

            <Button Template="{StaticResource template}"/>

</Window>

Without NameScope, there will be two buttons with the same name in the window, which will defiantly cause an error for us. How NameScope rescue?

Every template has its own NameScope , an instance of TemplateNameScope type. I think this is known to most developer; however you may not heard of that every UserControl has it own NameScope.

Now let’s see how NameScope.FindName works. Actually the idea is very simple; each element defined in the xaml with Name Property will be registered in a nameMap dictionary in its nearest NameScope. NameScope.FindName will simply get the element from the nameMap. You may wonder what’s the nearest NameScope? Here nearest means the nearest element which has a NameScope in the logic tree.

One thing you should keep in mind is that not every element has a NameScope, the top level window has a NameScope, each UserControl has a NameScope, and each element which defined at the top level in a ControlTemplate has a NameScope.

Notes

TemplateNameScope is a little different from NameScope. Elements have names  which are defined in a template will not be registered into the nameMap. I don’t want to digger into that too much, you just need to know TemplateNameScope.FindName is not just relay on nameMap dictionary and it’s a little different from NameScope.FindName, but we can still use it to find the elements defined in the template by using its name.

 

Now let’s go back to the element name binding. After studying the source codes of WPF, I find ElementName Binding use ElementObjectRef class for finding the element via its name. By looking into the source code, we can easily find how it works.

  1. Start from the element which applied the ElementName Binding, keep searching on the logic tree via its logic parent, until an element which has NameScope is found, let’s call it NameScopeElement. If no element owns a NameScope, search will stop.
  2. Call the NameScope.FindName method on the found NameScope.
  3. If the element is found, return it, otherwise try to get the template parent of NameScopeElement; if the template parent is null, it will stop search. or it goes back to step 1, search on the logic tree for element owns a NameScope.

Now let’s see an example in which ElementName Binding doesn’t work properly.

<Window x:Class="TestElementBindingInStyle.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:local="clr-namespace:TestElementBindingInStyle"

    Title="Window1" Height="300" Width="300" x:Name="root" >  

      <local:MyUC Grid.Row="0" x:Name="myuc" IsEnabled="false" >

        <local:MyUC.MyContent >

          <Button Content="{Binding ElementName=myuc, Path=IsEnabled}"  />

        </local:MyUC.MyContent>

      </local:MyUC>  

</Window>

<UserControl x:Class="TestElementBindingInStyle.MyUC"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:uc="clr-namespace:TestElementBindingInStyle"

    x:Name="root">

    <ContentPresenter Content="{Binding ElementName= root,Path= MyContent}" Grid.Row="0"/>

</UserControl>

    public partial class MyUC : UserControl

    {

        public MyUC()   {

            InitializeComponent();

        }

        /// <summary>

        /// MyContent Dependency Property

        /// </summary>

        public static readonly DependencyProperty MyContentProperty =

            DependencyProperty.Register("MyContent", typeof(object), typeof(MyUC),

                new FrameworkPropertyMetadata(null ,

                    new PropertyChangedCallback(OnMyContentChanged)));

        /// <summary>

        /// Gets or sets the MyContent property. This dependency property

        /// indicates ....

        /// </summary>

        public object MyContent

        {

            get { return (object)GetValue(MyContentProperty); }

            set { SetValue(MyContentProperty, value); }

        }

        /// <summary>

        /// Handles changes to the MyContent property.

        /// </summary>

        private static void OnMyContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

        {

            ((MyUC)d).OnMyContentChanged(e);

        }

        /// <summary>

        /// Provides derived classes an opportunity to handle changes to the MyContent property.

        /// </summary>

        protected virtual void OnMyContentChanged(DependencyPropertyChangedEventArgs e)

        {

             this.AddLogicalChild(e.NewValue);

        }

    }

I have highlighted the binding which doesn’t work. Now let’s go though the finding path to see why it doesn’t work here.

The search starts from myBtn, it will check whether it has a NameScope, apparently it doesn’t. Then we move to its logic parent which is myuc, since it’s a UserControl, it does have a NameScope; however we cannot find myuc in this Scope, because myuc is registered in the Window’s NameScope. Now it will try to get the TemplateParent of myuc, but the value is null, so the search stops. It cannot find Element whose name is myuc.

Now my question is what‘s the correct approach for making this binding work? Use RelativeSource Binding. 

<local:MyUc x:Name="myuc" IsEnabled="false">

            <local:MyUc.MyContent >

                <Button Content="{Binding Path=IsEnabled, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type local:MyUc}}}" />

            </local:MyUc.MyContent>

</local:MyUc> 

顺便做个广告,道富信息科技和网新恒天都招聘.NET高级开发人员和架构师。需要对.NET和面向对象设计有比较深入的了解和较好的英文读写能力,如果有WPF 或Silverlight开发经验更佳,工作地点杭州。简历请发至 nxu  [at] statestreet [dot] com。