MVVM ViewModelの構築中または後にデータをロードしますか?
-
22-09-2019 - |
質問
私の一般的な質問は、タイトルの状態と同じです、ビューモデルの構築中にデータをロードするのが最善ですか、それともいくつかのロードされたイベント処理を通して
答えは、いくつかのロードされたイベント処理による構築後のものだと思いますが、それがViewModelとViewの間でどのようにきれいに調整されているのだろうと思いますか?
私の状況と私が解決しようとしている特定の問題の詳細を次に示します。
MVVMライトフレームワークとDIのUnityを使用しています。ネストされたビューがいくつかあり、それぞれが対応するViewModelにバインドされています。 ViewModelsは、Laurent BugnionがMVVMライトに入れたViewModellocatorのアイデアを介して、各ビューのルートコントロールデータコンテキストにバインドされています。これにより、静的リソースを介してビューモデルを見つけることができ、この場合は依存噴射フレームワークを介してビューモデルの寿命を制御できます。また、ビューモデルとそれらをバインドする方法に関するすべてを見るための式ブレンドも可能になります。
とにかく、私はそのviewmodelの観測可能な収集のコンボボックスデータバウンドを持っている親ビューを持っています。コンボボックスのselectedItemは、ビューモデル上のプロパティにも(双方向)バインドされています。コンボボックスの選択が変更されると、これは他のビューやサブビューで更新をトリガーすることです。現在、MVVM Lightで見つかったメッセージングシステムを介してこれを達成しています。これはすべてうまく機能し、コンボボックスで異なるアイテムを選択すると予想どおりに機能します。
ただし、ViewModelは、一連の初期化メソッド呼び出しを介して、建設時にデータを取得しています。これは、コンボボックスの最初の選択項目が何であるかを制御したい場合にのみ問題のようです。 MVVM Lightのメッセージングシステムを使用して、現在、ViewModelのSelectedItemプロパティのセッターがアップデートを放送し、その他の興味のあるViewModelsがコンストラクターのメッセージに登録する場所である場所を設定しています。現在、建設時にViewModelを介してSelectedItemを設定しようとしているようです。これにより、サブビューモデルを構築して登録することはできません。
ViewModel内のSelectedItemのデータ負荷と初期設定を調整する最もクリーンな方法は何ですか?私は本当に合理的であると同じくらいビューのコードビハインドに少し入れたいと思っています。ビューモデルが物事がいつロードされたかを知る方法が必要だと思います。その後、データをロードしてセットアップフェーズを完成させ続けることができます。
ご回答ありがとうございます。
解決
イベントには、MVVM Light ToolkitのEventToCommandを使用する必要があります。これを使用すると、任意のUI要素の任意のイベントをRelayCommandにバインドできます。 EventToCommandの彼の記事をチェックしてください
サンプルをダウンロードして、見てください。それは素晴らしい。その場合、CodeBehindは必要ありません。例は次のとおりです。
<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を簡単に設定できます。
<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....;
});
}
}
他のヒント
次のソリューションは、既に提供および受け入れられているソリューションと類似していますが、データをロードするためにビューモデルのコマンドを使用するのではなく、「通常の方法」です。コマンドはユーザーアクションに適していると思います(コマンドは利用可能であり、実行時に利用できません)。そのため、通常のメソッドコールを使用するだけでなく、ビューにインタラクショントリガーを設定することもできます。
これをお勧めします。ビューモデルクラスを作成します。ビューのXAML内にビューモデルクラスをインスタンス化して、内部で作成して作成します DataContext
財産。
ビューモデルにデータをロードする方法を実装します。 LoadData
。ビューがロードされたときにこの方法が呼び出されるように、ビューを設定します。これは、ビューモデルのメソッドにリンクされている[Microsoft.Expression.Interactions]および「System.Windows.Interactivity」への参照にリンクされているビューでの相互作用トリガーによって行われます。
view(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
ビューがロードされた実行時のビューモデルのメソッド。これは、データをロードする場所です。
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
メソッドアセンキングもありますが、これはそれぞれの場合には必要ありません。
ちなみに、一般に、ビューモデルのコンストラクターにデータをロードしません。上記の例では、ビューモデルの(パラメーター以下)コンストラクターが呼び出されたときに呼び出されます。ここで複雑なことをすることは、あなたのビューを示すときにデザイナーにエラーを引き起こす可能性があります(同じ理由で、ビューコンストラクターで複雑なものを作成しません)。
ビューモデルの一部のシナリオコードでは、コンストラクターが実行時に問題を引き起こす可能性があります。ビューモデルコンストラクターが実行されると、ビュー内の要素に結合したビューモデルのプロパティを設定しますが、ビューオブジェクトは完全に作成されていません。
XAMLをビューのコードビヒンド上のロードされたイベントハンドラーに宣言的にバインドすることにしました。これは、ビューのルート要素usercontrol datacontextを介して、ビューモデルオブジェクトのメソッドと呼ばれるだけです。
それはかなりシンプルで、率直で、きれいな解決策でした。私は、XAMLのICommandと同じ宣言的な方法で、ロードされたイベントをViewModelオブジェクトにバインドする方法を望んでいたと思います。
私はクリンガーに公式の回答クレジットを与えたかもしれませんが、彼は私の質問にコメントを投稿しましたが、回答ではありません。だから私は少なくとも彼に彼のコメントに1つを与えた。
親ウィンドウとチャイルドウィンドウの間のメッセージを扱うとき、私は同じ問題を抱えていました。 ViewModellocatorクラスでビューモデルが作成される順序を変更するだけです。メッセージに依存するすべてのビューモデルが、メッセージを送信するビューモデルの前に作成されていることを確認してください。
たとえば、ViewModellocatorクラスのコンストラクターで:
public ViewModelLocator()
{
if (s_messageReceiverVm == null)
{
s_messageReceiverVm = new MessageReceiverVM();
}
if (s_messageSenderVm == null)
{
s_messageSenderVm = new MessageSenderVM();
}
}