古い WinForms アプリを WPF に移植しているときに、これに遭遇しました。心に留めておくべき重要なことは、WPF が内部で行うことの多くを、ビュー モデルとビューの間でイベント (つまり 、 など) でシグナル伝達することによって実現していることINotifyPropertyChanged.PropertyChanged
ですINotifyDataErrorInfo.ErrorsChanged
。この問題に対する私の解決策は、その例を取り上げて実行することでした。私のビューモデルでは:
/// <summary>
/// Occurs before the record is deleted
/// </summary>
public event CancelEventHandler DeletingRecord;
/// <summary>
/// Occurs before record changes are discarded (i.e. by a New or Close operation)
/// </summary>
public event DiscardingChangesEvent DiscardingChanges;
その後、ビューはこれらのイベントをリッスンし、必要に応じてユーザーにプロンプトを表示し、指示があればイベントをキャンセルできます。
CancelEventHandler
フレームワークによって定義されていることに注意してください。ただしDiscardingChanges
、 の場合は、操作をどのように処理するか (つまり、変更を保存する、変更を破棄する、または行っていることをキャンセルする) を示すトライステート結果が必要です。このために、私は自分で作成しました:
public delegate void DiscardingChangesEvent(object sender, DiscardingChangesEventArgs e);
public class DiscardingChangesEventArgs
{
public DiscardingChangesOperation Operation { get; set; } = DiscardingChangesOperation.Cancel;
}
public enum DiscardingChangesOperation
{
Save,
Discard,
Cancel
}
より良い命名規則を考え出そうとしましたが、これが私が考えることができる最高のものでした。
したがって、それを実行に移すと、次のようになります。
ViewModel (これは実際には CRUD ベースのビュー モデルの基本クラスです):
protected virtual void New()
{
// handle case when model is dirty
if (ModelIsDirty)
{
var args = new DiscardingChangesEventArgs(); // defaults to cancel, so someone will need to handle the event to signal discard/save
DiscardingChanges?.Invoke(this, args);
switch (args.Operation)
{
case DiscardingChangesOperation.Save:
if (!SaveInternal())
return;
break;
case DiscardingChangesOperation.Cancel:
return;
}
}
// continue with New operation
}
protected virtual void Delete()
{
var args = new CancelEventArgs();
DeletingRecord?.Invoke(this, args);
if (args.Cancel)
return;
// continue delete operation
}
意見:
<UserControl.DataContext>
<vm:CompanyViewModel DeletingRecord="CompanyViewModel_DeletingRecord" DiscardingChanges="CompanyViewModel_DiscardingChanges"></vm:CompanyViewModel>
</UserControl.DataContext>
コード ビハインドを表示:
private void CompanyViewModel_DeletingRecord(object sender, System.ComponentModel.CancelEventArgs e)
{
App.HandleRecordDeleting(sender, e);
}
private void CompanyViewModel_DiscardingChanges(object sender, DiscardingChangesEventArgs e)
{
App.HandleDiscardingChanges(sender, e);
}
そして、すべてのビューで使用できる App クラスの一部であるいくつかの静的メソッド:
public static void HandleDiscardingChanges(object sender, DiscardingChangesEventArgs e)
{
switch (MessageBox.Show("Save changes?", "Save", MessageBoxButton.YesNoCancel))
{
case MessageBoxResult.Yes:
e.Operation = DiscardingChangesOperation.Save;
break;
case MessageBoxResult.No:
e.Operation = DiscardingChangesOperation.Discard;
break;
case MessageBoxResult.Cancel:
e.Operation = DiscardingChangesOperation.Cancel;
break;
default:
throw new InvalidEnumArgumentException("Invalid MessageBoxResult returned from MessageBox.Show");
}
}
public static void HandleRecordDeleting(object sender, CancelEventArgs e)
{
e.Cancel = MessageBox.Show("Delete current record?", "Delete", MessageBoxButton.YesNo) == MessageBoxResult.No;
}
これらの静的メソッドでダイアログ ボックスを一元化すると、後でカスタム ダイアログに簡単に交換できます。