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

12voto

Devgig Punkte 173

Alles zu kompliziert... Nehmen Sie Caliburn Micro (http://caliburnmicro.codeplex.com/)

Ansicht:

<TreeView Micro:Message.Attach="[Event SelectedItemChanged] = [Action SetSelectedItem($this.SelectedItem)]" />

ViewModel:

public void SetSelectedItem(YourNodeViewModel item) {};

9voto

Wes Punkte 1009

Ich bin auf diese Seite gestoßen, als ich nach der gleichen Antwort wie der ursprüngliche Autor suchte, und da es immer mehr als einen Weg gibt, war die Lösung für mich sogar noch einfacher als die bisherigen Antworten hier, also dachte ich mir, ich könnte dem Stapel auch noch etwas hinzufügen.

Die Motivation für die Bindung ist, es schön & MVVM zu halten. Die wahrscheinliche Verwendung des ViewModel ist eine Eigenschaft w / einen Namen wie "CurrentThingy" zu haben, und irgendwo anders, der DataContext auf etwas anderes Ding ist gebunden an "CurrentThingy".

Anstatt durch zusätzliche Schritte erforderlich (z. B.: benutzerdefinierte Verhalten, 3rd-Party-Steuerelement), um eine schöne Bindung von der TreeView zu meinem Modell zu unterstützen, und dann von etwas anderes zu meinem Modell, war meine Lösung zu verwenden, einfache Element Bindung die andere Sache zu TreeView.SelectedItem, anstatt Bindung der anderen Sache zu meinem ViewModel, wodurch die zusätzliche Arbeit erforderlich überspringen.

XAML:

<TreeView x:Name="myTreeView" ItemsSource="{Binding MyThingyCollection}">
.... stuff
</TreeView>

<!-- then.. somewhere else where I want to see the currently selected TreeView item: -->

<local:MyThingyDetailsView 
       DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}" />

Das ist natürlich großartig, um das aktuell ausgewählte Element zu lesen, aber nicht, um es einzustellen, was alles ist, was ich brauchte.

6voto

nabeelfarid Punkte 4026

Vielleicht können Sie auch die Eigenschaft TreeViewItem.IsSelected verwenden

4voto

Meine Anforderung war für PRISM-MVVM-basierte Lösung, wo ein TreeView benötigt wurde und das gebundene Objekt vom Typ Collection<> ist und daher HierarchicalDataTemplate benötigt. Das Standard BindableSelectedItemBehavior ist nicht in der Lage, das Child TreeViewItem zu identifizieren. Um es in diesem Szenario zum Laufen zu bringen.

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 behavior = sender as BindableSelectedItemBehavior;
        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;
        }
        var itemsHostProperty = treeViewItem.GetType().GetProperty("ItemsHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        if (itemsHostProperty == null) return false;
        var itemsHost = itemsHostProperty.GetValue(treeViewItem, null) as Panel;
        if (itemsHost == null) return false;
        foreach (var item in itemsHost.Children.OfType<TreeViewItem>())
        {
            if (WalkTreeViewItem(item, selectedValue))
                break;
        }
        return false;
    }
    #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;
    }
}

Dies ermöglicht es, alle Elemente unabhängig von der Ebene zu durchlaufen.

3voto

Evgeny Bechkalo Punkte 109

Ich habe alle Lösungen für diese Fragen ausprobiert. Keiner hat mein Problem vollständig gelöst. Daher denke ich, dass es besser ist, eine solche geerbte Klasse mit neu definierter Eigenschaft SelectedItem zu verwenden. Es wird perfekt funktionieren, wenn Sie Baum-Element von GUI wählen und wenn Sie diese Eigenschaft Wert in Ihrem Code setzen

public class TreeViewEx : TreeView
{
    public TreeViewEx()
    {
        this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(TreeViewEx_SelectedItemChanged);
    }

    void TreeViewEx_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        this.SelectedItem = e.NewValue;
    }

    #region SelectedItem

    /// <summary>
    /// Gets or Sets the SelectedItem possible Value of the TreeViewItem object.
    /// </summary>
    public new object SelectedItem
    {
        get { return this.GetValue(TreeViewEx.SelectedItemProperty); }
        set { this.SetValue(TreeViewEx.SelectedItemProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public new static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object), typeof(TreeViewEx),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedItemProperty_Changed));

    static void SelectedItemProperty_Changed(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        TreeViewEx targetObject = dependencyObject as TreeViewEx;
        if (targetObject != null)
        {
            TreeViewItem tvi = targetObject.FindItemNode(targetObject.SelectedItem) as TreeViewItem;
            if (tvi != null)
                tvi.IsSelected = true;
        }
    }                                               
    #endregion SelectedItem   

    public TreeViewItem FindItemNode(object item)
    {
        TreeViewItem node = null;
        foreach (object data in this.Items)
        {
            node = this.ItemContainerGenerator.ContainerFromItem(data) as TreeViewItem;
            if (node != null)
            {
                if (data == item)
                    break;
                node = FindItemNodeInChildren(node, item);
                if (node != null)
                    break;
            }
        }
        return node;
    }

    protected TreeViewItem FindItemNodeInChildren(TreeViewItem parent, object item)
    {
        TreeViewItem node = null;
        bool isExpanded = parent.IsExpanded;
        if (!isExpanded) //Can't find child container unless the parent node is Expanded once
        {
            parent.IsExpanded = true;
            parent.UpdateLayout();
        }
        foreach (object data in parent.Items)
        {
            node = parent.ItemContainerGenerator.ContainerFromItem(data) as TreeViewItem;
            if (data == item && node != null)
                break;
            node = FindItemNodeInChildren(node, item);
            if (node != null)
                break;
        }
        if (node == null && parent.IsExpanded != isExpanded)
            parent.IsExpanded = isExpanded;
        if (node != null)
            parent.IsExpanded = true;
        return node;
    }
}

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