3

TreeView とカスタム詳細ビュー コントロールを使用して、アプリケーションにマスター/詳細ビューを実装しようとしています。また、MVVM パターンに固執しようとしています。

現在、TreeView はすべての詳細を含むビュー モデル オブジェクトのコレクションにバインドされており、詳細ビューは TreeView の選択された項目にバインドされています。

これはうまく機能します... TreeView ノードの 1 つに 5,000 の子があり、アプリケーションが突然 500MB の RAM を占有するまでは。

メイン ウィンドウ ビュー モデル:

public class MainWindowViewModel
{
    private readonly List<ItemViewModel> rootItems;

    public List<ItemViewModel> RootItems { get { return rootItems; } } // TreeView is bound to this property.

    public MainWindowViewModel()
    {
        rootItems = GetRootItems();
    }

    // ...
}

アイテム ビュー モデル:

public ItemViewModel
{
    private readonly ModelItem item; // Has a TON of properties
    private readonly List<ItemViewModel> children;

    public List<ItemViewModel> Children { get { return children; } }

    // ...
}

詳細ビューをバインドする方法は次のとおりです。

<View:ItemDetails DataContext="{Binding SelectedItem, ElementName=ItemTreeView}" />

私はWPFとMVVMパターンにかなり慣れていませんが、アイテムの表示に必要なプロパティ(名前やIDなど)のみを持つ、より小さく単純化されたオブジェクトのコレクションにTreeViewをバインドしたいのはもったいないようです。それが選択されると、すべての詳細が読み込まれます。このようなことをするにはどうすればよいですか?

4

1 に答える 1

2

概要

これ、TreeView の選択された項目のプロパティをソース上の何かにバインドするという単純な問題です。ただし、TreeView コントロールの作成方法が原因で、すぐに使用できる WPF を使用して MVVM に適したソリューションを取得するには、より多くのコードを記述する必要があります。

バニラの WPF を使用している場合 (そうであると想定しています)、アタッチされたビヘイビアーを使用することをお勧めします。添付された動作は、TreeView の選択が変更されたときに呼び出されるメイン ビュー モデルのアクションにバインドされます。アクションの代わりにコマンドを呼び出すこともできますが、ここではアクションの使用方法を示します。

基本的に、全体的な考え方は、マスター ビュー モデルのプロパティとして使用できるようになる詳細ビュー モデルの 1 つのインスタンスを使用することです。次に、何百ものビュー モデルのインスタンスを持つ RootItems コレクションの代わりに、ノードの表示名とおそらくその背後にある種の id フィールドを持つ軽量オブジェクトを使用できます。TreeView の選択が変更されたら、メソッドを呼び出すか、プロパティを設定して、詳細ビュー モデルに通知する必要があります。以下のデモ コードでは、Selection という DetailsViewModel のプロパティを設定しています。

コードによるウォークスルー

添付された動作のコードは次のとおりです。

public static class TreeViewBehavior
{
    public static readonly DependencyProperty SelectionChangedActionProperty =
        DependencyProperty.RegisterAttached("SelectionChangedAction", typeof (Action<object>), typeof (TreeViewBehavior), new PropertyMetadata(default(Action), OnSelectionChangedActionChanged));

    private static void OnSelectionChangedActionChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var treeView = sender as TreeView;
        if (treeView == null) return;

        var action = GetSelectionChangedAction(treeView);

        if (action != null)
        {
            // Remove the next line if you don't want to invoke immediately.
            InvokeSelectionChangedAction(treeView);
            treeView.SelectedItemChanged += TreeViewOnSelectedItemChanged;
        }
        else
        {
            treeView.SelectedItemChanged -= TreeViewOnSelectedItemChanged;
        }
    }

    private static void TreeViewOnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        var treeView = sender as TreeView;
        if (treeView == null) return;

        InvokeSelectionChangedAction(treeView);

    }

    private static void InvokeSelectionChangedAction(TreeView treeView)
    {
        var action = GetSelectionChangedAction(treeView);
        if (action == null) return;

        var selectedItem = treeView.GetValue(TreeView.SelectedItemProperty);

        action(selectedItem);
    }

    public static void SetSelectionChangedAction(TreeView treeView, Action<object> value)
    {
        treeView.SetValue(SelectionChangedActionProperty, value);
    }

    public static Action<object> GetSelectionChangedAction(TreeView treeView)
    {
        return (Action<object>) treeView.GetValue(SelectionChangedActionProperty);
    }
}

次に、TreeView 要素の XAML で、次を適用しますlocal:TreeViewBehavior.SelectionChangedAction="{Binding Path=SelectionChangedAction}"TreeViewBehaviorクラスの名前空間を local に置き換える必要があることに注意してください。

ここで、次のプロパティを MainWindowViewModel に追加します。

public Action<object> SelectionChangedAction { get; private set; } 
public DetailsViewModel DetailsViewModel { get; private set; }

MainWindowViewModel のコンストラクターで、SelectionChangedAction プロパティを何かに設定する必要があります。SelectionChangedAction = item => DetailsViewModel.Selection = item;DetailsViewModel に Selection プロパティがある場合は、そうすることができます。それは完全にあなた次第です。

最後に、XAML で、次のように詳細ビューをそのビュー モデルに結び付けます。

<View:ItemDetails DataContext="{Binding Path=DetailsViewModel}" />

これは、ストレート WPF を使用した MVVM フレンドリー ソリューションの基本アーキテクチャです。そうは言っても、Caliburn.Micro や PRISM などのフレームワークを使用している場合、アプローチはおそらくここで提供したものとは異なるでしょう。それを覚えておいてください。

于 2013-03-04T20:44:39.903 に答える