用MVVM与wpf对话的好习惯或不好的做法?

我最近有问题为我的WPF应用程序创build添加和编辑对话框。

我想在我的代码中做的就是这样的。 (我主要使用MVVM的viewmodel第一种方法)

调用对话窗口的ViewModel:

var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM); // Do anything with the dialog result 

它是如何工作的?

首先,我创build了一个对话服务:

 public interface IUIWindowDialogService { bool? ShowDialog(string title, object datacontext); } public class WpfUIWindowDialogService : IUIWindowDialogService { public bool? ShowDialog(string title, object datacontext) { var win = new WindowDialog(); win.Title = title; win.DataContext = datacontext; return win.ShowDialog(); } } 

WindowDialog是一个特殊但简单的窗口。 我需要它来保存我的内容:

 <Window x:Class="WindowDialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Title="WindowDialog" WindowStyle="SingleBorderWindow" WindowStartupLocation="CenterOwner" SizeToContent="WidthAndHeight"> <ContentPresenter x:Name="DialogPresenter" Content="{Binding .}"> </ContentPresenter> </Window> 

wpf中对话框的一个问题是dialogresult = true只能在代码中实现。 这就是为什么我为我的dialogviewmodel创build一个接口来实现它。

 public class RequestCloseDialogEventArgs : EventArgs { public bool DialogResult { get; set; } public RequestCloseDialogEventArgs(bool dialogresult) { this.DialogResult = dialogresult; } } public interface IDialogResultVMHelper { event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog; } 

每当我的ViewModel认为是时候了dialogresult = true ,然后引发这个事件。

 public partial class DialogWindow : Window { // Note: If the window is closed, it has no DialogResult private bool _isClosed = false; public DialogWindow() { InitializeComponent(); this.DialogPresenter.DataContextChanged += DialogPresenterDataContextChanged; this.Closed += DialogWindowClosed; } void DialogWindowClosed(object sender, EventArgs e) { this._isClosed = true; } private void DialogPresenterDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { var d = e.NewValue as IDialogResultVMHelper; if (d == null) return; d.RequestCloseDialog += new EventHandler<RequestCloseDialogEventArgs> (DialogResultTrueEvent).MakeWeak( eh => d.RequestCloseDialog -= eh;); } private void DialogResultTrueEvent(object sender, RequestCloseDialogEventArgs eventargs) { // Important: Do not set DialogResult for a closed window // GC clears windows anyways and with MakeWeak it // closes out with IDialogResultVMHelper if(_isClosed) return; this.DialogResult = eventargs.DialogResult; } 

现在至less我必须在我的资源文件( app.xaml或其他)中创build一个DataTemplate

 <DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" > <DialogView:EditOrNewAuswahlItem/> </DataTemplate> 

那么所有,我现在可以从我的viewmodels调用对话框:

  var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM); 

现在我的问题,你看到这个解决scheme的任何问题?

编辑:完整性。 ViewModel应该实现IDialogResultVMHelper ,然后它可以在OkCommand或类似的东西中引发它:

 public class MyViewmodel : IDialogResultVMHelper { private readonly Lazy<DelegateCommand> _okCommand; public MyViewmodel() { this._okCommand = new Lazy<DelegateCommand>(() => new DelegateCommand(() => InvokeRequestCloseDialog( new RequestCloseDialogEventArgs(true)), () => YourConditionsGoesHere = true)); } public ICommand OkCommand { get { return this._okCommand.Value; } } public event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog; private void InvokeRequestCloseDialog(RequestCloseDialogEventArgs e) { var handler = RequestCloseDialog; if (handler != null) handler(this, e); } } 

编辑2:我用这里的代码,使我的EventHandler注册弱:
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
(网站不再存在, WebArchive镜像 )

 public delegate void UnregisterCallback<TE>(EventHandler<TE> eventHandler) where TE : EventArgs; public interface IWeakEventHandler<TE> where TE : EventArgs { EventHandler<TE> Handler { get; } } public class WeakEventHandler<T, TE> : IWeakEventHandler<TE> where T : class where TE : EventArgs { private delegate void OpenEventHandler(T @this, object sender, TE e); private readonly WeakReference mTargetRef; private readonly OpenEventHandler mOpenHandler; private readonly EventHandler<TE> mHandler; private UnregisterCallback<TE> mUnregister; public WeakEventHandler(EventHandler<TE> eventHandler, UnregisterCallback<TE> unregister) { mTargetRef = new WeakReference(eventHandler.Target); mOpenHandler = (OpenEventHandler)Delegate.CreateDelegate( typeof(OpenEventHandler),null, eventHandler.Method); mHandler = Invoke; mUnregister = unregister; } public void Invoke(object sender, TE e) { T target = (T)mTargetRef.Target; if (target != null) mOpenHandler.Invoke(target, sender, e); else if (mUnregister != null) { mUnregister(mHandler); mUnregister = null; } } public EventHandler<TE> Handler { get { return mHandler; } } public static implicit operator EventHandler<TE>(WeakEventHandler<T, TE> weh) { return weh.mHandler; } } public static class EventHandlerUtils { public static EventHandler<TE> MakeWeak<TE>(this EventHandler<TE> eventHandler, UnregisterCallback<TE> unregister) where TE : EventArgs { if (eventHandler == null) throw new ArgumentNullException("eventHandler"); if (eventHandler.Method.IsStatic || eventHandler.Target == null) throw new ArgumentException("Only instance methods are supported.", "eventHandler"); var wehType = typeof(WeakEventHandler<,>).MakeGenericType( eventHandler.Method.DeclaringType, typeof(TE)); var wehConstructor = wehType.GetConstructor(new Type[] { typeof(EventHandler<TE>), typeof(UnregisterCallback<TE>) }); IWeakEventHandler<TE> weh = (IWeakEventHandler<TE>)wehConstructor.Invoke( new object[] { eventHandler, unregister }); return weh.Handler; } } 

这是一个很好的方法,过去我使用了类似的方法。 去吧!

我确实要做的一件小事是让事件在DialogResult中需要设置“false”时收到一个布尔值。

 event EventHandler<RequestCloseEventArgs> RequestCloseDialog; 

和EventArgs类:

 public class RequestCloseEventArgs : EventArgs { public RequestCloseEventArgs(bool dialogResult) { this.DialogResult = dialogResult; } public bool DialogResult { get; private set; } } 

我几个月来一直在使用几乎相同的方法,而且我对此感到非常满意(即,我还没有感觉到完全重写它的冲动…)

在我的实现中,我使用了一个IDialogViewModel ,它公开了一些东西,比如标题,展示的button(为了在所有的对话框中拥有一致的服从),一个RequestClose事件以及一些能够控制窗口的事情大小和行为

如果您正在讨论对话窗口而不仅仅是popup的消息框,请考虑下面的方法。 关键是:

  1. 我将Module Controller的引用传递给每个ViewModel的构造函数(可以使用注入)。
  2. Module Controller具有创build对话窗口的公共/内部方法(只是创build,没有返回结果)。 因此,在ViewModel打开一个对话窗口我写: controller.OpenDialogEntity(bla, bla...)
  3. 每个对话窗口通过弱事件通知其结果(如确定保存取消等)。 如果您使用PRISM,那么使用此EventAggregator发布通知更容易。
  4. 为了处理对话结果,我正在使用订阅通知(在PRISM的情况下再次弱事件和EventAggregator )。 要减less对这些通知的依赖,请使用具有标准通知的独立类。

优点:

  • 代码less 我不介意使用接口,但是我看到太多的项目,过度使用接口和抽象层导致更多的麻烦而不是帮助。
  • 通过Module Controller打开对话窗口是避免强引用的简单方法,仍然允许使用模型进行testing。
  • 通过弱事件通知减less潜在的内存泄漏的数量。

缺点:

  • 在处理程序中不容易区分所需的通知。 两种解决scheme
    • 在打开对话窗口时发送一个唯一的标记,并在订阅中检查该标记
    • 使用generics通知类<T>其中T是实体的枚举(或为简单起见,它可以是ViewModel的types)。
  • 对于一个项目应该是关于使用通知类来防止重复的一个协议。
  • 对于非常大的项目, Module Controller可能会被创build窗口的方法所淹没。 在这种情况下,最好把它分成几个模块。

PS我一直在使用这种方法已经有相当长的一段时间,并准备捍卫它的评论资格,并提供一些例子,如果需要的话。