[英]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
流程包括:
if
not numerical, break
operation (DONE) if
不是数字,则break
操作(完成) else
-- continue with adding child item (DONE) else
-继续添加子项(完成) if
duplicate is found break
operation (DONE) if
发现重复则break
操作(完成) else
-- continue (DONE) else
-继续(完成) List
of TreeViewItem
(DONE -- But not implemented) TreeViewItem
List
(完成-但未实现) TreeViewItem
for new child node (DONE) TreeViewItem
(完成) header
is set from user text in textBox
(DONE) textBox
用户文本设置TVI header
(DONE) List
in numerical order (Problem Area) List
函数(问题区域) 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.