简体   繁体   English

在C#中对TreeViewItems列表进行数字排序

[英]Numerically sort a List of TreeViewItems in C#

This question is a follow up to this question. 这个问题是一个跟进这个问题。 My overall goal at the moment is to add to my program's TreeViewItem (my TreeViewItem has child nodes added to it at run-time) in numerical ascending order according to the value entered in for the header . 目前,我的总体目标是根据header输入的值,以数字升序将其添加到程序的TreeViewItem (我的TreeViewItem在运行时添加了子节点)。

I received an answer using a ModelView , a tool that I am not all that familiar with, and I was also told that this could be done by using a List of TreeViewItems . 我收到了一个使用ModelView的答案,该工具并不是我所不熟悉的,而且还告诉我可以使用TreeViewItems List来完成。 I have decided to explore the List option due to my lack of experience with ModelView . 由于缺乏对ModelView的经验,我决定探索List选项。

In my research I've learned that Lists of TreeViewItems are a little different, because you really can't reference them like you can an array . 在我的研究中,我了解到TreeViewItems Lists有些不同,因为您真的不能像array一样引用它们。 This makes them more difficult to do work with. 这使它们更难处理。 I'll explain my current method and post my code. 我将解释当前的方法并发布代码。 Please steer me in the right direction and provide answers with coding solutions. 请指引我正确的方向,并提供编码解决方案的答案。 I'm currently stuck on my treeViewListAdd function. 我目前停留在treeViewListAdd函数上。 I have written pseudo code in comments on what I am trying to do with that area. 我在关于该区域要做什么的注释中编写了伪代码。

*Note: I am adding to my TreeViewItem from a separate window *注意:我是从单独的窗口添加到TreeViewItem

Right now my add TreeViewItem process consists of: 现在,我的添加TreeViewItem流程包括:

  1. Check to see if item entered is numerical (DONE) 检查输入的项目是否为数字(完成)
  2. if not numerical, break operation (DONE) if不是数字,则break操作(完成)
  3. else -- continue with adding child item (DONE) else -继续添加子项(完成)
  4. Check for duplicate children (DONE) 检查重复的孩子(完成)
  5. if duplicate is found break operation (DONE) if发现重复则break操作(完成)
  6. else -- continue (DONE) else -继续(完成)
  7. Create List of TreeViewItem (DONE -- But not implemented) 创建TreeViewItem List (完成-但未实现)
  8. Create TreeViewItem for new child node (DONE) 为新的子节点创建TreeViewItem (完成)
  9. TVI header is set from user text in textBox (DONE) 从文本textBox用户文本设置TVI header (DONE)
  10. Pass to function that attempts to add to the List in numerical order (Problem Area) 传递给尝试按数字顺序添加到List函数(问题区域)
  11. Add sorted List to TreeViewItem in main window (Problem Area) 在主窗口(问题区域)中将排序后的List添加到TreeViewItem

My current code: 我当前的代码:

//OKAY - Add child to TreeViewItem in Main Window
private void button2_Click(object sender, RoutedEventArgs e)
{
    //STEP 1: Checks to see if entered text is a numerical value
    string Str = textBox1.Text.Trim();
    double Num;
    bool isNum = double.TryParse(Str, out Num);

    //STEP 2: If not numerical value, warn user
    if (isNum == false)
        MessageBox.Show("Value must be Numerical");
    else //STEP 3: else, continue
    {
        //close window
        this.Close();

        //Query for Window1
        var mainWindow = Application.Current.Windows
            .Cast<Window1>()
            .FirstOrDefault(window => window is Window1) as Window1;

        //STEP 4: Check for duplicate
        //declare TreeViewItem from mainWindow
        TreeViewItem locations = mainWindow.TreeViewItem;
        //Passes to function -- checks for DUPLICATE locations
        CheckForDuplicate(locations.Items, textBox1.Text);

        //STEP 5: if Duplicate exists -- warn user
        if (isDuplicate == true)
            MessageBox.Show("Sorry, the number you entered is a duplicate of a current Node, please try again.");
        else //STEP 6: else -- create child node
        {
            //STEP 7
            List<TreeViewItem> treeViewList = new List<TreeViewItem>();

            //STEP 8: Creates child TreeViewItem for TVI in main window
            TreeViewItem newLocation = new TreeViewItem();

            //STEP 9: Sets Headers for new child nodes
            newLocation.Header = textBox1.Text;

            //STEP 10: Pass to function -- adds/sorts List in numerical ascending order
            treeViewListAdd(ref treeViewList, newLocation);

            //STEP 11: Add children to TVI in main window
            //This step will of course need to be changed to add the list
            //instead of just the child node
            mainWindow.TreeViewItem.Items.Add(newLocation);
        }
    }
}

//STEP 4: Checks to see whether the header entered is a DUPLICATE
private void CheckForDuplicate(ItemCollection treeViewItems, string input)
{
        for (int index = 0; index < treeViewItems.Count; index++)
        {
            TreeViewItem item = (TreeViewItem)treeViewItems[index];
            string header = item.Header.ToString();

            if (header == input)
            {
                isDuplicate = true;
                break;
            }
            else
                isDuplicate = false;
        }
}

//STEP 10: Adds to the TreeViewItem list in numerical ascending order
private void treeViewListAdd(ref List<TreeViewItem> currentList, TreeViewItem addLocation)
{
        //if there are no TreeViewItems in the list, add the current one
        if (currentList.Count() == 0)
            currentList.Add(addLocation);
        else
        {
            //gets the index of the last item in the List
            int lastItem = currentList.Count() - 1;

            /*
            if (value in header > lastItem)
                currentList.Add(addLocation);
            else
            {
                //iterate through list and add TreeViewItem
                //where appropriate
            }
            **/
        }
}

Thanks a lot for the help. 非常感谢您的帮助。 I tried to show that I have been working on this and trying everything that I could on my own. 我试图表明我一直在努力,并尝试自己能做的一切。

As requested, here is the structure of my TreeView . 根据要求,这是我的TreeView的结构。 Everything from the 3rd level and down is dynamically added by the user... 用户从第3层开始的所有内容都会动态添加...

在此处输入图片说明

Ok. 好。 Delete all your code and start all over. 删除所有代码,然后重新开始。

1: It is essential that you read up on MVVM before writing a single line of code in WPF. 1:在WPF中编写一行代码之前,必须先阅读MVVM。

You can read about it here and here and here 您可以在这里这里这里阅读有关它的信息。

<Window x:Class="MiscSamples.SortedTreeView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cmp="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        Title="SortedTreeView" Height="300" Width="300">
    <DockPanel>
        <TextBox Text="{Binding NewValueString}" DockPanel.Dock="Top"/>
        <Button Click="AddNewItem" DockPanel.Dock="Top" Content="Add"/>
        <TreeView ItemsSource="{Binding ItemsView}" SelectedItemChanged="OnSelectedItemChanged">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding ItemsView}">
                    <TextBlock Text="{Binding Value}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </DockPanel>
</Window>

Code Behind: 背后的代码:

public partial class SortedTreeView : Window
{
    public SortedTreeViewWindowViewModel ViewModel { get { return DataContext as SortedTreeViewWindowViewModel; } set { DataContext = value; } }

    public SortedTreeView()
    {
        InitializeComponent();
        ViewModel = new SortedTreeViewWindowViewModel()
            {
                Items = {new TreeViewModel(1)}
            };
    }

    private void AddNewItem(object sender, RoutedEventArgs e)
    {
        ViewModel.AddNewItem();
    }

    //Added due to limitation of TreeViewItem described in http://stackoverflow.com/questions/1000040/selecteditem-in-a-wpf-treeview
    private void OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        ViewModel.SelectedItem = e.NewValue as TreeViewModel;
    }
}

2: First thing you can notice above is that the Code Behind does NOTHING. 2:您在上面注意到的第一件事是背后的代码没有执行任何操作。 It just delegates functionality to something called the ViewModel (not ModelView, which is a misspelling) 它只是将功能委托给称为ViewModel的东西(不是ModelView,这是拼写错误)

Why is that? 这是为什么?

Because it's a much better approach. 因为这是一种更好的方法。 Period. 期。 Having the application logic / business logic and data separated and decoupled from the UI is the best thing to ever happen to any developer. 将应用程序逻辑/业务逻辑和数据 UI 分离分离是任何开发人员有史以来最好的事情。

So, What's the ViewModel about? 那么,ViewModel是关于什么的呢?

3: The ViewModel exposes Properties that contain the Data to be shown in the View, and Methods that contain the logic to operate with the Data. 3: ViewModel公开了包含要在视图中显示的数据的 Properties ,以及公开了用于对数据进行操作的逻辑的Methods

So it's as simple as: 如此简单:

public class SortedTreeViewWindowViewModel: PropertyChangedBase
{
    private string _newValueString;
    public int? NewValue { get; set; }

    public string NewValueString
    {
        get { return _newValueString; }
        set
        {
            _newValueString = value;
            int integervalue;

            //If the text is a valid numeric value, use that to create a new node.
            if (int.TryParse(value, out integervalue))
                NewValue = integervalue;
            else
                NewValue = null;

            OnPropertyChanged("NewValueString");
        }
    }

    public TreeViewModel SelectedItem { get; set; }

    public ObservableCollection<TreeViewModel> Items { get; set; }

    public ICollectionView ItemsView { get; set; }

    public SortedTreeViewWindowViewModel()
    {
        Items = new ObservableCollection<TreeViewModel>();
        ItemsView = new ListCollectionView(Items) {SortDescriptions = { new SortDescription("Value",ListSortDirection.Ascending)}};
    }

    public void AddNewItem()
    {
        ObservableCollection<TreeViewModel> targetcollection;

        //Insert the New Node as a Root node if nothing is selected.
        targetcollection = SelectedItem == null ? Items : SelectedItem.Items;

        if (NewValue != null && !targetcollection.Any(x => x.Value == NewValue))
        {
            targetcollection.Add(new TreeViewModel(NewValue.Value));
            NewValueString = string.Empty;    
        }

    }
}

See? 看到? All your 11 requirements are fulfilled by 5 lines of code in the AddNewItem() method. AddNewItem()方法中的5行代码可以满足您所有11个要求。 No Header.ToString() stuff, no casting anything, no horrible code behind approaches. 没有Header.ToString()东西,没有强制转换,在方法后面没有可怕的代码。

Just simple, simple properties and INotifyPropertyChanged . 只是简单,简单的属性和INotifyPropertyChanged

And what about the sorting? 那排序呢?

4: The sorting is performed by the CollectionView , and it occurs at the ViewModel level, not at the View Level. 4:排序是由CollectionView执行的,它发生在ViewModel级别,而不是View级别。

Why? 为什么?

Because Data is kept separate from it's visual representation in the UI. 因为在UI中,数据与其视觉表示是分开的。

5: Finally, here is the actual Data Item: 5:最后是实际的数据项:

public class TreeViewModel: PropertyChangedBase
{
    public int Value { get; set; }

    public ObservableCollection<TreeViewModel> Items { get; set; }

    public CollectionView ItemsView { get; set; }

    public TreeViewModel(int value)
    {
        Items = new ObservableCollection<TreeViewModel>();
        ItemsView = new ListCollectionView(Items)
            {
                SortDescriptions =
                    {
                        new SortDescription("Value",ListSortDirection.Ascending)
                    }
            };
        Value = value;
    }
}

This is the class that will hold the data, in this case, an int Value, because you only care about numbers, so that's the right data type, and then an ObservableCollection that will hold the child nodes, and again the CollectionView that takes care of the sorting. 这是将保存数据的类,在这种情况下为int值,因为您只关心数字,所以这才是正确的数据类型,然后是将保存子节点的ObservableCollection ,然后是负责保存数据的CollectionView的排序。

6: Whenever you use DataBinding in WPF (which is essential to all this MVVM thing) you need to implement INotifyPropertyChanged , so this is the PropertyChangedBase class All ViewModels inherit from: 6:每当在WPF中使用DataBinding(这对于所有MVVM事情都是必不可少的)时,都需要实现INotifyPropertyChanged ,这就是PropertyChangedBase类,所有ViewModel都继承自:

public class PropertyChangedBase:INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        Application.Current.Dispatcher.BeginInvoke((Action) (() =>
                                                                 {
                                                                     PropertyChangedEventHandler handler = PropertyChanged;
                                                                     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
                                                                 }));
    }
}

And wherever there's a change in a property you need to Notify that by doing: 无论属性有何变化,都需要执行以下操作来通知:

OnPropertyChanged("PropertyName");

as in

OnPropertyChanged("NewValueString");

And this is the result: 结果如下:

在此处输入图片说明

  • Just copy and paste all my code in a File -> New Project -> WPF Application and see the results for yourself. 只需将我的所有代码复制并粘贴到File -> New Project -> WPF Application然后亲自查看结果。

  • Let me know if you need me to clarify anything. 让我知道您是否需要我澄清任何事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM