对于对话框是好还是不好的做法在WPF与MVVM? [英] Good or bad practice for Dialogs in wpf with MVVM?

查看:547
本文介绍了对于对话框是好还是不好的做法在WPF与MVVM?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近作了创建添加和编辑对话框我WPF应用程序的问题。

我只想在我的code做的就是这样的事情。 (我主要使用与MVVM视图模型第一种方法)

视图模型这就要求一个对话窗口:

  VAR的结果= this.uiDialogService.ShowDialog(Dialogwindow名称在这儿,dialogwindowVM); ...做的对话框结果什么...

它是如何工作的?

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

 公共接口IUIWindowDialogService
{
    布尔?的ShowDialog(字符串名称,对象的datacontext);
}公共类WpfUIWindowDialogService:IUIWindowDialogService
{
    公共BOOL?的ShowDialog(字符串名称,对象的datacontext)
    {
        VAR赢=新WindowDialog();
        win.Title =称号;
        win.DataContext = DataContext的;        返回win.ShowDialog();
    }}

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

 <窗​​口x:类=WindowDialog
    的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/$p$psentation
    标题=WindowDialog
    WindowStyle =SingleBorderWindow
    WindowStartupLocation =CenterOwnerSizeToContent =WidthAndHeight>
    <内容presenter X:NAME =对话框presenterCONTENT ={结合}>    < /内容presenter>
< /窗GT;

在WPF对话的一个问题是的DialogResult = TRUE 。这只能在code来实现。这就是为什么我创造了我的 dialogviewmodel 来实现一个接口。

 公共类RequestCloseDialogEventArgs:EventArgs的
{
    公共RequestCloseDialogEventArgs(布尔的DialogResult)
    {
        this.DialogResult =的DialogResult;
    }    公共BOOL的DialogResult
    {
        得到;组;
    }
}公共接口IDialogResultVMHelper
{
    事件的EventHandler< RequestCloseDialogEventArgs> RequestCloseDialog;
}

每当我的视图模型认为它的时间的DialogResult = true,则引发此事件。

 公共部分类DialogWindow:窗口
{
    // Merken WENN窗口geschlossen wurde,damit kein的DialogResult梅尔gesetzt wird
    私人布尔_isClosed = FALSE;    公共DialogWindow()
    {
        的InitializeComponent();
        this.Dialog presenter.DataContextChanged + =对话框presenterDataContextChanged;
        this.Closed + = DialogWindowClosed;
    }    无效DialogWindowClosed(对象发件人,EventArgs的发送)
    {
        this._isClosed = TRUE;
    }    私人无效对话presenterDataContextChanged(对象发件人,DependencyPropertyChangedEventArgs E)
    {
        变种D = e.NewValue为IDialogResultVMHelper;        如果(D == NULL)
            返回;        d.RequestCloseDialog + =新的EventHandler< RequestCloseDialogEventArgs>(DialogResultTrueEvent).MakeWeak(EH = GT; d.RequestCloseDialog - = EH); ;
    }    私人无效DialogResultTrueEvent(对象发件人,EventArgs的RequestCloseDialogEventArgs)
    {
        //重要事项damit献给EIN geschlossenes窗口kein的DialogResult梅尔gesetzt wird
        // GCräumt窗口irgendwann WEG UND durch MakeWeak fliegt ES奥赫BEIM IDialogResultVMHelper的RAU
        如果(_isClosed)回报;        this.DialogResult = eventargs.DialogResult;
    }

现在我至少要创建一个的DataTemplate 在我的资源文件(的App.xaml 或东西):

 <数据类型的DataTemplate ={X:类型DialogViewModel:EditOrNewAuswahlItemVM}>
        < D​​ialogView:EditOrNewAuswahlItem />
< / DataTemplate中>

好了多数民众赞成,我现在可以叫对话从我的ViewModels:

  VAR的结果= this.uiDialogService.ShowDialog(Dialogwindow名称在这儿,dialogwindowVM);

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

编辑:完整。该视图模型应该实现IDialogResultVMHelper,然后可以在OkCommand或像这样

中提出来

 公共类MyViewmodel:IDialogResultVMHelper
{
    私人只读懒< D​​elegateCommand> _okCommand;    公共MyViewmodel()
    {
         this._okCommand =新懒人< D​​elegateCommand>(()=>新建DelegateCommand(()=> InvokeRequestCloseDialog(新RequestCloseDialogEventArgs(真)),()=> YourConditionsGoesHere = TRUE));
    }    公众的ICommand OkCommand
    {
        {返回this._okCommand.Value; }
    }    公共事件的EventHandler< RequestCloseDialogEventArgs> RequestCloseDialog;    私人无效InvokeRequestCloseDialog(RequestCloseDialogEventArgs E)
    {
        VAR处理器= RequestCloseDialog;
        如果(处理!= NULL)
            处理程序(这一点,E);
    }
 }

编辑2:我用的是code从<一个href=\"http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx\">http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx让我的事件处理程序注册薄弱。

 公共委托无效UnregisterCallback&LT;&TE GT;(事件处理程序和LT; TE&GT;事件处理程序)
    其中,TE:EventArgs的;公共接口IWeakEventHandler&LT;&TE GT;
    其中,TE:EventArgs的
{
    事件处理&LT;&TE GT;处理程序{搞定; }
}公共类WeakEventHandler&LT; T,TE&GT; :IWeakEventHandler&LT;&TE GT;
    其中T:类
    其中,TE:EventArgs的
{
    私人委托无效OpenEventHandler(T @this,对象发件人,TE E);    私人只读了WeakReference mTargetRef;
    私人只读OpenEventHandler mOpenHandler;
    私人只读事件处理程序和LT; TE&GT; mHandler;
    私人UnregisterCallback&LT;&TE GT; mUnregister;    公共WeakEventHandler(事件处理程序和LT; TE&GT;事件处理程序,UnregisterCallback&LT;&TE GT;注销)
    {
        mTargetRef =新的WeakReference(eventHandler.Target);        mOpenHandler =(OpenEventHandler)Delegate.CreateDelegate(typeof运算(OpenEventHandler),空,eventHandler.Method);        mHandler =调用;
        mUnregister =注销;
    }    公共无效调用(对象发件人,TE E)
    {
        t定向=(T)mTargetRef.Target;        如果(目标!= NULL)
            mOpenHandler.Invoke(目标,发件人,E);
        否则,如果(mUnregister!= NULL)
        {
            mUnregister(mHandler);
            mUnregister = NULL;
        }
    }    公共事件处理程序和LT; TE&GT;处理器
    {
        {返回mHandler; }
    }    公共静态隐运营商的EventHandler&LT;&TE GT;(WeakEventHandler&LT; T,TE&GT; WEH)
    {
        返回weh.mHandler;
    }
}公共静态类EventHandlerUtils
{
    公共静态事件处理程序和LT; TE&GT; MakeWeak&LT;&TE GT;(此事件处理程序和LT; TE&GT;事件处理程序,UnregisterCallback&LT;&TE GT;注销)
      其中,TE:EventArgs的
    {
        如果(事件处理程序== NULL)
            抛出新的ArgumentNullException(事件处理程序);        如果(eventHandler.Method.IsStatic || eventHandler.Target == NULL)
            抛出新的ArgumentException(只有实例方法都支持。,事件处理程序);        VAR wehType = typeof运算(WeakEventHandler&LT;,&GT;)。MakeGenericType(eventHandler.Method.DeclaringType的typeof(TE));        VAR wehConstructor = wehType.GetConstructor(新类型[] {typeof运算(事件处理程序和LT; TE&GT;)的typeof(UnregisterCallback&LT;&TE GT;)});        IWeakEventHandler&LT;&TE GT; WEH =(IWeakEventHandler&LT;&TE GT;)wehConstructor.Invoke(新的对象[] {事件处理程序,注销});        返回weh.Handler;
    }
}


解决方案

这是一个很好的方法,我在过去使用类似的。加油!

一个次要的事情我肯定会做的是使事件得到当你需要在DialogResult的设置假的布尔。

 事件事件处理程序和LT; RequestCloseEventArgs&GT; RequestCloseDialog;

和EventArgs类:

 公共类RequestCloseEventArgs:EventArgs的
{
    公共RequestCloseEventArgs(布尔的DialogResult)
    {
        this.DialogResult =的DialogResult;
    }    公共BOOL的DialogResult {搞定;私人集; }
}

I lately had the problem of creating add and edit dialogs for my wpf app.

All I want to do in my code was something like this. (I mostly use viewmodel first approach with mvvm)

ViewModel which calls a dialog window:

 var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

 ... do anything with the dialog result...

How does it work?

First, I created a dialog service:

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 is a special but simple window. I need it to hold my content:

<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>

A problem with dialogs in wpf is the dialogresult = true. This can only be achieved in code. That's why I created an interface for my dialogviewmodel to implement.

public class RequestCloseDialogEventArgs : EventArgs
{
    public RequestCloseDialogEventArgs(bool dialogresult)
    {
        this.DialogResult = dialogresult;
    }

    public bool DialogResult
    {
        get; set;
    }
}

public interface IDialogResultVMHelper
{
    event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
}

Whenever my viewmodel think its time for dialogresult=true, then raise this event.

public partial class DialogWindow : Window
{
    //Merken wenn Window geschlossen wurde, damit kein DialogResult mehr gesetzt wird
    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)
    {
        //Wichtig damit für ein geschlossenes Window kein DialogResult mehr gesetzt wird
        //GC räumt Window irgendwann weg und durch MakeWeak fliegt es auch beim IDialogResultVMHelper raus
        if(_isClosed) return;

        this.DialogResult = eventargs.DialogResult;
    }

Now at least I have to create a DataTemplate in my resource file(app.xaml or something):

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

Well thats all, I can now call dialogs from my viewmodels:

 var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

Now my question, do you see any problems with this solution?

Edit: for completeness. the viewmodel should implement IDialogResultVMHelper and then can raise it within a OkCommand or something like this

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);
    }
 }

EDIT 2: i use the code from http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx for making my Eventhandler register weak.

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;
    }
}

解决方案

This is a good approach and I used similar ones in the past. Go for it!

One minor thing I'd definitely do is make the event receive a boolean for when you need to set "false" in the DialogResult.

event EventHandler<RequestCloseEventArgs> RequestCloseDialog;

and the EventArgs class:

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

    public bool DialogResult { get; private set; }
}

这篇关于对于对话框是好还是不好的做法在WPF与MVVM?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆