29

私の一般的な質問は、タイトルが示すように、ViewModelの構築中、または後でLoadedイベント処理を介してデータをロードするのが最善ですか?

答えは、いくつかのLoadedイベント処理を介した構築後だと思いますが、それがViewModelとViewの間でどのように最もきれいに調整されているのか疑問に思います。

私の状況と私が解決しようとしている特定の問題についての詳細は次のとおりです。

UnityforDIだけでなくMVVMLightフレームワークも使用しています。ネストされたビューがいくつかあり、それぞれが対応するViewModelにバインドされています。ViewModelsは、LaurentBugnionがMVVMLightに組み込んだViewModelLocatorのアイデアを介して、各ビューのルートコントロールDataContextにバインドされます。これにより、静的リソースを介してViewModelを検索し、依存性注入フレームワーク(この場合はUnity)を介してViewModelの有効期間を制御できます。また、Expression Blendで、ViewModelとそれらをバインドする方法に関するすべてを確認できます。

とにかく、ViewModelのObservableCollectionにComboBoxデータバインドされた親ビューがあります。ComboBoxのSelectedItemも、ViewModelのプロパティに(双方向で)バインドされます。ComboBoxの選択が変更されると、これは他のビューとサブビューの更新をトリガーします。現在、MVVMLightにあるメッセージングシステムを介してこれを達成しています。これはすべてうまく機能しており、ComboBoxでさまざまなアイテムを選択すると期待どおりに機能します。

ただし、ViewModelは、一連の初期化メソッド呼び出しを介して、構築時にデータを取得しています。これは、ComboBoxの最初のSelectedItemが何であるかを制御したい場合にのみ問題になるようです。MVVM Lightのメッセージングシステムを使用して、現在、ViewModelのSelectedItemプロパティのセッターが更新をブロードキャストし、他の関心のあるViewModelがコンストラクターのメッセージに登録するように設定しています。現在、構築時にViewModelを介してSelectedItemを設定しようとしているようですが、サブViewModelの構築と登録はまだ許可されていません。

ViewModel内のSelectedItemのデータロードと初期設定を調整するための最もクリーンな方法は何でしょうか?私は本当に、ビューのコードビハインドを合理的な範囲でできるだけ少なくすることに固執したいと思っています。ViewModelがデータのロードを確認し、データのロードを続行してセットアップフェーズを完了する方法が必要だと思います。

よろしくお願いします。

4

5 に答える 5

27

イベントの場合は、MVVMLightToolkitのEventToCommandを使用する必要があります。これを使用すると、任意のui要素の任意のイベントをrelaycommandにバインドできます。EventToCommandに関する彼の記事をチェックしてください。

http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx

サンプルをダウンロードしてご覧ください。それは素晴らしい。その場合、コードビハインドは必要ありません。例は次のとおりです。

<Page x:Class="cubic.cats.Wpf.Views.SplashScreenView"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
      xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras"
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
    Title="SplashScreenPage">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <cmd:EventToCommand Command="{Binding LoadedCommand}" />
        </i:EventTrigger>        
    </i:Interaction.Triggers>

    <Grid>
        <Label Content="This is test page" />
    </Grid>
</Page>

表示モードは次のようになります

 public class SplashScreenViewModel : ViewModelBase
    {
        public RelayCommand LoadedCommand
        {
            get;
            private set;
        }

        /// <summary>
        /// Initializes a new instance of the SplashScreenViewModel class.
        /// </summary>
        public SplashScreenViewModel()
        {
            LoadedCommand = new RelayCommand(() =>
            {
                string a = "put a break point here to see that it gets called after the view as been loaded";
            });
        }
    }

ビューモデルにEventArgsを設定する場合は、PassEventArgsToCommandをtrueに設定するだけです。

<i:Interaction.Triggers>
            <i:EventTrigger EventName="Loaded">
                <cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding LoadedCommand}" />
  </i:EventTrigger>        
</i:Interaction.Triggers>

ビューモデルは次のようになります

public class SplashScreenViewModel : ViewModelBase
{
    public RelayCommand<MouseEventArgs> LoadedCommand
    {
        get;
        private set;
    }

    /// <summary>
    /// Initializes a new instance of the SplashScreenViewModel class.
    /// </summary>
    public SplashScreenViewModel()
    {
        LoadedCommand = new RelayCommand<MouseEventArgs>(e =>
        {
            var a = e.WhateverParameters....;
        });
    }

}
于 2010-08-03T11:36:55.603 に答える
5

次のソリューションは、すでに提供され受け入れられているソリューションと似ていますが、ビューモデルのコマンドを使用してデータをロードするのではなく、「通常の方法」を使用します。コマンドはユーザーアクションに適していると思います(コマンドは実行時に使用できる場合とできない場合があります)。そのため、通常のメソッド呼び出しを使用するだけでなく、ビューでインタラクショントリガーを設定することもできます。

私はこれを提案します:ビューモデルクラスを作成します。プロパティ内に作成することにより、ビューのxaml内でビューモデルクラスをインスタンス化しDataContextます。

ビューモデルにデータをロードするメソッドを実装しますLoadData。例:ビューが読み込まれるときにこのメソッドが呼び出されるように、ビューを設定します。これは、ビューモデルのメソッドにリンクされているビューのインタラクショントリガーによって実行されます(「Microsoft.Expression.Interactions」および「System.Windows.Interactivity」への参照が必要です)。

ビュー(xaml):

<Window x:Class="MyWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Test" 
    xmlns:viewModel="clr-namespace:ViewModels"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"            
    >
<Window.DataContext>
    <viewModel:ExampleViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <ei:CallMethodAction TargetObject="{Binding}" MethodName="LoadData"/>
    </i:EventTrigger>
</i:Interaction.Triggers>   

これによりLoadData、実行時にビューがロードされるときにViewModelのメソッドが呼び出されます。これは、データをロードする場所です。

public class ExampleViewModel
{
    /// <summary>
    /// Constructor.
    /// </summary>
    public ExampleViewModel()
    {
        // Do NOT do complex stuff here
    }


    public void LoadData()
    {
        // Make a call to the repository class here
        // to set properties of your view model
    }

リポジトリ内のメソッドが非同期メソッドの場合、メソッドを非同期にすることもできますがLoadData、いずれの場合もこれは必要ありません。

ちなみに、私は通常、ビューモデルのコンストラクターにデータをロードしません。上記の例では、デザイナーがビューを表示したときに、ビューモデルの(パラメーターなしの)コンストラクターが呼び出されます。ここで複雑なことを行うと、ビューを表示するときにデザイナでエラーが発生する可能性があります(同じ理由で、ビューコンストラクタで複雑なことは行いません)。

一部のシナリオでは、ビューモデルコンストラクターのコードが実行時に問題を引き起こす可能性があります。ビューモデルコンストラクターが実行されると、ビューオブジェクトの作成が完全に完了していないときに、ビューの要素にバインドされているビューモデルのプロパティが設定されます。

于 2015-08-03T15:33:06.797 に答える
2

では、それでは。:-)

ビヘイビアーを使用して、ViewModelのメソッドにバインドできます。

これがあなたを助けるリンクです。 http://expressionblend.codeplex.com/

于 2010-03-29T15:55:08.277 に答える
1

XAMLをビューのコードビハインドのLoadedイベントハンドラーに宣言的にバインドすることにしました。このハンドラーは、ビューのルート要素UserControlDataContextを介してViewModelオブジェクトのメソッドを呼び出しました。

これは、かなり単純で、単純明快で、クリーンなソリューションでした。XAMLのICommandsでできるのと同じ宣言的な方法で、LoadedイベントをViewModelオブジェクトにバインドする方法を望んでいたと思います。

私はクリンガーに公式の回答クレジットを与えたかもしれませんが、彼は私の質問にコメントを投稿しましたが、回答ではありません。だから私は少なくとも彼のコメントにワンアップを与えた。

于 2010-03-28T20:54:41.963 に答える
0

親ウィンドウと子ウィンドウの間のメッセージを処理するときに、これと同じ問題が発生しました。ViewModelLocatorクラスでビューモデルが作成される順序を変更するだけです。メッセージを送信するビューモデルの前に、メッセージに依存するすべてのビューモデルが作成されていることを確認してください。

たとえば、ViewModelLocatorクラスのコンストラクターでは次のようになります。

public ViewModelLocator()
{
    if (s_messageReceiverVm == null)
    {
        s_messageReceiverVm = new MessageReceiverVM();
    }

    if (s_messageSenderVm == null)
    {
        s_messageSenderVm = new MessageSenderVM();
    }
}
于 2012-09-19T22:30:38.107 に答える