WPF TreeView에서 프로그래밍 방식으로 항목을 선택하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/413890

문제

WPF에서 항목을 프로그래밍 방식으로 선택할 수있는 방법 TreeView? 그만큼 ItemsControl 모델은 그것을 막는 것 같습니다.

도움이 되었습니까?

해결책

이상한 이유는 정말 고통 스럽습니다. 컨테이너를 사용하여 컨테이너를 얻은 다음 선택 방법을 호출해야합니다.

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

한 번은 그것을하는 방법에 대한 블로그 항목이있었습니다. 여기, 그러나 링크는 이제 죽었습니다.

다른 팁

이 문제에 대한 올바른 해결책을 여전히 찾고있는 사람들에게는 다음과 같습니다. 코드 프로젝트 기사“WPF TreeView Selection”에 대한 의견에서 이것을 찾았습니다. http://www.codeproject.com/kb/wpf/treeview_selectionwpff.aspx Dawanderer에 의해. Kenrae가 2008 년 11 월 25 일에 게시했습니다. 이것은 저에게 훌륭하게 작동했습니다. 감사합니다 Kenrae!

그의 게시물은 다음과 같습니다.

트리를 걷는 대신 자신의 데이터 객체에 선택된 속성이 있습니다 (그리고 isexpanded 속성도 권장합니다). TreeView의 TreeView에서 해당 속성을 데이터 객체로 바인딩하는 TreeView의 itemContainersTyle 속성을 사용하여 트리의 TreeViewItems의 스타일을 정의하십시오. 이 같은:

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

당신은 가져 가야합니다 TreeViewItem 그런 다음 설정합니다 IsSelected 에게 true.

이 코드에 성공했습니다.

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

용법:

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

이것은 보이는 것만 큼 간단하지 않습니다. Steven이 제공 한 링크는 2008 년에 게시 된 솔루션을 가지고 있으며, 이는 여전히 작동하지만 가상화 된 TreeViews를 처리하지 않습니다. 또한 그 기사의 의견에 다른 많은 문제가 언급되어 있습니다. 범죄는 없지만 같은 문제가 발생하여 완벽한 해결책을 찾을 수 없습니다. 다음은 몇 가지 기사/게시물에 대한 링크입니다.

TreeView에서 항목을 어떻게 확장 할 수 있습니까? - 파트 III :http://bea.stollnitz.com/blog/?p=59

프로그래밍 방식으로 트리 뷰에서 항목 선택 :http://blog.quantumbitdesigns.com/2008/07/22/programmically-selecting-an-item-in-a-treeview/#respond

TreeView, TreeViewItem 및 isselected :http://social.msdn.microsoft.com/forums/en-us/wpf/thread/7e368b93-f509-4cd6-88e7-561e8d3246ae/

확장 방법을 썼습니다.

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

다음과 같이 사용할 수 있습니다.

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

자녀의 자녀와 함께 위치한 항목을 선택하려면 사용자 재귀를 통해 그렇게 할 수 있습니다.

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

이것으로 시도하십시오

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

참조: http://amastaneh.blogspot.com/2011/06/wpf-selectedvalue-for-treeview.html

내가 함께 갔던 솔루션으로 차임 할 것이라고 생각했습니다. 이를 수행하는 가장 좋은 방법은 Kuninl의 답변에 따라 'isselected'와 같은 묶인 속성을 사용하는 것이지만, 제 경우에는 MVVM을 따르지 않은 레거시 응용 프로그램이므로 아래를 끝내 셨습니다.

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

이것이하는 일은 UI의 트리 뷰 항목을 선택하고 확장하는 것입니다. 이것의 목적은 동일한 창의 항목 컨트롤에서 사용자 선택이 변경되었을 때 TreeView의 선택 변경을 수행하는 것이 었습니다.

나는 방법을 만들었다 VisualTreeExt.GetDescendants<T> 지정된 유형과 일치하는 열거 가능한 요소 모음을 반환합니다.

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

당신이 요청할 때 VisualTreeHelperExt.GetDescendants<TreeViewItem>(MyAmazingTreeView) 당신은 모든 것을 얻을 것입니다 TreeViewItem 어린이. 다음 코드를 사용하여 특정 값을 선택할 수 있습니다.

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

그것은 약간 더러운 솔루션 (그리고 아마도 가장 효율적이지 않을 것입니다)이며 실제 시각적 요소의 존재에 따라 가상화 된 TreeView를 사용하는 경우 작동하지 않습니다. 그러나 그것은 내 상황에 효과적입니다 ...

그래 .. 질문이 제기 된 이후 몇 년을 알고 있지만 .. 여전히이 문제에 대한 빠른 해결책은 없습니다.

다음은 OP가 요청한 것을 할 것입니다.

내가 기본적으로 수행 한 것은이 페이지의 모든 답을 읽고 모든 관련 링크를 따라 한번만 이 자극적 인 문제에 대한 해결책.

이익:

  • 가상화 트리 뷰도 지원합니다.
  • 동작 기술을 사용하여 XAML이 쉽습니다.
  • 선택한 TreeView 항목에 바인딩 할 수 있도록 종속성 프로페티를 추가합니다.


이 부분은 복사해야 할 유일한 코드이며 다른 부분은 예제를 완성하는 데 도움이됩니다.

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
}

그리고 이것은 XAML에서 TreeView 라인이 어떻게 보이는지의 예입니다.

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

걱정해야 할 유일한 것은 SelectedItemex에 묶여있는 뷰 모델 속성이 무효가 아닌지 확인하는 것입니다. 그러나 그것은 특별한 경우가 아닙니다. 사람들이 혼란스러워하는 경우를 대비하여 언급했습니다.

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

그리고 마지막으로 .. 프로그래밍 방식으로 선택한 예 : MainWindow.xaml에서 버튼을 만들었습니다.

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

이것이 누군가를 돕기를 바랍니다 :)

당신은 같은 코드를 통해 그것을 할 수 있습니다

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

이것이 가장 간단한 해결책이라고 생각합니다.

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

제안 된 답변은 작동하지 않습니다. @Fandisusanto의 대답은 아마도 효과가 있지만 더 간단하게 만들 수 있습니다. 이것은 내가 생각해 낼 수있는 가장 간단한 대답입니다.

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

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

용법:

    private void ClearSelectedItem()
    {
        if (AssetTreeView.SelectedItem != null)
        {
            DeselectTreeViewItem(AssetTreeView.Items.Cast<TreeViewItem>());
        }
    }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top