双向绑定不能在一个方向上工作

本文关键字:一个 方向 工作 绑定 不能 | 更新日期: 2023-09-27 18:07:57

我创建了一个非常简单的例子来显示我的问题。也许是我想错了。

我想在我的TreeView中选择一个项目-我想在视图中看到它(蓝色背景)。

为了实现TwoWayBinding,我使用以下行为:数据绑定到WPF树视图中的SelectedItem

public class BindableSelectedItemBehavior : Behavior<TreeView>
{
    #region SelectedItem Property
    public object SelectedItem
    {
        get { return (object)GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object), typeof(BindableSelectedItemBehavior), new UIPropertyMetadata(null, OnSelectedItemChanged));
    private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var item = e.NewValue as TreeViewItem;
        if (item != null)
        {
            item.SetValue(TreeViewItem.IsSelectedProperty, true);
        }
    }
    #endregion
    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
    }
    protected override void OnDetaching()
    {
        base.OnDetaching();
        if (this.AssociatedObject != null)
        {
            this.AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
        }
    }
    private void OnTreeViewSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        this.SelectedItem = e.NewValue;
    }
}

但是如果我点击一个项目它不会进入OnSelectedItemChanged的if因为e.newValue as TreeViewItemnull

我的XAML很简单:

<StackPanel>
    <TreeView xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
      ItemsSource="{Binding Items}">
        <i:Interaction.Behaviors>
            <local:BindableSelectedItemBehavior
                SelectedItem="{Binding Item}" />
        </i:Interaction.Behaviors>
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate>
                <TextBlock Text="{Binding Text}"/>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    <TextBox Text="{Binding Item.Text}"/>
</StackPanel>

谢谢大家!

双向绑定不能在一个方向上工作

为了方便起见,下面是OP和ghrod的答案的最终解决方案:

namespace MyPoject.Behaviors
{
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Interactivity;
  public class BindableSelectedItemBehavior : Behavior<TreeView>
  {
    #region SelectedItem Property
    public object SelectedItem
    {
      get { return (object)GetValue(SelectedItemProperty); }
      set { SetValue(SelectedItemProperty, value); }
    }
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register(
          nameof(SelectedItem),
          typeof(object),
          typeof(BindableSelectedItemBehavior),
          new FrameworkPropertyMetadata(null, 
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
            OnSelectedItemChanged));
    static void OnSelectedItemChanged(DependencyObject sender, 
      DependencyPropertyChangedEventArgs e)
    {
      var behavior = (BindableSelectedItemBehavior)sender;
      var generator = behavior.AssociatedObject.ItemContainerGenerator;
      if (generator.ContainerFromItem(e.NewValue) is TreeViewItem item)
        item.SetValue(TreeViewItem.IsSelectedProperty, true);
    }
    #endregion
    protected override void OnAttached()
    {
      base.OnAttached();
      AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
    }
    protected override void OnDetaching()
    {
      base.OnDetaching();
      if (this.AssociatedObject != null)
        AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
    }
    void OnTreeViewSelectedItemChanged(object sender, 
        RoutedPropertyChangedEventArgs<object> e) =>
      SelectedItem = e.NewValue;
  }
}

TreeViewSelectedItem属性在您的情况下不返回TreeViewItem。它从绑定的Items集合中返回当前选定的项目。要从SelectedItem获得TreeViewItem,您需要在这里使用ItemContainerGenerator:

private static void OnSelectedItemChanged(DependencyObject sender, 
  DependencyPropertyChangedEventArgs e)
{
    var behavior = (BindableSelectedItemBehavior)sender;
    var generator = behavior.AssociatedObject.ItemContainerGenerator;
    var item = generator.ContainerFromItem(e.NewValue) as TreeViewItem;
    if (item != null)
    {
        item.SetValue(TreeViewItem.IsSelectedProperty, true);
    }
}

你的OnSelectedItemChanged只会传递一个类型为TreeViewItem的对象,如果你的实际模型对象是类型为TreeViewItem,这不会是99%的情况下。相反,你必须从模型对象中检索TreeViewItem,但是如果节点当前是折叠的,它将不可用,这使得选择折叠的节点非常重要。

我已经在我的博客中非常彻底地解释了这一点,包括这里的代码示例