代码之家  ›  专栏  ›  技术社区  ›  Danko Durbić

WPF:为什么DataContextChanged没有在逻辑子级上引发?

  •  2
  • Danko Durbić  · 技术社区  · 17 年前

    DataContextChanged 未在自定义面板控件的逻辑子级上引发。我把范围缩小到:

    从向导生成的WPF应用程序开始,我添加:

        private void Window_Loaded( object sender, RoutedEventArgs e )
        {
            var elt = new FrameworkElement();
            this.AddLogicalChild( elt );
            DataContext = 42;
            Debug.Assert( (int)elt.DataContext == 42 );
        }
    

    据我所知,这是因为 数据上下文 inheritable dependency property .

    现在,我为添加事件处理程序 DataContextChanged 在窗口(此)及其逻辑子级上:

        this.DataContextChanged += 
            delegate { Debug.WriteLine( "this:DataContextChanged" ); };
        elt.DataContextChanged += 
            delegate { Debug.WriteLine( "elt:DataContextChanged" ); };
    

    如果我运行这个, 将执行。 为什么会这样? 如果不使用AddLogicalChild(elt),我将执行以下操作:

    this.Content = elt;
    

    两个处理程序都将执行。但在我的例子中,这不是一个选项——我将FrameworkContentElements添加到我的控件中,这些元素不应该是可视的子元素。

    (幸运的是,有一个相当简单的解决方法-只需将元素的DataContext绑定到窗口的DataContext)

    BindingOperations.SetBinding( elt, FrameworkElement.DataContextProperty, 
                new Binding( "DataContext" ) { Source = this } );
    

    非常感谢。

    1 回复  |  直到 17 年前
        1
  •  7
  •   Tim Cooper    14 年前

    您需要覆盖 LogicalChildren 财产也是:

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

    当然,您还需要返回由基本实现定义的任何逻辑子级。

        2
  •  1
  •   Lord Tasci    5 年前

    • 创建自己的枚举器类,并在重写的LogicalChildren属性中返回该类的实例

    如果不将内容对象添加到逻辑树,则可能会遇到诸如无法解析与ElementName的绑定之类的问题(ElementName由FindName解析,FindName反过来使用LogicalTree查找元素)。

    如果不重写LogicalChildren,则不会像上面所述那样更新DataContext。

    拆分容器:

        public class SplitContainer : Control
    {
        static SplitContainer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitContainer), new FrameworkPropertyMetadata(typeof(SplitContainer)));
        }
    
        /// <summary>
        /// Identifies the <see cref="Child1"/> property.
        /// </summary>
        public static readonly DependencyProperty Child1Property =
            DependencyProperty.Register(nameof(Child1), typeof(object), typeof(SplitContainer), new FrameworkPropertyMetadata(Child1PropertyChangedCallback));
    
        /// <summary>
        /// Left Container
        /// </summary>
        public object Child1
        {
            get { return (object)GetValue(Child1Property); }
            set { SetValue(Child1Property, value); }
        }
    
        /// <summary>
        /// Identifies the <see cref="Child2"/> property.
        /// </summary>
        public static readonly DependencyProperty Child2Property =
            DependencyProperty.Register(nameof(Child2), typeof(object), typeof(SplitContainer), new FrameworkPropertyMetadata(Child2PropertyChangedCallback));
    
        /// <summary>
        /// Right Container
        /// </summary>
        public object Child2
        {
            get { return (object)GetValue(Child2Property); }
            set { SetValue(Child2Property, value); }
        }
    
        private static void Child1PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var splitContainer = (SplitContainer)d;
    
            if (e.OldValue != null)
            {
                splitContainer.RemoveLogicalChild(e.OldValue);
            }
            if (e.NewValue != null)
            {
                splitContainer.AddLogicalChild(((object)e.NewValue));
            }
        }
    
        private static void Child2PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var splitContainer = (SplitContainer)d;
    
            if (e.OldValue != null)
            {
                splitContainer.RemoveLogicalChild(e.OldValue);
            }
            if (e.NewValue != null)
            {
                splitContainer.AddLogicalChild(((object)e.NewValue));
            }
        }
    
        protected override IEnumerator LogicalChildren
        {
            get
            {
                return new SplitContainerLogicalChildrenEnumerator(this);
            }
        }
    }
    

    SplitContainerLogicalChildrenEnumerator:

        internal class SplitContainerLogicalChildrenEnumerator : IEnumerator
    {
        private readonly SplitContainer splitContainer;
        private int index = -1;
    
        public SplitContainerLogicalChildrenEnumerator(SplitContainer splitContainer)
        {
            this.splitContainer = splitContainer;
        }
    
        public object Current
        {
            get
            {
                if (index == 0)
                {
                    return splitContainer.Child1;
                }
                else if (index == 1)
                {
                    return splitContainer.Child2;
                }
                throw new InvalidOperationException("No child for this index available");
            }
        }
    
        public bool MoveNext()
        {
            index++;
            return index < 2;
        }
    
        public void Reset()
        {
            index = -1;
        }
    }
    

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:scm="clr-namespace:System.ComponentModel;assembly=PresentationFramework"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:local="clr-namespace:SplitContainerElementNameProblem">
    <Style TargetType="{x:Type local:SplitContainer}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:SplitContainer}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <ContentPresenter Grid.Column="0"
                                          Content="{TemplateBinding Child1}" />
                        <ContentPresenter Grid.Column="1"
                                          Content="{TemplateBinding Child2}" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    证明每个绑定工作正常的示例:

    XAML:

    <Window x:Class="SplitContainerElementNameProblem.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SplitContainerElementNameProblem"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <TextBox x:Name="text1" Text="abc" />
        </Grid>
        <local:SplitContainer Grid.Row="1">
            <local:SplitContainer.Child1>
                <TextBox x:Name="text2"
                         Text="{Binding ElementName=text1, Path=Text}" />
            </local:SplitContainer.Child1>
            <local:SplitContainer.Child2>
                <StackPanel>
                    <TextBox x:Name="text3"
                             Text="{Binding ElementName=text2, Path=Text}" />
                    <TextBox x:Name="text4"
                             Text="{Binding MyName}" />
                </StackPanel>
            </local:SplitContainer.Child2>
        </local:SplitContainer>
    </Grid>
    

        public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
    
            DataContext = this;
    
            MyName = "Bruno";
        }
    
        public string MyName
        {
            get;
            set;
        }
    }
    
    推荐文章