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

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
The Last Watchdog
The Last Watchdog
T
Tenable Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
Simon Willison's Weblog
Simon Willison's Weblog
V
Vulnerabilities – Threatpost
F
Fortinet All Blogs
Microsoft Security Blog
Microsoft Security Blog
A
Arctic Wolf
云风的 BLOG
云风的 BLOG
Know Your Adversary
Know Your Adversary
P
Palo Alto Networks Blog
GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
MyScale Blog
MyScale Blog
B
Blog
Spread Privacy
Spread Privacy
S
Schneier on Security
Project Zero
Project Zero
L
LINUX DO - 热门话题
M
MIT News - Artificial intelligence
F
Full Disclosure
WordPress大学
WordPress大学
Apple Machine Learning Research
Apple Machine Learning Research
Cyberwarzone
Cyberwarzone
AWS News Blog
AWS News Blog
aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
C
Cybersecurity and Infrastructure Security Agency CISA
Hugging Face - Blog
Hugging Face - Blog
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Tailwind CSS Blog
K
Kaspersky official blog
Recent Announcements
Recent Announcements
NISL@THU
NISL@THU
Cisco Talos Blog
Cisco Talos Blog
S
Securelist
P
Privacy & Cybersecurity Law Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
The Exploit Database - CXSecurity.com
V
Visual Studio Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Webroot Blog
Webroot 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 2 BindingExpression Logical Tree & Visual Tree Memory leak caused by EventHandle - weak event Resources on Debugging/Tracing WPF How does ElementName Binding work? - Part 1 Logical Tree & NameScope Inside WCF Runtime 有谁准备参加10月份的MVP聚会? MVP聚会 Practical .NET2 and C#2 翻译样章 Be evil or not?
How does ElementName Binding work – Part 3 InheritanceContext
idior · 2010-06-12 · via 博客园 - idior

In this part, I am going to introduce a new concept called InheritanceContext. In WPF, there are some elements are not FrameworkElement or even Visual, which means they will not be shown on either Logical Tree or Visual Tree, e.g. Brush, however we still wish they can enjoy the feature called “property value inheritance”. Property value inheritance enables child elements to obtain the value of a particular property from parent elements, inheriting that value as it was set anywhere in the nearest parent element.  

In following example, the Brush can get the DataContext property from its parent, however a Brush is neither a FramewrokElement nor Visual.

<Window x:Class="TestElementBindingInUserControl.MainWindow">
    <StackPanel DataContext="Red">
        <Rectangle Width="75" Height="75">
            <Rectangle.Fill>
                <SolidColorBrush Color="{Binding}" />
            </Rectangle.Fill>
        </Rectangle>
    </StackPanel>
</Window> 

In this example, Brush doesn’t have a logical parent nor visual parent, so how does it get the DataContext property from its parent? The secret is in InheritanceContext. Brush is a Freezable object whose InheritanceContext property is set to the element which contains it.

Here is the Rectangle, while Rectangle inherits the DataContext property from its Logical Parent StatckPanel.

With the help of InheritanceContext, the ElementName Binding will work properly in following codes:

 
<Window x:Class="TestElementBindingInUserControl.MainWindow">
    <StackPanel>
        <Button Content="Blue" x:Name="btn"/>
        <Rectangle Width="75" Height="75">
            <Rectangle.Fill>
                <SolidColorBrush Color="{Binding ElementName=btn, Path=Content}" />
            </Rectangle.Fill>
        </Rectangle>
    </StackPanel>
</Window> 

Although SolidColorBrush doesn’t have a Logical parent, it will try to get its InheritanceContext instead, by which it will reach the Rectangle, then follow by the logical parent, we will find the Window which owns a NameScope; Finally btn will be found in this NameScope.

Now let’s take a look at a popular issue in WPF development: use ElementName in ToolTip or ContextMenu.

<Window x:Class="TestElementBindingInUserControl.MainWindow"
        Title="MainWindow" x:Name="win">
    <Button >
        <Button.ToolTip>
            <TextBox x:Name="tbx" Text="{Binding ElementName=win, Path=Title}"/>
        </Button.ToolTip>
    </Button>
</Window> 

The content defined in the a ToolTip is not part of Logical tree or Visual tree, it’s not a Freezable object either. Then how can we use ElementName binding in the content?

A straightforward solution comes into my mind is to do some hack to set the InheritanceContext property of TextBox to the Window directly. By looking into the source code of FrameworkElement, I found a static filed called InheritanceContextField whose type is UncommonField<DependencyObject> in which there is a SetValue method. By knowing this, I can use reflection to set the InheritanceContext manually.

      void Window_Loaded(object sender, RoutedEventArgs e)
        {
 
            var tooltipContent = tbx.tooltip;
 
            var field = typeof(FrameworkElement).GetField("InheritanceContextField",
                                                          BindingFlags.static | BindingFlags.nonpublic);
 
            var inheritanceContext=field.GetValue(null);
 
            //type of UncommonField<DependencyObject>
            var type = Type.GetType("System.Windows.UncommonField`1[
                                                                    [System.Windows.Dependencyobject,
                                                                     windowsbase,
                                                                     version=3.0.0.0,
                                                                     culture=neutral,
                                                                     publickeytoken=31bf3856ad364e35]
                                                                  ],
                                    windowsbase, version=3.0.0.0, culture=neutral, publickeytoken=31bf3856ad364e35"
);
 
  
 
            var setMethod=type.GetMethod("SetValue");
            setMethod.Invoke(inheritanceContext,new object[]{ tooltipContent,this});
        }

Although it’s not a good fix, it do resolve the problem. Do we have a better solution? Since ElementName binding cannot work here, we can try to use another Binding - Source Binding.

First of all, we need to define a Spy make it inherit from Freezable class,

public class ElementSpy : Freezable
   {
     
       public static readonly DependencyProperty ValueProperty =
           DependencyProperty.Register("Value", typeof(object), typeof(ElementSpy),
               new FrameworkPropertyMetadata(null));
   
       public object Value
       {
           get { return (object)GetValue(ValueProperty); }
           set { SetValue(ValueProperty, value); }
       }

       protected override Freezable CreateInstanceCore()
       {
           throw new NotImplementedException();
       }
   }
 

Then we put it into the Resource of the Window. Since Freezable object’s InheritanceContext will be set to the containing element, here it’s the window, Element binding will work fine for ElementSpy.

<Window x:Class="TestElementBindingInUserControl.MainWindow"
        Title="MainWindow" x:Name="win">
    <Window.Resources>
        <local:ElementSpy x:Key="spy" Value="{Binding ElementName=win, Path=Title}"/>
    </Window.Resources>
</Window> 
 

Now we can simply use ElementSpy as a bridge to make ElementName Binding work for ToolTip.

<Window x:Class="TestElementBindingInUserControl.MainWindow"
        Title="MainWindow" x:Name="win">
    <Window.Resources>
        <local:ElementSpy x:Key="spy" Value="{Binding ElementName=win, Path=Title}"/>
    </Window.Resources>
    <Button >
        <Button.ToolTip>
            <TextBox  Text="{Binding Source={StaticResource spy}, Path=Value}"/>
        </Button.ToolTip>
    </Button>
</Window> 

Here is the rule how ElementName binding works: 

  1. Get the BindingExpression which is created by the ElementName Binding.
  2. Start from the TargetElement of the BindingExpression. If the value of BindingExpression’s ResolveNamesInTemplate property is true, it will search in the TargetElement’s template, if an element with the same name can be found in its template then return it, else go to next step.
  3. Keep searching on the logic tree via its logic parent or InheritanceContext(when logic parent is null), until an element which has NameScope is found, let’s call it NameScopeElement. If no element owns a NameScope, search will stop.
  4. Call the NameScope.FindName method on the found NameScope.
  5. 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 3, search on the logic tree for an element owns a NameScope.

Related resources:

Enable ElementName Bindings with ElementSpy

Artificial Inheritance Contexts in WPF

Hillberg Freezable Trick

Leveraging Freezables to Provide an Inheritance Context for Bindings