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

推荐订阅源

Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
I
InfoQ
宝玉的分享
宝玉的分享
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
P
Privacy International News Feed
T
Threatpost
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Vulnerabilities – Threatpost
NISL@THU
NISL@THU
aimingoo的专栏
aimingoo的专栏
S
Schneier on Security
C
Cisco Blogs
T
The Blog of Author Tim Ferriss
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Jina AI
Jina AI
雷峰网
雷峰网
Know Your Adversary
Know Your Adversary
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
I
Intezer
博客园 - Franky
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
The Hacker News
The Hacker News
K
Kaspersky official blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tailwind CSS Blog
Project Zero
Project Zero
T
Tor Project blog
B
Blog RSS Feed
Recorded Future
Recorded Future
Scott Helme
Scott Helme
美团技术团队
V
V2EX
V
Visual Studio Blog
L
Lohrmann on Cybersecurity
P
Proofpoint News Feed
D
DataBreaches.Net
The Register - Security
The Register - Security
M
MIT News - Artificial intelligence
L
LangChain Blog
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Cyber Attacks, Cyber Crime and Cyber Security
博客园_首页
P
Privacy & Cybersecurity Law Blog

博客园 - 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。