263 Stimmen

Datenbindung an SelectedItem in einem WPF Treeview

Wie kann ich das Element abrufen, das in einer WPF-Treeview ausgewählt ist? Ich möchte dies in XAML zu tun, weil ich es zu binden möchten.

Man könnte meinen, es sei SelectedItem aber offenbar ist das gibt es nicht ist schreibgeschützt und daher unbrauchbar.

Das ist es, was ich tun möchte:

<TreeView ItemsSource="{Binding Path=Model.Clusters}" 
            ItemTemplate="{StaticResource ClusterTemplate}"
            SelectedItem="{Binding Path=Model.SelectedCluster}" />

Ich möchte die SelectedItem zu einer Eigenschaft in meinem Modell.

Dies führt jedoch zu einer Fehlermeldung:

Die Eigenschaft "SelectedItem" ist schreibgeschützt und kann nicht über das Markup gesetzt werden.

Editar: Ok, so habe ich das Problem gelöst:

<TreeView
          ItemsSource="{Binding Path=Model.Clusters}" 
          ItemTemplate="{StaticResource HoofdCLusterTemplate}"
          SelectedItemChanged="TreeView_OnSelectedItemChanged" />

und in der Code-Hintergrunddatei meiner Xaml:

private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
    Model.SelectedCluster = (Cluster)e.NewValue;
}

3voto

Es gibt auch eine Möglichkeit, eine XAML-bindbare SelectedItem-Eigenschaft zu erstellen, ohne Interaction.Behaviors zu verwenden.

public static class BindableSelectedItemHelper
{
    #region Properties

    public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached("SelectedItem", typeof(object), typeof(BindableSelectedItemHelper),
        new FrameworkPropertyMetadata(null, OnSelectedItemPropertyChanged));

    public static readonly DependencyProperty AttachProperty = DependencyProperty.RegisterAttached("Attach", typeof(bool), typeof(BindableSelectedItemHelper), new PropertyMetadata(false, Attach));

    private static readonly DependencyProperty IsUpdatingProperty = DependencyProperty.RegisterAttached("IsUpdating", typeof(bool), typeof(BindableSelectedItemHelper));

    #endregion

    #region Implementation

    public static void SetAttach(DependencyObject dp, bool value)
    {
        dp.SetValue(AttachProperty, value);
    }

    public static bool GetAttach(DependencyObject dp)
    {
        return (bool)dp.GetValue(AttachProperty);
    }

    public static string GetSelectedItem(DependencyObject dp)
    {
        return (string)dp.GetValue(SelectedItemProperty);
    }

    public static void SetSelectedItem(DependencyObject dp, object value)
    {
        dp.SetValue(SelectedItemProperty, value);
    }

    private static bool GetIsUpdating(DependencyObject dp)
    {
        return (bool)dp.GetValue(IsUpdatingProperty);
    }

    private static void SetIsUpdating(DependencyObject dp, bool value)
    {
        dp.SetValue(IsUpdatingProperty, value);
    }

    private static void Attach(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        TreeListView treeListView = sender as TreeListView;
        if (treeListView != null)
        {
            if ((bool)e.OldValue)
                treeListView.SelectedItemChanged -= SelectedItemChanged;

            if ((bool)e.NewValue)
                treeListView.SelectedItemChanged += SelectedItemChanged;
        }
    }

    private static void OnSelectedItemPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        TreeListView treeListView = sender as TreeListView;
        if (treeListView != null)
        {
            treeListView.SelectedItemChanged -= SelectedItemChanged;

            if (!(bool)GetIsUpdating(treeListView))
            {
                foreach (TreeViewItem item in treeListView.Items)
                {
                    if (item == e.NewValue)
                    {
                        item.IsSelected = true;
                        break;
                    }
                    else
                       item.IsSelected = false;                        
                }
            }

            treeListView.SelectedItemChanged += SelectedItemChanged;
        }
    }

    private static void SelectedItemChanged(object sender, RoutedEventArgs e)
    {
        TreeListView treeListView = sender as TreeListView;
        if (treeListView != null)
        {
            SetIsUpdating(treeListView, true);
            SetSelectedItem(treeListView, treeListView.SelectedItem);
            SetIsUpdating(treeListView, false);
        }
    }
    #endregion
}

Sie können dies dann in Ihrer XAML als verwenden:

<TreeView  helper:BindableSelectedItemHelper.Attach="True" 
           helper:BindableSelectedItemHelper.SelectedItem="{Binding SelectedItem, Mode=TwoWay}">

3voto

Arthur Nunes Punkte 6368

Ich schlage einen Zusatz zu dem von Steve Greatrex beschriebenen Verhalten vor. Sein Verhalten spiegelt keine Änderungen der Quelle wider, da es sich nicht um eine Sammlung von TreeViewItems handeln darf. Es geht also darum, das TreeViewItem im Baum zu finden, dessen Datentext der selectedValue aus der Quelle ist. Der TreeView hat eine geschützte Eigenschaft namens "ItemsHost", welche die TreeViewItem Sammlung enthält. Wir können sie durch Reflexion erhalten und den Baum auf der Suche nach dem ausgewählten Element durchlaufen.

private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var behavior = sender as BindableSelectedItemBehaviour;

        if (behavior == null) return;

        var tree = behavior.AssociatedObject;

        if (tree == null) return;

        if (e.NewValue == null) 
            foreach (var item in tree.Items.OfType<TreeViewItem>())
                item.SetValue(TreeViewItem.IsSelectedProperty, false);

        var treeViewItem = e.NewValue as TreeViewItem; 
        if (treeViewItem != null)
        {
            treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
        }
        else
        {
            var itemsHostProperty = tree.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            if (itemsHostProperty == null) return;

            var itemsHost = itemsHostProperty.GetValue(tree, null) as Panel;

            if (itemsHost == null) return;

            foreach (var item in itemsHost.Children.OfType<TreeViewItem>())
                if (WalkTreeViewItem(item, e.NewValue)) break;
        }
    }

    public static bool WalkTreeViewItem(TreeViewItem treeViewItem, object selectedValue) {
        if (treeViewItem.DataContext == selectedValue)
        {
            treeViewItem.SetValue(TreeViewItem.IsSelectedProperty, true);
            treeViewItem.Focus();
            return true;
        }

        foreach (var item in treeViewItem.Items.OfType<TreeViewItem>())
            if (WalkTreeViewItem(item, selectedValue)) return true;

        return false;
    }

Auf diese Weise funktioniert das Verhalten bei bidirektionalen Bindungen. Alternativ ist es möglich, die ItemsHost-Erfassung in die OnAttached-Methode des Verhaltens zu verschieben, wodurch der Overhead der Verwendung von Reflection bei jeder Aktualisierung der Bindung eingespart wird.

2voto

JustinMichel Punkte 241

WPF MVVM TreeView SelectedItem

... ist eine bessere Antwort, aber nicht erwähnen, einen Weg zu erhalten/setzen die SelectedItem im ViewModel.

  1. Fügen Sie eine IsSelected boolesche Eigenschaft zu Ihrem ItemViewModel hinzu, und binden Sie diese in einem Style Setter für das TreeViewItem.
  2. Fügen Sie eine SelectedItem-Eigenschaft zu Ihrem ViewModel hinzu, das als DataContext für den TreeView verwendet wird. Dies ist das fehlende Element in der obigen Lösung.

    ' ItemVM...
    Public Property IsSelected As Boolean
        Get
            Return \_func.SelectedNode Is Me
        End Get
        Set(value As Boolean)
            If IsSelected  value Then
                \_func.SelectedNode = If(value, Me, Nothing)
            End If
            RaisePropertyChange()
        End Set
    End Property
    ' TreeVM...
    Public Property SelectedItem As ItemVM
        Get
            Return \_selectedItem
        End Get
        Set(value As ItemVM)
            If \_selectedItem Is value Then
                Return
            End If
            Dim prev = \_selectedItem
            \_selectedItem = value
            If prev IsNot Nothing Then
                prev.IsSelected = False
            End If
            If \_selectedItem IsNot Nothing Then
                \_selectedItem.IsSelected = True
            End If
        End Set
    End Property

    <TreeView ItemsSource="{Binding Path=TreeVM}" BorderBrush="Transparent"> <TreeView.ItemContainerStyle> <Style TargetType="TreeViewItem"> <Setter Property="IsExpanded" Value="{Binding IsExpanded}"/> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Children}"> <TextBlock Text="{Binding Name}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>

1voto

Fahad Owais Punkte 19

Dies kann auch über die Eigenschaft IsSelected des TreeView-Elements erfolgen. Hier ist, wie ich es geschafft habe,

public delegate void TreeviewItemSelectedHandler(TreeViewItem item);
public class TreeViewItem
{      
  public static event TreeviewItemSelectedHandler OnItemSelected = delegate { };
  public bool IsSelected 
  {
    get { return isSelected; }
    set 
    { 
      isSelected = value;
      if (value)
        OnItemSelected(this);
    }
  }
}

Im ViewModel, das die Daten enthält, an die Ihr TreeView gebunden ist, abonnieren Sie dann einfach das Ereignis in der TreeViewItem-Klasse.

TreeViewItem.OnItemSelected += TreeViewItemSelected;

Und schließlich implementieren Sie diesen Handler in demselben ViewModel,

private void TreeViewItemSelected(TreeViewItem item)
{
  //Do something
}

Und natürlich die Bindung,

<Setter Property="IsSelected" Value="{Binding IsSelected}" />

1voto

karma Punkte 11

Nachdem ich einen Tag lang das Internet studiert hatte, fand ich meine eigene Lösung für die Auswahl eines Artikels nach dem Erstellen eines normal Baumansicht in einer normal WPF/C#-Umgebung

private void BuildSortTree(int sel)
        {
            MergeSort.Items.Clear();
            TreeViewItem itTemp = new TreeViewItem();
            itTemp.Header = SortList[0];
            MergeSort.Items.Add(itTemp);
            TreeViewItem prev;
            itTemp.IsExpanded = true;
            if (0 == sel) itTemp.IsSelected= true;
            prev = itTemp;
            for(int i = 1; i<SortList.Count; i++)
            {

                TreeViewItem itTempNEW = new TreeViewItem();
                itTempNEW.Header = SortList[i];
                prev.Items.Add(itTempNEW);
                itTempNEW.IsExpanded = true;
                if (i == sel) itTempNEW.IsSelected = true;
                prev = itTempNEW ;
            }
        }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X