我试图创建一些快速定位和观看文件的东西。所以我创建了一个将StackPanels作为Items的TreeView。 StackPanel包含图像和标签。
private TreeViewItem createFile(string Name, string soureFile)
{
TreeViewItem tvi = new TreeViewItem();
StackPanel sp = new StackPanel();
Image i = new Image();
Label l_Text = new Label();
Label l_FileName = new Label();
l_FileName.Content = soureFile;
l_FileName.Width = 0;
l_Text.Content = Name;
System.Drawing.Bitmap dImg = (System.Drawing.Bitmap)Properties.Resources.ResourceManager.GetObject("Picture");
MemoryStream ms = new MemoryStream();
dImg.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
BitmapImage bImg = new BitmapImage();
bImg.BeginInit();
bImg.StreamSource = new MemoryStream(ms.ToArray());
bImg.EndInit();
i.Source = bImg;
i.Height = 20;
i.Width = 20;
sp.Name = "SP_File";
sp.Orientation = Orientation.Horizontal;
sp.Children.Add(i);
sp.Children.Add(l_Text);
sp.Children.Add(l_FileName);
tvi.Header = sp;
return tvi;
}
可以创建逻辑文件夹(仅用于创建结构)并将文件和其他文件夹添加到文件夹(仅引用hdd上的实际文件)。这工作正常,直到我尝试排序TreeView。我阅读了使用
对TreeViews进行排序的内容 SortDescriptions.Add(new SortDescription("Header", ListSortDirection.Ascending));
显然这对我来说不起作用,因为我无法交换" Header"用" Header.StackPanel.Label.Text" 随着我进一步阅读,似乎我通过不使用MVVM(Numerically sort a List of TreeViewItems in C#)使用了错误的方法。
由于我没有使用MVVM的经验,有人可以向我解释一下如何用MVVM做到这一点?我使用了一个列表" watchedFile"保留文件和文件夹。
我基本上有一个文件的下面的类
class watchedFile
{
public string name { get; private set; }
public string path { get; private set; }
public List<string> tags { get; private set; }
public watchedFile(string Name, string Path, List<string> Tags)
{
name = Name;
path = Path;
tags = Tags;
}
}
如果path为null或清空其文件夹。 TreeViewItem有一个小图片,显示一个&#34;文件夹&#34;或&#34;图片&#34;,一个标签,显示&#34; watchedFile.name&#34;和一个不可见的标签,其中包含watchedfile.path(仅显示为工具提示)。我想我应该使用DataBinding这样做,所以我不需要添加一个不可见的标签。
问题:
非常感谢任何帮助。
答案 0 :(得分:1)
这里是如何做这个MVVM时尚的。
首先,编写viewmodel类。在这里,我们有一个主视图模型,其中包含WatchedFile
个实例的集合,然后我们自己获得了WatchedFile
类。我还决定让Tag
成为一个类,而不仅仅是使用字符串。这使我们可以在XAML中编写数据模板,这些模板显式地和唯一地用于显示Tag
实例,而不是一般的字符串。用户界面充满了字符串。
如果您没有片段,通知属性会很繁琐。 I have snippets(偷了他们!他们没有被钉死!)。
一旦你有了这个,排序就没什么大不了的了。如果要对根级别项目进行排序,则这些项目为WatchedFile
。
SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
但我们将在下面的XAML中做到这一点。
序列化也很简单:只需使您的viewmodel类可序列化即可。这里重要的是,您的排序和序列化不必关心树视图项模板中的内容。 StackPanels,GroupBoxes,无论如何 - 它根本不重要,因为你的排序和序列化代码只处理你的数据类,而不是UI的东西。您可以从根本上更改数据模板中的可视详细信息,而不必担心它会影响代码的任何其他部分。这对MVVM来说是个好消息。
的ViewModels:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace WatchedFile.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class WatchedFile : ViewModelBase
{
#region Name Property
private String _name = default(String);
public String Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
OnPropertyChanged();
}
}
}
#endregion Name Property
#region Path Property
private String _path = default(String);
public String Path
{
get { return _path; }
set
{
if (value != _path)
{
_path = value;
OnPropertyChanged();
}
}
}
#endregion Path Property
#region Tags Property
private ObservableCollection<Tag> _tags = new ObservableCollection<Tag>();
public ObservableCollection<Tag> Tags
{
get { return _tags; }
protected set
{
if (value != _tags)
{
_tags = value;
OnPropertyChanged();
}
}
}
#endregion Tags Property
}
public class Tag
{
public Tag(String value)
{
Value = value;
}
public String Value { get; private set; }
}
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
Populate();
}
public void Populate()
{
// Arbitrary test info, just for display.
WatchedFiles = new ObservableCollection<WatchedFile>
{
new WatchedFile() { Name = "foobar.txt", Path = "c:\\testfiles\\foobar.txt", Tags = { new Tag("Testfile"), new Tag("Text") } },
new WatchedFile() { Name = "bazfoo.txt", Path = "c:\\testfiles\\bazfoo.txt", Tags = { new Tag("Testfile"), new Tag("Text") } },
new WatchedFile() { Name = "whatever.xml", Path = "c:\\testfiles\\whatever.xml", Tags = { new Tag("Testfile"), new Tag("XML") } },
};
}
#region WatchedFiles Property
private ObservableCollection<WatchedFile> _watchedFiles = new ObservableCollection<WatchedFile>();
public ObservableCollection<WatchedFile> WatchedFiles
{
get { return _watchedFiles; }
protected set
{
if (value != _watchedFiles)
{
_watchedFiles = value;
OnPropertyChanged();
}
}
}
#endregion WatchedFiles Property
}
}
代码背后。注意我在这里只添加了一行到向导给我的内容。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WatchedFile
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModels.MainViewModel();
}
}
}
最后是XAML:
<Window
x:Class="WatchedFile.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:local="clr-namespace:WatchedFile"
xmlns:vm="clr-namespace:WatchedFile.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource
x:Key="SortedWatchedFiles"
Source="{Binding WatchedFiles}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name" Direction="Ascending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<TreeView
ItemsSource="{Binding Source={StaticResource SortedWatchedFiles}}"
>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type vm:WatchedFile}"
ItemsSource="{Binding Tags}"
>
<TextBlock
Text="{Binding Name}"
ToolTip="{Binding Path}"
/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type vm:Tag}"
>
<TextBlock
Text="{Binding Value}"
/>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Window>
XAML不太明显。 TreeView.Resources
适用于任何TreeView
的孩子。 HierarchicalDataTemplate
没有x:Key
属性,这使得他们隐式。这意味着当TreeView
项被实例化时,每个根项将为其WatchedFile
具有DataContext
类实例。由于WatchedFile
具有隐式数据模板,因此将用于填充其内容。 TreeView
是递归的,因此它使用HierarchicalDataTemplate
而非常规DataTemplate
。 HierarchicalDataTemplate
添加了ItemSource
属性,该属性告诉项目在DataContext
对象上查找子项的位置。 WatchedFile.Tags
是根级树项的ItemsSource
。这些是Tag
,它有自己的隐式HierarchicalDataTemplate
。它没有孩子,因此它不会使用HierarchicalDataTemplate.ItemsSource
。
由于我们的所有馆藏都是ObservableCollection<T>
,您可以随时在任何馆藏中添加和删除项目,并且UI会自动更新。您可以对Name
的{{1}}和Path
属性执行相同的操作,因为它实现了WatchedFile
,并且当其值发生更改时,其属性会引发INotifyPropertyChanged
。 XAML UI在没有被告知的情况下订阅所有通知事件,并做正确的事情 - 假设您已经告诉它需要知道做什么。
您的代码隐藏可以使用PropertyChanged
获取SortedWatchedFiles
并更改其FindResource
,但这对我来说更有意义,因为它不知道您是如何填充的树视图:
SortDescriptions
代码背后:
<Button Content="Re-Sort" Click="Button_Click" HorizontalAlignment="Left" />
<!-- ... snip ... -->
<TreeView
x:Name="WatchedFilesTreeView"
...etc. as before...
答案 1 :(得分:-1)
或者非MVVM解决方案本来是......
我看到您的标题是一个有两个孩子的StackPanel,您希望对标签的内容(第二个孩子)进行排序
由于数组是基于0的,因此您可以将标签子项作为位置[1]
的数组来访问。
TreeView1.Items.SortDescriptions.Clear();
TreeView1.Items.SortDescriptions.Add(new SortDescription("Header.Children[1].Content", ListSortDirection.Ascending));
TreeView1.Items.Refresh();