MVVM のベスト プラクティスに従うデモンストレーション用の WPF アプリケーションを構築しようとしていますが、ベスト プラクティスが何であるかがわからないことがすぐにわかりました。:)
私は今、特定の問題を抱えています。
いくつかの背景として (私のスニペットに表示される場合)、MVVMLight (PCL の移植性のため)、NInject をコンテナーとして、Prism をリージョン サポートのために使用しています。
意見
[スニペット]
<!-- bind the selection of a new item to the view model -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding SelectTypeCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<!-- visual template of the list items -->
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock>Name: <Run Text="{Binding Name}"></Run></TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- detail view -->
<Grid Grid.Row="0" Grid.Column="1">
<StackPanel Orientation="Vertical" Margin="5">
<Label>ID</Label>
<TextBox Text="{Binding SelectedType.Id, Mode=TwoWay}" IsEnabled="False"></TextBox>
<Label>Name</Label>
<TextBox Text="{Binding SelectedType.Name, Mode=TwoWay}"></TextBox>
</StackPanel>
</Grid>
ビューモデル
[スニペット]
public class ClientTypesViewModel : BaseUpdateableViewModel
{
private ThingType selectedtype = null;
public ThingType SelectedType
{
get { return selectedtype; }
protected set { Set(() => SelectedType, ref selectedtype, value); }
}
private RelayCommand<ThingType> selecttypecommand;
public RelayCommand<ThingType> SelectTypeCommand
{
get { return selecttypecommand ?? (selecttypecommand = new RelayCommand<ThingType>(ExecuteSelectTypeCommand)); }
}
private async void ExecuteSelectTypeCommand(ThingType newtype)
{
// Save the type if required - goes away to a service (HttpClient...)
if (!await SaveSelectedType())
{
// Cancel navigation?
return;
}
// Update the selected type
this.SelectedType = newtype;
}
private async Task<bool> SaveSelectedType()
{
if (selectedtype == null) return true;
if (!selectedtype.IsDirty) return true;
bool? result = await navigationservice.AskConfirmation("Do you want to save this client type?", "Yes", "No", "Cancel");
if (result == null)
return false; // cancel
if (!result.Value)
{
selectedtype.MakeClean();
return true; // ignore changes
}
// Ask the data service to save for us
await dataservice.UpdateClientType(selectedtype);
selectedtype.MakeClean();
return true;
}
}
左側の 2 つの列には、エンティティのリストが保持されます。いずれかを選択すると、右側の詳細列が更新されて表示/編集できるようになります。エンティティが右側のパネルでユーザーによって編集された場合、そのビュー モデルに「ダーティ」のマークを付けます。
ユーザーが左側の列で別のエンティティを選択しようとしたときに、(ViewModel で) ユーザーに移動して変更を失いたいのか、それとも保存したいのかを尋ねられるようにしたいと考えています。 .
これは (ナビゲーション サービスを介して) 提示できますが、ビュー モデルで「キャンセル」を実際に機能させる方法について途方に暮れており、アプローチ全体を再考しています。
を両方向にバインドしていた場合SelectedItem
、 が起動される前に基になるフィールドを更新できなかったと思いますRaisePropertyChanged
が、非同期コードを呼び出してエンティティ (HttpClient) を永続化する必要があるため、 a 内からそれを行うことはできませんプロパティ セッター。
だから私は、恐ろしいハックのように感じるものがなければ、実際に仕事をすることができないという上記のことを行ってきました。
私が見ていない、これに対するより良い一般的な解決策はありますか?それとももっと良い例がありますか?
編集
ここで何を達成しようとしているのかが明確ではないことに気付きました。問題は、行われた変更を元に戻す方法ではありません.@Alanは、それに対処するための標準的な方法がいくつかあることを正しく指摘しています.
左ペインで選択したタイプを変更しようとして、ユーザーに次のように尋ねています。
このタイプを保存しますか?
[はい] - 保存してナビゲーションを許可
[いいえ] - 変更を元に戻し、ナビゲーションを許可します
[キャンセル] - 変更を保持し、ナビゲーションをキャンセルします
その【キャンセル】はどう処理したらいいのかわからない。