it-swarm.com.de

Wie kann ich ein Element in einer WPF-TreeView programmgesteuert auswählen?

Wie ist es möglich, ein Element in einer WPF TreeView programmgesteuert auszuwählen? Das Modell ItemsControl scheint dies zu verhindern.

42
Thomas Bratt

Es ist aus einem seltsamen Grund ein echter Schmerz, Sie müssen ContainerFromItem verwenden, um den Container abzurufen, und dann die select-Methode aufrufen. 

//  selectedItemObject is not a TreeViewItem, but an item from the collection that 
//  populated the TreeView.

var tvi = treeView.ItemContainerGenerator.ContainerFromItem(selectedItemObject) 
          as TreeViewItem;

if (tvi != null)
{
    tvi.IsSelected = true;
}

Es gab einmal einen Blogeintrag, wie das geht, hier , aber der Link ist jetzt tot.

19
Steven Robbins

Für diejenigen, die noch nach der richtigen Lösung für dieses Problem suchen, ist hier die folgende. Ich fand diese in den Kommentaren zum Code-Projekt-Artikel „WPF TreeView Selection“ http://www.codeproject.com/KB/WPF/TreeView_SelectionWPF.aspx von DaWanderer ..__ 25. November 2008. Das hat für mich super funktioniert. Danke Kenrae!

Hier ist sein Beitrag:

Anstatt den Baum zu durchlaufen, sollten Sie Ihr eigenes Datenobjekt über die IsSelected-Eigenschaft verfügen (und ich empfehle auch die IsExpanded-Eigenschaft). Definieren Sie einen Stil für die TreeViewItems des Baums mithilfe der ItemContainerStyle-Eigenschaft in der TreeView, die diese Eigenschaften vom TreeViewItem an Ihre Datenobjekte bindet. Etwas wie das:

<Style x:Key="LibraryTreeViewItemStyle"
               TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded"
                        Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected"
                        Value="{Binding IsSelected, Mode=TwoWay}" />
            <Setter Property="FontWeight"
                        Value="Normal" />
            <Style.Triggers>
                  <Trigger Property="IsSelected"
                              Value="True">
                        <Setter Property="FontWeight"
                                    Value="Bold" />
                  </Trigger>
            </Style.Triggers>
      </Style>

<TreeView ItemsSource="{Binding Path=YourCollection}"
               ItemContainerStyle="{StaticResource LibraryTreeViewItemStyle}"
               ItemTemplate={StaticResource YourHierarchicalDataTemplate}/>
38
kuninl

Sie müssen die TreeViewItem abrufen und dann IsSelected auf true setzen.

22
Kent Boogaart

Dies ist nicht so einfach, wie es aussieht, der von Steven bereitgestellte Link enthält eine Lösung aus dem Jahr 2008, die möglicherweise noch funktioniert, sich aber nicht um Virtualized TreeViews kümmert. Darüber hinaus werden viele andere Probleme in den Kommentaren dieses Artikels erwähnt. Keine Beleidigungen, aber ich habe auch das gleiche Problem und finde keine perfekte Lösung. Hier sind die Links zu einigen Artikeln/Beiträgen, die mir sehr geholfen haben-

Wie kann ich Elemente in einer TreeView erweitern? - Teil III: http://bea.stollnitz.com/blog/?p=59

Programmgesteuertes Auswählen eines Elements in einer Baumansicht: http://blog.quantumbitdesigns.com/2008/07/22/programmatic-selecting-an-item-in-a-treeview/#respond

TreeView, TreeViewItem und IsSelected: http://social.msdn.Microsoft.com/Forums/en-US/wpf/thread/7e368b93-f509-4cd6-88e7-561e8d3246ae/

5
akjoshi

Ich bin mit diesem Code erfolgreich gewesen:

public static TreeViewItem FindTviFromObjectRecursive(ItemsControl ic, object o) {
  //Search for the object model in first level children (recursively)
  TreeViewItem tvi = ic.ItemContainerGenerator.ContainerFromItem(o) as TreeViewItem;
  if (tvi != null) return tvi;
  //Loop through user object models
  foreach (object i in ic.Items) {
    //Get the TreeViewItem associated with the iterated object model
    TreeViewItem tvi2 = ic.ItemContainerGenerator.ContainerFromItem(i) as TreeViewItem;
    tvi = FindTviFromObjectRecursive(tvi2, o);
    if (tvi != null) return tvi;
  }
  return null;
}

Verwendungszweck:

var tvi = FindTviFromObjectRecursive(TheTreeView, TheModel);
if (tvi != null) tvi.IsSelected = true;
3
Fandi Susanto

Ich habe eine Erweiterungsmethode geschrieben:

using System.Windows.Controls;

namespace Extensions
{
    public static class TreeViewEx
    {
        /// <summary>
        /// Select specified item in a TreeView
        /// </summary>
        public static void SelectItem(this TreeView treeView, object item)
        {
            var tvItem = treeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
            if (tvItem != null)
            {
                tvItem.IsSelected = true;
            }
        }
    }
}

Was kann ich so verwenden:

if (_items.Count > 0)
    _treeView.SelectItem(_items[0]);
2

Wenn Sie ein Element auswählen möchten, das sich in untergeordneten Elementen von untergeordneten Objekten befindet, können Sie die Rekursion des Benutzers dazu durchführen.

public bool Select(TreeViewItem item, object select) // recursive function to set item selection in treeview
{
    if (item == null)
        return false;
    TreeViewItem child = item.ItemContainerGenerator.ContainerFromItem(select) as TreeViewItem;
    if (child != null)
    {
        child.IsSelected = true;
        return true;
    }
    foreach (object c in item.Items)
    {
        bool result = Select(item.ItemContainerGenerator.ContainerFromItem(c) as TreeViewItem, select);
        if (result == true)
            return true;
    }
    return false;
}
1
purvin

Ich habe eine Methode VisualTreeExt.GetDescendants<T> erstellt, die eine aufzählbare Auflistung von Elementen zurückgibt, die dem angegebenen Typ entsprechen:

public static class VisualTreeExt
{
  public static IEnumerable<T> GetDescendants<T>(DependencyObject parent) where T : DependencyObject
  {
    var count = VisualTreeHelper.GetChildrenCount(parent);
    for (var i = 0; i < count; ++i)
    {
       // Obtain the child
       var child = VisualTreeHelper.GetChild(parent, i);
       if (child is T)
         yield return (T)child;

       // Return all the descendant children
       foreach (var subItem in GetDescendants<T>(child))
         yield return subItem;
    }
  }
}

Wenn Sie nach VisualTreeHelperExt.GetDescendants<TreeViewItem>(MyAmazingTreeView) fragen, erhalten Sie alle TreeViewItem-Childs. Sie können einen bestimmten Wert mit dem folgenden Code auswählen:

var treeViewItem = VisualTreeExt.GetDescendants<TreeViewItem>(MyTreeView).FirstOrDefault(tvi => tvi.DataContext == newValue);
if (treeViewItem != null)
  treeViewItem.IsSelected = true;

Es ist eine etwas schmutzige Lösung (und wahrscheinlich nicht die effizienteste) und funktioniert nicht, wenn Sie eine virtualisierte TreeView verwenden, da dies von den vorhandenen visuellen Elementen abhängt. Aber es funktioniert für meine Situation ...

0
Ramon de Klein

Ich dachte nur, ich würde mit der Lösung, mit der ich gegangen bin, einrasten, falls dies jedem helfen kann. Beachten Sie, dass der beste Weg, dies zu tun, die Verwendung einer gebundenen Eigenschaft wie 'IsSelected' gemäß der Antwort von kuninl ist. In meinem Fall war es jedoch eine ältere Anwendung, die der MVVM nicht folgte, sodass ich am Ende die unten stehende beendete.

private void ChangeSessionSelection()
{
    foreach (SessionContainer item in this.treeActiveSessions.Items)
    {
        var treeviewItem = this.treeActiveSessions.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;

        if (item.Session == this.selectedSession.Session)
        {
            treeviewItem.IsSelected = true;
            treeviewItem.IsExpanded = true;
        }
        else
        {
            treeviewItem.IsSelected = false;
            treeviewItem.IsExpanded = false;
        }
    }            
}

Dazu wird das Element der Baumansicht in der Benutzeroberfläche ausgewählt und erweitert, die das ausgewählte Datenelement im Code dahinter darstellt. Dies diente dazu, die Auswahl in der Baumansicht zu ändern, wenn sich die Benutzerauswahl in einem Elementsteuerelement in demselben Fenster geändert hat.

0
RobJohnson

Ja .. Ich weiß seit vielen Jahren, seit die Frage gestellt wurde, aber immer noch keine schnelle Lösung für dieses Problem .. Und so:

Das Folgende wird das tun, wonach das OP gefragt hat. 

Was ich im Wesentlichen getan habe, ist das Lesen aller Antworten auf dieser Seite und das Befolgen aller relevanten Links, um eine ein für alle Mal Lösung für dieses irritierende Problem zu erstellen.

Leistungen: 

  • Es unterstützt auch das Virtualisieren von TreeView.
  • Es verwendet die Verhaltenstechnik, so dass XAML sehr einfach ist.
  • Fügt eine Abhängigkeitseigenschaft hinzu, um die Bindung an das ausgewählte TreeView-Element zu ermöglichen.


Dieser Teil ist der einzige Code, den Sie kopieren müssen. Die anderen Teile dienen nur zur Veranschaulichung eines Beispiels.

public static class TreeViewSelectedItemExBehavior
{
    private static List<TreeView> isRegisteredToSelectionChanged = new List<TreeView>();

    public static readonly DependencyProperty SelectedItemExProperty =
        DependencyProperty.RegisterAttached("SelectedItemEx",
            typeof(object),
            typeof(TreeViewSelectedItemExBehavior),
            new FrameworkPropertyMetadata(new object(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemExChanged, null));

    #region SelectedItemEx

    public static object GetSelectedItemEx(TreeView target)
    {
        return target.GetValue(SelectedItemExProperty);
    }

    public static void SetSelectedItemEx(TreeView target, object value)
    {
        target.SetValue(SelectedItemExProperty, value);
        var treeViewItemToSelect = GetTreeViewItem(target, value);
        if (treeViewItemToSelect == null)
        {
            if (target.SelectedItem == null)
                return;
            var treeViewItemToUnSelect = GetTreeViewItem(target, target.SelectedItem);
            treeViewItemToUnSelect.IsSelected = false;
        }
        else
            treeViewItemToSelect.IsSelected = true;
    }

    public static void OnSelectedItemExChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        var treeView = depObj as TreeView;
        if (treeView == null)
            return;
        if (!isRegisteredToSelectionChanged.Contains(treeView))
        {
            treeView.SelectedItemChanged += TreeView_SelectedItemChanged;
            isRegisteredToSelectionChanged.Add(treeView);
        }
    }

    #endregion

    private static void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        var treeView = (TreeView)sender;
        SetSelectedItemEx(treeView, e.NewValue);
    }

    #region Helper Structures & Methods

    public class MyVirtualizingStackPanel : VirtualizingStackPanel
    {
        /// <summary>
        /// Publically expose BringIndexIntoView.
        /// </summary>
        public void BringIntoView(int index)
        {
            BringIndexIntoView(index);
        }
    }

    /// <summary>Recursively search for an item in this subtree.</summary>
    /// <param name="container">The parent ItemsControl. This can be a TreeView or a TreeViewItem.</param>
    /// <param name="item">The item to search for.</param>
    /// <returns>The TreeViewItem that contains the specified item.</returns>
    private static TreeViewItem GetTreeViewItem(ItemsControl container, object item)
    {
        if (container != null)
        {
            if (container.DataContext == item)
            {
                return container as TreeViewItem;
            }

            // Expand the current container
            if (container is TreeViewItem && !((TreeViewItem)container).IsExpanded)
            {
                container.SetValue(TreeViewItem.IsExpandedProperty, true);
            }

            // Try to generate the ItemsPresenter and the ItemsPanel.
            // by calling ApplyTemplate.  Note that in the 
            // virtualizing case even if the item is marked 
            // expanded we still need to do this step in order to 
            // regenerate the visuals because they may have been virtualized away.

            container.ApplyTemplate();
            ItemsPresenter itemsPresenter =
                (ItemsPresenter)container.Template.FindName("ItemsHost", container);
            if (itemsPresenter != null)
            {
                itemsPresenter.ApplyTemplate();
            }
            else
            {
                // The Tree template has not named the ItemsPresenter, 
                // so walk the descendents and find the child.
                itemsPresenter = FindVisualChild<ItemsPresenter>(container);
                if (itemsPresenter == null)
                {
                    container.UpdateLayout();

                    itemsPresenter = FindVisualChild<ItemsPresenter>(container);
                }
            }

            Panel itemsHostPanel = (Panel)VisualTreeHelper.GetChild(itemsPresenter, 0);


            // Ensure that the generator for this panel has been created.
            UIElementCollection children = itemsHostPanel.Children;

            MyVirtualizingStackPanel virtualizingPanel =
                itemsHostPanel as MyVirtualizingStackPanel;

            for (int i = 0, count = container.Items.Count; i < count; i++)
            {
                TreeViewItem subContainer;
                if (virtualizingPanel != null)
                {
                    // Bring the item into view so 
                    // that the container will be generated.
                    virtualizingPanel.BringIntoView(i);

                    subContainer =
                        (TreeViewItem)container.ItemContainerGenerator.
                        ContainerFromIndex(i);
                }
                else
                {
                    subContainer =
                        (TreeViewItem)container.ItemContainerGenerator.
                        ContainerFromIndex(i);

                    // Bring the item into view to maintain the 
                    // same behavior as with a virtualizing panel.
                    subContainer.BringIntoView();
                }

                if (subContainer != null)
                {
                    // Search the next level for the object.
                    TreeViewItem resultContainer = GetTreeViewItem(subContainer, item);
                    if (resultContainer != null)
                    {
                        return resultContainer;
                    }
                    else
                    {
                        // The object is not under this TreeViewItem
                        // so collapse it.
                        subContainer.IsExpanded = false;
                    }
                }
            }
        }

        return null;
    }

    /// <summary>Search for an element of a certain type in the visual tree.</summary>
    /// <typeparam name="T">The type of element to find.</typeparam>
    /// <param name="visual">The parent element.</param>
    /// <returns></returns>
    private static T FindVisualChild<T>(Visual visual) where T : Visual
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
        {
            Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
            if (child != null)
            {
                T correctlyTyped = child as T;
                if (correctlyTyped != null)
                {
                    return correctlyTyped;
                }

                T descendent = FindVisualChild<T>(child);
                if (descendent != null)
                {
                    return descendent;
                }
            }
        }
        return null;
    }

    #endregion
}

Und dies ist ein Beispiel, wie die TreeView-Zeile in XAML aussieht:

<TreeView x:Name="trvwSs"
          Grid.Column="2" Grid.Row="1" Margin="4" ItemsSource="{Binding ItemsTreeViewSs}"
          behaviors:TreeViewSelectedItemExBehavior.SelectedItemEx="{Binding SelectedItemTreeViewSs}" />

Sie müssen sich nur darum kümmern, dass Ihre Ansichtsmodell-Eigenschaft, die Sie gerade an SelectedItemEx binden, nicht null ist. Aber das ist kein Sonderfall. Nur erwähnt, falls die Leute verwirrt werden.

public class VmMainContainer : INotifyPropertyChanged
{
    private object selectedItemTreeViewSs = new object();
    private ObservableCollection<object> selectedItemsTreeViewSs = new ObservableCollection<object>();
    private ObservableCollection<VmItem> itemsTreeViewSs = new ObservableCollection<VmItem>();

 public object SelectedItemTreeViewSs
        {
            get
            {
                return selectedItemTreeViewSs;
            }
            set
            {
                selectedItemTreeViewSs = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemTreeViewSs)));
            }
        }

public ObservableCollection<object> SelectedItemsTreeViewSs
        {
            get
            {
                return selectedItemsTreeViewSs;
            }
            set
            {
                selectedItemsTreeViewSs = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItemsTreeViewSs)));
            }
        }

 public ObservableCollection<VmItem> ItemsTreeViewSs
        {
            get { return itemsTreeViewSs; }
            set
            {
                itemsTreeViewSs = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemsTreeViewSs)));
            }
        }
    }

Und als letztes. Beispiel für eine programmgesteuerte Auswahl: Ich habe eine Schaltfläche in meiner MainWindow.xaml und von deren Handler erstellt.

private void Button_Click(object sender, RoutedEventArgs e)
{
    TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, trvwSs.Items[3]);
    //TreeViewSelectedItemExBehavior.SetSelectedItemEx(trvwSs, null);
}

Hoffe das hilft jemandem :)

0
G.Y

Ich denke, das ist die einfachste Lösung:

private void MouseDownEventProcessing(TreeNodeMouseClickEventArgs e)
{
    tvEmployeeDirectory.SelectedNode = e.Node;
}
0
Indian

Die vorgeschlagene Antwort funktioniert nicht. @ Fandisusantos Antwort funktioniert wahrscheinlich, aber es kann einfacher gemacht werden. Dies ist die einfachste Antwort, die ich finden kann:

    private static void DeselectTreeViewItem(IEnumerable<TreeViewItem> treeViewItems)
    {
        foreach (var treeViewItem in treeViewItems)
        {
            if (treeViewItem.IsSelected)
            {
                treeViewItem.IsSelected = false;
                return;
            }

            DeselectTreeViewItem(treeViewItem.Items.Cast<TreeViewItem>());
        }
    }

Verwendungszweck:

    private void ClearSelectedItem()
    {
        if (AssetTreeView.SelectedItem != null)
        {
            DeselectTreeViewItem(AssetTreeView.Items.Cast<TreeViewItem>());
        }
    }
0

Versuchen Sie es damit

    /// <summary>
    /// Selects the tree view item.
    /// </summary>
    /// <param name="Collection">The collection.</param>
    /// <param name="Value">The value.</param>
    /// <returns></returns>
    private TreeViewItem SelectTreeViewItem(ItemCollection Collection, String Value)
    {
        if (Collection == null) return null;
        foreach(TreeViewItem Item in Collection)
        {
            /// Find in current
            if (Item.Header.Equals(Value))
            {
                Item.IsSelected = true;
                return Item;
            }
            /// Find in Childs
            if (Item.Items != null)
            {
                TreeViewItem childItem = this.SelectTreeViewItem(Item.Items, Value);
                if (childItem != null)
                {
                    Item.IsExpanded = true;
                    return childItem;
                }
            }
        }
        return null;
    }

Referenz: http://amastaneh.blogspot.com/2011/06/wpf-selvalval-for-treeview.html

Du kannst es per hinterlegtem Code machen 

if (TreeView1.Items.Count > 0)
        (TreeView1.Items[0] as TreeViewItem).IsSelected = true;