TreeView SelectedItem 行为 - 双向绑定在一个方向上不起作用
TreeView SelectedItem Behavior - Two Way Binding does not work in One Direction
我创建了一个非常简单的示例来说明我的问题。可能是我想错了。
我想 select 我的 TreeView 的一个项目 - 我想在视图(蓝色背景)中看到它。
为了实现 TwoWayBinding,我使用了这个行为:Data binding to SelectedItem in a WPF Treeview
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 TreeViewItem
是 null
我的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>
谢谢大家!
SelectedItem
TreeView
的 属性 在您的情况下不 return TreeViewItem
。它 return 当前从您绑定的 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);
}
}
如果您的实际模型对象是 TreeViewItem 类型,您的 OnSelectedItemChanged 将仅传递 TreeViewItem 类型的对象,而 99% 的情况都不会是这种情况。您将不得不从模型对象中检索 TreeViewItem,但如果节点当前折叠,它将不可用,这使得选择折叠节点非常重要。
我已经努力在我的博客中对此进行了非常详尽的解释,包括代码示例 here
为了方便起见,这里是 and 的最终解决方案:
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;
}
}
我创建了一个非常简单的示例来说明我的问题。可能是我想错了。
我想 select 我的 TreeView 的一个项目 - 我想在视图(蓝色背景)中看到它。
为了实现 TwoWayBinding,我使用了这个行为:Data binding to SelectedItem in a WPF Treeview
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 TreeViewItem
是 null
我的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>
谢谢大家!
SelectedItem
TreeView
的 属性 在您的情况下不 return TreeViewItem
。它 return 当前从您绑定的 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);
}
}
如果您的实际模型对象是 TreeViewItem 类型,您的 OnSelectedItemChanged 将仅传递 TreeViewItem 类型的对象,而 99% 的情况都不会是这种情况。您将不得不从模型对象中检索 TreeViewItem,但如果节点当前折叠,它将不可用,这使得选择折叠节点非常重要。
我已经努力在我的博客中对此进行了非常详尽的解释,包括代码示例 here
为了方便起见,这里是
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;
}
}