سؤال

كيف يمكن برمجيا تحديد عنصر في WPF TreeView?على ItemsControl يبدو نموذج لمنع ذلك.

هل كانت مفيدة؟

المحلول

وانها الألم الحقيقي لسبب غريب، لديك لاستخدام ContainerFromItem للحصول على الحاوية، ثم استدعاء حدد الأسلوب.

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

وكان هناك مرة واحدة في دخول بلوق على كيفية القيام بذلك <لأ href = "http://askernest.com/archive/2008/01/23/how-to-programmatically-change-the-selecteditem-in-a -wpf-treeview.aspx "يختلط =" noreferrer "> هنا ، ولكن هذا العنوان هو ميت الآن.

نصائح أخرى

وبالنسبة لأولئك الذين ما زالوا يبحثون عن الحل المناسب لهذه المشكلة هنا هو أقل من واحد. لقد وجدت هذا واحد في التعليقات على المقال مشروع قانون "WPF تريفيف اختيار" HTTP: // www.codeproject.com/KB/WPF/TreeView_SelectionWPF.aspx التي كتبها DaWanderer. تم نشره من قبل Kenrae في 25 نوفمبر 2008. وهذا عمل عظيم بالنسبة لي. بفضل Kenrae!

وهنا منصبه:

وبدلا من المشي الشجرة، يكون الكائن البيانات الخاصة بك لديها ممتلكات IsSelected (وأوصي الملكية IsExpanded أيضا). تعريف نمط لTreeViewItems الشجرة باستخدام الخاصية ItemContainerStyle على تريفيف التي تربط تلك الخصائص من TreeViewItem إلى كائنات البيانات الخاصة بك. شيء من هذا القبيل:

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

وهذه ليست بسيطة كما يبدو، على الرابط التي تقدمها ستيفن لديه حل نشرت في عام 2008، والتي قد لا يزال يعمل ولكن لا رعاية الافتراضية TreeViews. وعلاوة على ذلك يتم ذكر العديد من المشاكل الأخرى في تصريحات من تلك المادة. لا جرائم، ولكن أنا أيضا عالقة مع نفس المشكلة ولا يمكن العثور على الحل الأمثل. وفيما يلي روابط لبعض المواد / المشاركات التي ساعدتني كثيرا -

وكيف يمكنني توسيع العناصر في تريفيف؟ - الجزء الثالث: http://bea.stollnitz.com/blog/؟p=59

واختيار برمجيا عنصر في تريفيف: HTTP: // بلوق. quantumbitdesigns.com/2008/07/22/programmatically-selecting-an-item-in-a-treeview/#respond

وتريفيف، TreeViewItem وIsSelected: HTTP: // الاجتماعية. 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 / برنامج الأغذية العالمي، selectedvalue مقابل treeview.html

ومجرد التفكير أود أن تتناغم مع الحل ذهبت مع، في الحالة هذه يمكن أن تساعد أي شخص. لاحظ أن أفضل طريقة للقيام بذلك هو استخدام خاصية ملزمة مثل 'IsSelected' كما في الجواب kuninl، ولكن في حالتي كان تطبيق القديمة التي لم تتبع 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;
        }
    }            
}

وهذا ما يفعله هو اختيار وتوسيع هذا البند تريفيف في واجهة المستخدم التي تمثل dataitem المحدد في التعليمات البرمجية خلف. وكان الغرض من هذا أن يكون التغيير التحديد في تريفيف عند اختيار المستخدمين تغير في بنود تتحكم في نفس الإطار.

ولقد خلق VisualTreeExt.GetDescendants<T> أسلوب بإرجاع مجموعة enumerable ل العناصر التي تتناسب مع نوع معين:

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;

وانها قليلا من حل القذرة (وربما لا يكون الأكثر فعالية)، وسوف لا تعمل إذا كنت تستخدم تريفيف ظاهرية، لأنه يعتمد على الوجود من العناصر البصرية الفعلية. ولكنه يعمل لوضعي ...

نعم..أنا أعرف العديد من السنوات الماضية منذ أن تم طرح السؤال ولكن..لا يوجد حل سريع لهذه المشكلة..و لذلك:

وفيما يلي سوف تفعل ما OP طلب.

ما أنا أساسا القيام به هو قراءة كل الأجوبة في هذه الصفحة و بعد كل الروابط ذات الصلة لإنشاء مرة واحدة وإلى الأبد حل هذه المشكلة المزعجة.

الفوائد:

  • دعم من خارج الميزانية TreeView كذلك.
  • باستخدام السلوك التقنية ، لذا XAML هو الطريق السهل.
  • ويضيف dependancy-خاصية تسمح الربط المحدد 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
}

و هذا مثال على كيفية TreeView الخط يبدو في XAML:

<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