• Weird behavior of DataContext Inheritance


    Actually there are several questions in this post, though all of them are about DataContext inheritance. I think you will have have fun with these questions, if anyone can explain what's going on here, it will be greatly appreciated, however it's really not that easy to answer.

    I created a CustomControl which derives from ContentControl by adding a Gutter property, so that the user of this control can specify two part of this control Content and Gutter. Here is my codes:

    public class BizField : ContentControl
       {
           static BizField()
           {
               DefaultStyleKeyProperty.OverrideMetadata(typeof(BizField), new FrameworkPropertyMetadata(typeof(BizField)));
           }
    
           public object Gutter
           {
               get { return (object)GetValue(GutterProperty); }
               set { SetValue(GutterProperty, value); }
           }
    
           // Using a DependencyProperty as the backing store for Gutter.  This enables animation, styling, binding, etc...
           public static readonly DependencyProperty GutterProperty =
               DependencyProperty.Register("Gutter",
                               typeof(object), 
                               typeof(BizField), 
                               new UIPropertyMetadata(null,
                                   new PropertyChangedCallback(OnGutterChanged)));
    
    
           private static void OnGutterChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
           {
               var me = sender as BizField;
               me.OnGutterChanged(e);
           }
    
           private void OnGutterChanged(DependencyPropertyChangedEventArgs e)
           {
             
           }
       }
    

    The template is quite simple:

        <Style TargetType="{x:Type local:BizField}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:BizField}">
                        <Border Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}">
                            <StackPanel>
                                <ContentPresenter/>
                                <ContentPresenter ContentSource="Gutter"/>
                            </StackPanel>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    

     

    Now if I use it in the main window, it works as expected:

    <Window x:Class="DataContexPropagate.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:DataContexPropagate"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <local:BizField>
                <Button Content="Click one"/>
                <local:BizField.Gutter>
                    <Button Content="{Binding}"/>
                </local:BizField.Gutter>
            </local:BizField>
        </Grid>
    </Window>
    
    
    public partial class MainWindow : Window
      {
          public MainWindow()
          {
              //Notice DataContext is set before parsing the baml
              this.DataContext = "Hello";       
              InitializeComponent();
             
          }
      }
    

     

    The button defined in the Gutter shows  "Hello". Now I try to add the gutter as BizField's logical child.

        private void OnGutterChanged(DependencyPropertyChangedEventArgs e)
       {
          this.AddLogicalChild(Gutter);
       }
    

    It still works fine, but if I set the DataContext after loading xaml, it will broke.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {             
            InitializeComponent();
            //Set datacontext after parsing baml
            this.DataContext = "Hello";
           
        }
    }
    

    If you run the application, the button show nothing, apparently DataContext Inheritance doesn’t work properly now. But if i change the button to a TextBlock, it will work. What’s the magic with TextBlock?

    <Window x:Class="DataContexPropagate.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:DataContexPropagate"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <local:BizField>
                <Button Content="Click one"/>
                <local:BizField.Gutter>
                    <TextBlock Text="{Binding}"/>
                </local:BizField.Gutter>
            </local:BizField>
        </Grid>
    </Window>
    
    

    To make the databinding works for the button, i have to add following codes to BizControl.

    protected override System.Collections.IEnumerator LogicalChildren
         {
             get
             {
                 yield return Content;
                 yield return Gutter;
             }
         }
    

    According to this example, you will find that "setting datacontext before or after parsing Baml does matter".  But i don't why and more intersting what happened to the TextBlock?

    Full source codes for BizField:

    public class BizField : ContentControl
       {
           static BizField()
           {
               DefaultStyleKeyProperty.OverrideMetadata(typeof(BizField), new FrameworkPropertyMetadata(typeof(BizField)));
           }
    
    
    
           public object Gutter
           {
               get { return (object)GetValue(GutterProperty); }
               set { SetValue(GutterProperty, value); }
           }
    
           // Using a DependencyProperty as the backing store for Gutter.  This enables animation, styling, binding, etc...
           public static readonly DependencyProperty GutterProperty =
               DependencyProperty.Register("Gutter",
                               typeof(object),
                               typeof(BizField),
                               new UIPropertyMetadata(null,
                                   new PropertyChangedCallback(OnGutterChanged)));
    
    
           private static void OnGutterChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
           {
               var me = sender as BizField;
               me.OnGutterChanged(e);
           }
    
           private void OnGutterChanged(DependencyPropertyChangedEventArgs e)
           {
               //Comment this out, it will search for the visual parent.
               this.AddLogicalChild(Gutter);
           }
    
           //Only by adding this method, it can work properly.
           protected override System.Collections.IEnumerator LogicalChildren
           {
               get
               {
                   yield return Content;
                   yield return Gutter;
               }
           }
       }
    
     
     
    <Window x:Class="DataContexPropagate.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:DataContexPropagate"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <local:BizField>
                <Button Content="Click one"/>
                <local:BizField.Gutter>
                    <Button Content="{Binding}"/>
                    <!--<TextBlock Text="{Binding}"/>-->
                </local:BizField.Gutter>
            </local:BizField>
        </Grid>
    </Window>
    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            //Notice DataContext is set before parsing the baml
            //this.DataContext = "Hello";        
            InitializeComponent();
            //Set datacontext after parsing baml
            this.DataContext = "Hello";
           
        }
    }
    
  • 相关阅读:
    在Myeclipse中配置tomcat
    tomcat用startup.bat启动时,界面一闪消失
    单向链表的基本操作
    简单排序的对比
    关于string的排序 选择插入排序
    insertsort
    selectsort
    理解函数式编程
    vue中使用$nextTick后任然无法正确计算出元素高度
    使用vuex+vue-i18n方式国际化
  • 原文地址:https://www.cnblogs.com/idior/p/1757974.html
Copyright © 2020-2023  润新知