2015-08-13 3 views
1

J'ai créé un exemple très simple pour montrer mon problème. Peut-être que je pense juste d'une mauvaise façon.TreeView SelectedItem Behavior - Liaison bidirectionnelle ne fonctionne pas dans One Direction

Je veux sélectionner un élément de mon TreeView - et je voudrais le voir dans la vue (fond bleu).

Pour réaliser le TwoWayBinding J'utilise ce comportement: 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; 
    } 
} 

Mais si je clique sur un élément, il ne va pas dans le « si » du OnSelectedItemChanged parce e.newValue as TreeViewItem est null

Mon XAML est très simple:

<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> 

Merci les gars!

Répondre

1

Juste pour le bien de la commodité, voici la solution finale combinée du OP et 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 La propriété de TreeView ne renvoie pas TreeViewItem dans votre cas. Il renvoie l'élément actuellement sélectionné de votre collection Items liée. Pour obtenir TreeViewItem de SelectedItem, vous devez utiliser ItemContainerGenerator ici:

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

Merci d'avoir répondu, le problème est que "generator.ContainerFromItem (e.NewValue)" renvoie null car mon onglet n'est pas actif. Savez-vous comment résoudre ce problème? – MisterPresident

+0

@MisterPresident dans mon cas, j'essaie d'y parvenir en utilisant un 'TreeView', et obtenir le conteneur renvoie aussi null. – Shimmy

0

Votre OnSelectedItemChanged ne passera que un objet de type TreeViewItem si vos objets de modèle réels sont de type TreeViewItem, ce qui ne va pas être le cas 99 % du temps. Vous devrez à la place récupérer TreeViewItem à partir d'un objet modèle, mais il ne sera pas disponible si le nœud est actuellement réduit, ce qui rend la sélection des nœuds réduits très triviale.

J'ai fait un effort pour expliquer très bien dans mon blog, y compris des exemples de code here