このMVVMファッションを行う方法は次のとおりです。
まず、viewmodelクラスを作成します。ここでは、WatchedFile
インスタンスのコレクションを持つメインのビューモデルがあり、次にWatchedFile
クラスがあります。また、文字列だけでなく、Tag
をクラスにすることにしました。これにより、一般的な文字列ではなく、Tag
のインスタンスを明示的かつ排他的に表示するXAMLにデータテンプレートを書き込むことができます。 UIは文字列でいっぱいです。
スニペットがない場合は、通知プロパティーの作成が面倒です。 I have snippets(それらを盗む!彼らは釘付けにされていない!)。
これを取得したら、並べ替えは大したことではありません。ルートレベルのアイテムを並べ替える場合は、WatchedFile
です。
SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
ただし、以下のXAMLで説明します。
シリアライザもシンプルです。ビューモデルをシリアライズ可能にするだけです。ここで重要なことは、ソートとシリアライゼーションがツリービューアイテムテンプレートに何があるか気にする必要がないことです。 StackPanels、GroupBox、何でも - 大事なことではありません。ソートやシリアライゼーションコードは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
}
}
コード。注:ここでは、ウィザードが私に与えたものに1行追加しました。
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
のアイテムがインスタンス化されると、各ルートアイテムのDataContext
のクラスインスタンスはWatchedFile
になります。 WatchedFile
には暗黙的なデータテンプレートがあるので、その内容を記入するために使用されます。 TreeView
は再帰型なので、DataTemplate
の代わりにHierarchicalDataTemplate
を使用します。 HierarchicalDataTemplate
は、DataContext
オブジェクトで子を探す場所を項目に指示するItemSource
プロパティを追加します。 WatchedFile.Tags
は、ルートレベルのツリー項目の場合はItemsSource
です。それらはTag
であり、暗黙的にHierarchicalDataTemplate
があります。子供がいないので、HierarchicalDataTemplate.ItemsSource
は使用しません。
すべてのコレクションがObservableCollection<T>
であるため、いつでもコレクションにアイテムを追加したり削除したりできます。UIは自動的に更新されます。 Name
とPath
のプロパティをWatchedFile
とすると、INotifyPropertyChanged
が実装されており、値が変更されたときにそのプロパティの値が「PropertyChanged
」になるので、同じことができます。 XAML UIは通知されずにすべての通知イベントにサブスクライブし、適切な処理を行います。
あなたの分離コードはFindResource
でSortedWatchedFiles
を取得し、そのSortDescriptions
を変更するが、それはあなたがツリービューを移入している方法については不可知論者だから、これは、私に多くの意味がありますすることができます:背後
<Button Content="Re-Sort" Click="Button_Click" HorizontalAlignment="Left" />
<!-- ... snip ... -->
<TreeView
x:Name="WatchedFilesTreeView"
...etc. as before...
コード:
private void Button_Click(object sender, RoutedEventArgs e)
{
var cv = CollectionViewSource.GetDefaultView(WatchedFilesTreeView.ItemsSource);
cv.SortDescriptions.Clear();
cv.SortDescriptions.Add(
new System.ComponentModel.SortDescription("Name",
System.ComponentModel.ListSortDirection.Descending));
}
非常に詳細な回答をいただきありがとうございます - 私は非常に好きです。それは今までにうまく動作し、私はMVVMにもう少し持ってきている。私はまだいくつかの問題を抱えています。私は、ファイル/フォルダ自体に複数のエントリがあるようにしたいと思います。だから私は監視されたコレクションをWatchedFileに追加しました – Thoms
さらなる質問のために次の "答え"を見てください – Thoms
申し訳ありませんが、正しく読み込めませんでした) 'ItemsSource =" {Binding Tags} "'を 'ItemsSource =" {Binding Subs} ' – Thoms