WPF MVVM 对话框示例

Posted

技术标签:

【中文标题】WPF MVVM 对话框示例【英文标题】:WPF MVVM dialog example 【发布时间】:2010-12-12 16:17:17 【问题描述】:

有没有人有任何使用 MVVM (Prism) 显示窗口对话框的示例? - 例如执行命令时的配置设置窗口。

我见过的所有示例都使用中介者模式,这很好,但它们也都引用了视图模型中的视图,这并不理想(我们正在使用 DataTemplates)

谢谢

【问题讨论】:

【参考方案1】:

我会使用服务来显示对话框。该服务还可以将视图与视图模型链接。

public interface IDialogService 
    void RegisterView<TView, TViewModel>() where TViewModel:IDialogViewModel;
    bool? ShowDialog(IDialogViewModel viewModel);


public interface IDialogViewModel 
    bool CanClose();
    void Close();

RegisterView 只是将视图类型与 ViewModel 类型链接起来。您可以在模块初始化中设置这些链接。这比尝试让模块在应用程序的顶层注册数据模板更简单。

ShowDialog 显示您要显示的 ViewModel。就像 Window.ShowDialog 方法一样,它返回 true、false 和 null 来关闭。该实现只是从您的容器中创建一个 TView 类型的新视图,将其连接到提供的 ViewModel 并显示它。

IDialogViewModel 为 ViewModel 提供了一种机制来进行验证并取消对话框的关闭。

我有一个标准的对话窗口,里面有一个内容控件。当ShowDialog 被调用时,它会创建一个新的标准对话框,将视图添加到内容控件,连接 ViewModel 并显示它。标准对话框已经有 [OK] 和 [Cancel] 按钮,它们具有适当的逻辑,可以从 IDialogViewModel 调用正确的方法。

【讨论】:

嗨卡梅伦!视图如何通知服务是否按下了确定或取消? 标准方式。 ShowDialog 的代码实现只会找到它需要显示的视图,然后在该视图上调用ShowDialog,并将结果传回。 IDIAlogViewModel 中的属性更改如何触发对绑定到标准对话框上的 [OK] 和 [Cancel] 按钮的命令的 CanExecute 评估? InvalidateRequerySuggested?【参考方案2】:

我这样做的方式也是使用中介模式。当 ViewModel 想要显示一个对话框时,它会发送一条消息,该消息由应用程序的主窗口接收。该消息包含对话框使用的 ViewModel 实例。

然后主窗口构造对话框窗口的实例,将视图模型传递给它并显示对话框。对话的结果在原始消息中传回给调用者。

看起来像这样:

在您的视图模型中:

DialogViewModel viewModel = new DialogViewModel(...);
ShowDialogMessage message = new ShowDialogMessage(viewModel);

_messenger.Broadcast(message);

if (message.Result == true)

    ...

在主窗口代码隐藏中:

void RecieveShowDialogMessage(ShowDialogMessage message)

    DialogWindow w = new DialogWindow();
    w.DataContext = message.ViewModel;
    message.Result = w.ShowDialog();

我希望这足以给你这个想法......

【讨论】:

感谢您的回答。您能否澄清以下情况。假设您使用 Save 和 Cancel 命令在 DialogWindow 中显示视图模型。当用户单击视图上的保存(绑定到 SaveCommand)时,您可能希望运行一些验证并保存配置,只有当这成功时对话框才会关闭。我似乎无法理解 VM 将如何设置视图的 DialogResult(因此关闭对话框)。再次感谢。 有很多方法可以让 VM 与视图交互而不会破坏抽象。例如,对话框视图模型基类通常具有一些基本属性和方法,以使显示对话框更容易一些(我通常有一个对话框视图模型的接口,其中包括诸如在视图加载时运行的方法之类的东西,以及作为对话结果,提交/中止命令等)。让视图模型告诉视图关闭的一种简单方法是公开一个属性(例如 CanClose)。 然后在你的对话框上创建一个对应的依赖属性,当datacontext改变时设置两者之间的绑定——这样你就可以在你的绑定属性的改变事件处理程序中处理任何逻辑。再说一次,在对话框视图模型上简单地公开一个“关闭”事件会更容易,并且在对话框窗口数据上下文更改的处理程序中订阅此事件,处理程序可以分配适当的对话框结果并关闭窗口。【参考方案3】:

我同意,根据 MVVM 模式使用服务显示对话框是最简单的解决方案。但是,我也问自己,如果我的项目中有 3 个程序集 Model、ViewModel、View 和根据 MVVM 模式的程序集 ViewModel 有对 Model 的引用,而 View 对 Model 和 ViewModel 我应该在哪里放置 DialogService 类?如果我将在 ViewModel 程序集中放置一个 - 我没有机会创建 DialogView 实例;另一方面,如果我将 DialogService 放在 View 程序集中,我应该如何将它注入到我的 ViewModel 类中?

所以,我会推荐看看Advanced MVVM scenarios with Prism 部分:使用交互请求对象

作为这种方法的示例:

DialogViewModelBase

public abstract class DialogViewModelBase : ViewModelBase

    private ICommand _ok;

    public ICommand Ok
    
        get  return _ok ?? (_ok = new DelegateCommand(OkExecute, CanOkExecute)); 
    

    protected virtual bool CanOkExecute()
    
        return true;
    

    protected virtual void OkExecute()
    
        _isSaved = true;
        Close = true;
    

    private ICommand _cancel;

    public ICommand Cancel
    
        get 
        
           return _cancel ?? (_cancel = new DelegateCommand(CancelExecute, CanCancelExecute));
        
    

    protected virtual bool CanCancelExecute()
    
        return true;
    

    protected virtual void CancelExecute()
    
        Close = true;
    

    private bool _isSaved = false;
    public bool IsSaved
    
        get  return _isSaved; 
    

    private bool _close = false;

    public bool Close
    
        get  return _close; 
        set
        
            _close = value;
            RaisePropertyChanged(() => Close);
        
    

创建用户故事视图模型:

public class CreateUserStoryViewModel : DialogViewModelBase

    private string _name = String.Empty;

    public string Name
    
        get  return _name; 
        set
        
            _name = value;
            RaisePropertyChanged(() => Name);
        
    

创建用户故事请求

private InteractionRequest<Notification> _createUserStoryRequest;
public InteractionRequest<Notification> CreateUserStoryRequest

    get
    
        return _createUserStoryRequest ?? (_createUserStoryRequest = new InteractionRequest<Notification>());
    

CreateUserStory 命令

private void CreateUserStoryExecute()

    CreateUserStoryRequest.Raise(new Notification()
    
        Content = new CreateUserStoryViewModel(),
        Title = "Create User Story"
    , 
    notification =>
                 
                      CreateUserStoryViewModel createUserStoryViewModel =
                               (CreateUserStoryViewModel)notification.Content;
                      if (createUserStoryViewModel.IsSaved)
                      
                         _domainContext.CreateUserStory(
new UserStory() Name = createUserStoryViewModel.Name, );
                      
                 );

XAML:

<!--where xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
          xmlns:ir="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity"-->

<i:Interaction.Triggers>
  <ir:InteractionRequestTrigger SourceObject="Binding CreateUserStoryRequest">
    <ir:PopupChildWindowAction>
      <ir:PopupChildWindowAction.ChildWindow>
        <view:CreateUserStory />
      </ir:PopupChildWindowAction.ChildWindow>
    </ir:PopupChildWindowAction>
  </ir:InteractionRequestTrigger>
</i:Interaction.Triggers>

【讨论】:

Vladimir:我已经尝试过这种方法,但它对我不起作用。你能详细说明什么是 _domainContext。我的场景是打开一个模式对话框,其中有一个文本框、标签和 pdfviewer。我必须更改 ModalDialog 的数据上下文 [即。根据按钮(触发器)单击将这些值作为视图模型传递]。任何想法或参考都会有所帮助...谢谢 _domainContext 在我的示例中是一种存储库。这只是示例,您可以将其替换为 WCF 服务调用或保存到磁盘。此示例适用于您的方案。您需要将 CreateUserStoryViewModel 替换为您自己的 ModalDialogViewModel 属性 Title、Text 等,并将这些属性绑定到您的 ModalDialogView 的 Label 和 PdfViewer(应使用 ModalDialogView 而不是我的 CreateUserStory 视图)。 这似乎不适用于 WPF。 元素不存在。但它在 Silverlight 中确实如此。 @GarethOates,你是对的。请看这个***.com/questions/18596168/…【参考方案4】:

据我了解您在上面的评论,问题不在于显示对话框,而在于隐藏它们。有两种方法可以解决这个问题:

    使用标准对话窗口来实现视图。这需要在 View 和 ViewModel 之间有一种松散耦合的通信方式,以便 ViewModel 可以通知 View 可以在不引用视图的情况下关闭。

    存在多个允许这样做的框架 - Prism 的事件聚合器就是其中之一。在这种情况下,View 将订阅一个事件(例如,MyDialogResultValidated),并且在接收到该事件时,它将相应地设置 DialogResult。如果验证成功,ViewModel(在其 SaveCommand 中)将触发该事件。

    不要使用标准的对话窗口来实现视图。这需要有一个可以有效模拟模态的叠加层。

    在这种情况下,视图和叠加层的可见性将绑定 ViewModel 的 IsVisible 属性,该属性将由 SaveCommand 实现相应地设置,或者在 ViewModel 需要显示视图时进行设置。

第一种方法需要在代码隐藏中添加一些代码,需要添加全局事件,并且(可以说)更少 MVVM-ish。第二种方法需要实现(或使用其他人的实现)覆盖,但不需要在代码隐藏中包含任何代码,不需要全局事件,并且(有争议的)更 MVVM-ish .

【讨论】:

【参考方案5】:

您可能对以下示例应用程序感兴趣:

http://compositeextensions.codeplex.com

它将 Prism2 与 PresentationModel(又名 MVVM)模式一起使用。示例应用程序包含一个模式对话框。

【讨论】:

【参考方案6】:

这不是 Prism,但这个 MVVM demo 有一个完全是 MVVM 的选项对话框。

【讨论】:

以上是关于WPF MVVM 对话框示例的主要内容,如果未能解决你的问题,请参考以下文章

使用 Prism 和 MVVM 模式在 WPF 中创建模态对话框的“漂亮”方式

对于对话框是好还是不好的做法在WPF与MVVM

如何从作为wpf mvvm模式中的窗口打开的视图模型中关闭用户控件?

使用 MVVM 在 wpf 中使用 Dialogs 的好做法还是坏做法?

使用 MVVM 在 WPF 中创建新窗口的最佳方法

WPF MVVM 从 ViewModel 触发事件的正确方法