2015-08-13 4 views
1

Я создал очень простой пример, чтобы показать свою проблему. Возможно, я просто ошибаюсь.TreeView SelectedItem Behavior - Two Way Binding не работает в одном направлении

Я хочу выбрать элемент моего 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, потому что 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> 

Спасибо, ребята!

ответ

1

Просто ради удобства, вот окончательное решение в сочетании из OP и ghrod's answer:

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; 
    } 
} 
1

SelectedItem имущество TreeView не возвращается TreeViewItem в вашем случае. Он возвращает выбранный вами элемент из вашей коллекции Items. Чтобы получить TreeViewItem от SelectedItem, вам нужно использовать 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); 
    } 
} 
+0

Спасибо за ответ, проблема в том, что «generator.ContainerFromItem (e.NewValue)» возвращает значение null, потому что моя вкладка не активна. Вы знаете, как это решить? – MisterPresident

+0

@MisterPresident в моем случае, я пытаюсь достичь этого, используя «TreeView», и получение контейнера также возвращает значение null. – Shimmy

0

Ваш OnSelectedItemChanged будет только передать объект типа TreeViewItem если фактические объекты модели имеют тип TreeViewItem, который не собирается быть дело 99 % времени. Вместо этого вам придется извлечь TreeViewItem из объекта модели, но он не будет доступен, если узел в настоящее время рухнул, что делает выбор свернутых узлов очень нетривиальным.

Я сделал попытку объяснить это очень хорошо в своем блоге, в том числе образцы кода here