247

在 WPF 的 MVVM 模式中,处理对话框是更复杂的操作之一。由于您的视图模型对视图一无所知,因此对话通信可能很有趣。我可以公开一个ICommand,当视图调用它时,会出现一个对话框。

有谁知道处理对话结果的好方法?我说的是 Windows 对话框,例如MessageBox.

我们这样做的一种方法是在视图模型上设置一个事件,当需要对话框时视图将订阅该事件。

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

这没关系,但这意味着视图需要代码,这是我想远离的。

4

23 回答 23

132

我建议放弃 1990 年代的模态对话框,而是将控件实现为覆盖(画布+绝对定位),其可见性与 VM 中的布尔值相关联。更接近 ajax 类型的控件。

这非常有用:

<BooleanToVisibilityConverter x:Key="booltoVis" />

如:

<my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>

这是我如何将一个实现为用户控件的方法。点击'x'会在usercontrol的代码后面的一行代码中关闭控件。(因为我的视图在 .exe 中,视图模型在 dll 中,所以我对操纵 UI 的代码并不感到难过。)

Wpf 对话框

于 2009-05-08T16:36:40.773 回答
54

编辑: 10 多年后,我可以说使用调解器或任何其他消息传递模式在很多层面上都是一个非常糟糕的主意。不要这样做,只需实现 Jeffrey 的答案或在您的视图模型中注入的 IDialogService 即可。


您应该为此使用调解器。Mediator 是一种常见的设计模式,在其某些实现中也称为Messenger。它是 Register/Notify 类型的范例,使您的 ViewModel 和 Views 能够通过低耦合消息传递机制进行通信。

您应该查看 google WPF Disciples 组,然后搜索 Mediator。你会很高兴得到答案...

但是,您可以从以下开始:

http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/

享受 !

编辑:您可以在此处使用 MVVM Light Toolkit 看到此问题的答案:

http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338

于 2009-04-17T10:06:28.567 回答
35

一个好的 MVVM 对话框应该:

  1. 仅使用 XAML 声明。
  2. 从数据绑定中获取所有行为。

不幸的是,WPF 不提供这些功能。显示对话框需要对ShowDialog(). 支持对话框的 Window 类不能在 XAML 中声明,因此不能轻易将数据绑定到DataContext.

为了解决这个问题,我编写了一个 XAML 存根控件,它位于逻辑树中并将数据绑定中继到 aWindow并处理显示和隐藏对话框。你可以在这里找到它:http: //www.codeproject.com/KB/WPF/XAMLDialog.aspx

它使用起来非常简单,不需要对您的 ViewModel 进行任何奇怪的更改,也不需要事件或消息。基本调用如下所示:

<dialog:Dialog Content="{Binding Path=DialogViewModel}" Showing="True" />

您可能想要添加一个设置Showing. 我在我的文章中解释它。我希望这可以帮助你。

于 2009-04-18T16:41:19.110 回答
25

我将这种方法用于与 MVVM 的对话。

我现在要做的就是从我的视图模型中调用以下内容。

var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
于 2011-02-10T11:01:10.097 回答
16

我当前的解决方案解决了您提到的大多数问题,但它完全从特定于平台的事物中抽象出来并且可以重用。此外,我没有使用任何代码隐藏,仅与实现 ICommand 的 DelegateCommands 绑定。Dialog 基本上是一个 View - 一个单独的控件,它有自己的 ViewModel,它从主屏幕的 ViewModel 中显示,但通过 DelagateCommand 绑定从 UI 触发。

在此处查看完整的 Silverlight 4 解决方案Modal dialogs with MVVM and Silverlight 4

于 2010-01-21T21:14:17.257 回答
6

在学习(仍在学习)MVVM 时,我真的为这个概念苦苦挣扎了一段时间。我决定的,以及我认为其他人已经决定但我不清楚的是:

我最初的想法是不应该允许 ViewModel 直接调用对话框,因为它没有决定对话框应该如何显示的业务。正因为如此,我开始思考如何像在 MVP 中那样传递消息(即 View.ShowSaveFileDialog())。但是,我认为这是错误的做法。

ViewModel 可以直接调用对话框。但是,当您测试 ViewModel 时,这意味着对话框将在您的测试期间弹出,或者一起失败(从未真正尝试过)。

因此,需要发生的事情是在测试时使用对话的“测试”版本。这意味着对于您拥有的任何对话,您都需要创建一个接口并模拟对话响应或创建一个具有默认行为的测试模拟。

您应该已经在使用某种服务定位器或 IoC,您可以对其进行配置,以根据上下文为您提供正确的版本。

使用这种方法,您的 ViewModel 仍然是可测试的,并且根据您模拟对话框的方式,您可以控制行为。

希望这可以帮助。

于 2010-03-04T00:28:28.493 回答
6

有两种很好的方法可以做到这一点,1)对话服务(简单,干净),2)视图辅助。视图辅助提供了一些简洁的功能,但通常不值得。

对话服务

a)一个对话服务接口,比如通过构造函数或一些依赖容器:

interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }

b) 您的 IDialogService 实现应该打开一个窗口(或将一些控件注入活动窗口),创建一个与给定 dlgVm 类型名称对应的视图(使用容器注册或约定或具有类型关联 DataTemplates 的 ContentPresenter)。ShowDialogAsync 应该创建一个 TaskCompletionSource 并返回其 .Task 属性。DialogViewModel 类本身需要一个事件,您可以在要关闭时在派生类中调用,并在对话框视图中观察以实际关闭/隐藏对话框并完成 TaskCompletionSource。

b) 要使用,只需在您的某个 DialogViewModel 派生类的实例上调用 await this.DialogService.ShowDialog(myDlgVm)。在 await 返回后,查看您在对话 VM 上添加的属性以确定发生了什么;你甚至不需要回调。

查看辅助

这让您的视图监听视图模型上的事件。如果您愿意的话,这一切都可以包含在一个混合行为中,以避免代码落后和资源使用(FMI,将“行为”类子类化以查看类固醇上的一种可混合附加属性)。现在,我们将在每个视图上手动执行此操作:

a) 使用自定义负载(DialogViewModel 派生类)创建一个 OpenXXXXXDialogEvent。

b) 让视图在其 OnDataContextChanged 事件中订阅该事件。如果旧值 != null 并且在 Window 的 Unloaded 事件中,请务必隐藏并取消订阅。

c) 当事件触发时,让视图打开您的视图,该视图可能位于页面上的资源中,或者您可以按照惯例在其他地方找到它(例如在对话服务方法中)。

这种方法更灵活,但需要更多的工作才能使用。我用的不多。例如,一个很好的优势是能够将视图物理放置在选项卡中。我使用了一种算法将其放置在当前用户控件的边界中,或者如果不够大,则向上遍历可视化树,直到找到足够大的容器。

这允许对话框靠近它们实际使用的地方,只使应用程序中与当前活动相关的部分变暗,并让用户在应用程序内移动而无需手动将对话框推开,甚至有多个准模态对话框在不同的选项卡或子视图上打开。

于 2015-03-21T09:25:10.520 回答
5

使用可冻结命令

<Grid>
        <Grid.DataContext>
            <WpfApplication1:ViewModel />
        </Grid.DataContext>


        <Button Content="Text">
            <Button.Command>
                <WpfApplication1:MessageBoxCommand YesCommand="{Binding MyViewModelCommand}" />
            </Button.Command>
        </Button>

</Grid>
public class MessageBoxCommand : Freezable, ICommand
{
    public static readonly DependencyProperty YesCommandProperty = DependencyProperty.Register(
        "YesCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty OKCommandProperty = DependencyProperty.Register(
        "OKCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register(
        "CancelCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty NoCommandProperty = DependencyProperty.Register(
        "NoCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
        "Message",
        typeof (string),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata("")
        );

    public static readonly DependencyProperty MessageBoxButtonsProperty = DependencyProperty.Register(
        "MessageBoxButtons",
        typeof(MessageBoxButton),
        typeof(MessageBoxCommand),
        new FrameworkPropertyMetadata(MessageBoxButton.OKCancel)
        );

    public ICommand YesCommand
    {
        get { return (ICommand) GetValue(YesCommandProperty); }
        set { SetValue(YesCommandProperty, value); }
    }

    public ICommand OKCommand
    {
        get { return (ICommand) GetValue(OKCommandProperty); }
        set { SetValue(OKCommandProperty, value); }
    }

    public ICommand CancelCommand
    {
        get { return (ICommand) GetValue(CancelCommandProperty); }
        set { SetValue(CancelCommandProperty, value); }
    }

    public ICommand NoCommand
    {
        get { return (ICommand) GetValue(NoCommandProperty); }
        set { SetValue(NoCommandProperty, value); }
    }

    public MessageBoxButton MessageBoxButtons
    {
        get { return (MessageBoxButton)GetValue(MessageBoxButtonsProperty); }
        set { SetValue(MessageBoxButtonsProperty, value); }
    }

    public string Message
    {
        get { return (string) GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    public void Execute(object parameter)
    {
        var messageBoxResult = MessageBox.Show(Message);
        switch (messageBoxResult)
        {
            case MessageBoxResult.OK:
                OKCommand.Execute(null);
                break;
            case MessageBoxResult.Yes:
                YesCommand.Execute(null);
                break;
            case MessageBoxResult.No:
                NoCommand.Execute(null);
                break;
            case MessageBoxResult.Cancel:
                if (CancelCommand != null) CancelCommand.Execute(null); //Cancel usually means do nothing ,so can be null
                break;

        }
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;


    protected override Freezable CreateInstanceCore()
    {
        throw new NotImplementedException();
    }
}
于 2011-11-17T18:16:02.287 回答
3

我认为对话框的处理应该是视图的责任,视图需要有代码来支持它。

如果您更改 ViewModel - View 交互以处理对话框,则 ViewModel 依赖于该实现。处理这个问题最简单的方法是让 View 负责执行任务。如果这意味着显示一个对话框,那么很好,但也可能是状态栏中的状态消息等。

我的观点是,MVVM 模式的重点是将业务逻辑与 GUI 分离,因此您不应该在业务层(ViewModel)中混合 GUI 逻辑(以显示对话框)。

于 2009-01-18T09:36:34.407 回答
3

一个有趣的替代方法是使用负责显示视图(对话框)的控制器。

WPF 应用程序框架 (WAF)展示了其工作原理。

于 2010-03-21T14:25:17.007 回答
3

为什么不在 VM 中引发事件并订阅视图中的事件呢?这将使应用程序逻辑和视图保持分离,并且仍然允许您将子窗口用于对话框。

于 2011-01-29T18:25:04.853 回答
3

我已经实现了一个监听来自 ViewModel 的消息的行为。它基于 Laurent Bugnion 解决方案,但由于它不使用后面的代码并且更可重用,我认为它更优雅。

如何使 WPF 的行为就像开箱即用地支持 MVVM

于 2011-02-06T17:23:16.810 回答
2

我认为视图可以有代码来处理来自视图模型的事件。

根据事件/场景,它还可以有一个订阅视图模型事件的事件触发器,以及一个或多个作为响应调用的操作。

于 2009-04-24T01:39:29.323 回答
2

我有同样的情况,并将 MessageBox 包装成一个设计器不可见的控件。详情在我的博客

http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

同样可以扩展到任何模式对话框、文件浏览控件等。

于 2010-03-11T21:30:48.540 回答
2

标准方法

在 WPF 中处理这个问题多年后,我终于找到了在 WPF 中实现对话框的标准方法。以下是这种方法的优点:

  1. 干净的
  2. 不违反 MVVM 设计模式
  3. ViewModal 从不引用任何 UI 库(WindowBase、PresentationFramework 等)
  4. 非常适合自动化测试
  5. 对话框可以轻松更换。

那么关键是什么。它是DI + IoC

下面是它的工作原理。我正在使用 MVVM Light,但这种方法也可以扩展到其他框架:

  1. 将 WPF 应用程序项目添加到您的解决方案。称之为应用程序
  2. 添加一个 ViewModal 类库。称之为VM
  3. 应用引用 VM 项目。VM项目对App一无所知。
  4. 将 NuGet 对 MVVM Light 的引用添加到两个项目中。这些天我正在使用MVVM Light Standard,但你也可以使用完整的框架版本。
  5. 在 VM 项目中添加一个接口IDialogService :

    public interface IDialogService
    {
      void ShowMessage(string msg, bool isError);
      bool AskBooleanQuestion(string msg);
      string AskStringQuestion(string msg, string default_value);
    
      string ShowOpen(string filter, string initDir = "", string title = "");
      string ShowSave(string filter, string initDir = "", string title = "", string fileName = "");
      string ShowFolder(string initDir = "");
    
      bool ShowSettings();
    }
    
  6. IDialogService在您的 中公开一个类型的公共静态属性ViewModelLocator,但将注册部分留给 View 层执行。这是关键。:

    public static IDialogService DialogService => SimpleIoc.Default.GetInstance<IDialogService>();
    
  7. 在 App 项目中添加此接口的实现。

    public class DialogPresenter : IDialogService
    {
        private static OpenFileDialog dlgOpen = new OpenFileDialog();
        private static SaveFileDialog dlgSave = new SaveFileDialog();
        private static FolderBrowserDialog dlgFolder = new FolderBrowserDialog();
    
        /// <summary>
        /// Displays a simple Information or Error message to the user.
        /// </summary>
        /// <param name="msg">String text that is to be displayed in the MessageBox</param>
        /// <param name="isError">If true, Error icon is displayed. If false, Information icon is displayed.</param>
        public void ShowMessage(string msg, bool isError)
        {
                if(isError)
                        System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.OK, MessageBoxImage.Error);
                else
                        System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.OK, MessageBoxImage.Information);
        }
    
        /// <summary>
        /// Displays a Yes/No MessageBox.Returns true if user clicks Yes, otherwise false.
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public bool AskBooleanQuestion(string msg)
        {
                var Result = System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
                return Result;
        }
    
        /// <summary>
        /// Displays Save dialog. User can specify file filter, initial directory and dialog title. Returns full path of the selected file if
        /// user clicks Save button. Returns null if user clicks Cancel button.
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="initDir"></param>
        /// <param name="title"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public string ShowSave(string filter, string initDir = "", string title = "", string fileName = "")
        {
                if (!string.IsNullOrEmpty(title))
                        dlgSave.Title = title;
                else
                        dlgSave.Title = "Save";
    
                if (!string.IsNullOrEmpty(fileName))
                        dlgSave.FileName = fileName;
                else
                        dlgSave.FileName = "";
    
                dlgSave.Filter = filter;
                if (!string.IsNullOrEmpty(initDir))
                        dlgSave.InitialDirectory = initDir;
    
                if (dlgSave.ShowDialog() == DialogResult.OK)
                        return dlgSave.FileName;
                else
                        return null;
        }
    
    
        public string ShowFolder(string initDir = "")
        {
                if (!string.IsNullOrEmpty(initDir))
                        dlgFolder.SelectedPath = initDir;
    
                if (dlgFolder.ShowDialog() == DialogResult.OK)
                        return dlgFolder.SelectedPath;
                else
                        return null;
        }
    
    
        /// <summary>
        /// Displays Open dialog. User can specify file filter, initial directory and dialog title. Returns full path of the selected file if
        /// user clicks Open button. Returns null if user clicks Cancel button.
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="initDir"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        public string ShowOpen(string filter, string initDir = "", string title = "")
        {
                if (!string.IsNullOrEmpty(title))
                        dlgOpen.Title = title;
                else
                        dlgOpen.Title = "Open";
    
                dlgOpen.Multiselect = false;
                dlgOpen.Filter = filter;
                if (!string.IsNullOrEmpty(initDir))
                        dlgOpen.InitialDirectory = initDir;
    
                if (dlgOpen.ShowDialog() == DialogResult.OK)
                        return dlgOpen.FileName;
                else
                        return null;
        }
    
        /// <summary>
        /// Shows Settings dialog.
        /// </summary>
        /// <returns>true if User clicks OK button, otherwise false.</returns>
        public bool ShowSettings()
        {
                var w = new SettingsWindow();
                MakeChild(w); //Show this dialog as child of Microsoft Word window.
                var Result = w.ShowDialog().Value;
                return Result;
        }
    
        /// <summary>
        /// Prompts user for a single value input. First parameter specifies the message to be displayed in the dialog 
        /// and the second string specifies the default value to be displayed in the input box.
        /// </summary>
        /// <param name="m"></param>
        public string AskStringQuestion(string msg, string default_value)
        {
                string Result = null;
    
                InputBox w = new InputBox();
                MakeChild(w);
                if (w.ShowDialog(msg, default_value).Value)
                        Result = w.Value;
    
                return Result;
        }
    
        /// <summary>
        /// Sets Word window as parent of the specified window.
        /// </summary>
        /// <param name="w"></param>
        private static void MakeChild(System.Windows.Window w)
        {
                IntPtr HWND = Process.GetCurrentProcess().MainWindowHandle;
                var helper = new WindowInteropHelper(w) { Owner = HWND };
        }
    }
    
  8. 虽然其中一些功能是通用的(ShowMessageAskBooleanQuestion),但其他一些特定于该项目并使用 custom Window。您可以以相同的方式添加更多自定义窗口。关键是将特定于 UI 的元素保留在 View 层中,并在 VM 层中使用 POCO 公开返回的数据
  9. 使用此类在视图层中执行 IoC 注册您的界面。您可以在主视图的构造函数中执行此操作(InitializeComponent()调用后):

    SimpleIoc.Default.Register<IDialogService, DialogPresenter>();
    
  10. 你去吧。您现在可以在 VM 和 View 层访问所有对话功能。您的 VM 层可以像这样调用这些函数:

    var NoTrump = ViewModelLocator.DialogService.AskBooleanQuestion("Really stop the trade war???", "");
    
  11. 这么干净,你看。VM 层不知道 UI 层将如何向用户呈现 Yes/No 问题,并且仍然可以成功地使用对话框返回的结果。

其他免费福利

  1. 为了编写单元测试,您可以在您的测试项目中提供一个自定义实现,IDialogService并在您的测试类的构造函数中将该类注册到 IoC。
  2. 您需要导入一些命名空间,例如Microsoft.Win32访问“打开”和“保存”对话框。我将它们排除在外是因为这些对话框还有一个 WinForms 版本可用,而且有人可能想要创建自己的版本。另请注意,其中使用的一些标识符DialogPresenter是我自己的窗口的名称(例如SettingsWindow)。您需要从界面和实现中删除它们,或者提供您自己的窗口。
  3. 如果您的 VM 执行多线程,请DispatcherHelper.Initialize()在应用程序生命周期的早期调用 MVVM Light。
  4. 除了DialogPresenter在 View 层中注入的,其他的 ViewModal 应该被注册,ViewModelLocator然后应该公开该类型的公共静态属性以供 View 层使用。像这样的东西:

    public static SettingsVM Settings => SimpleIoc.Default.GetInstance<SettingsVM>();
    
  5. 在大多数情况下,你的对话框不应该有任何代码隐藏,比如绑定或设置 DataContext 等。你甚至不应该将东西作为构造函数参数传递。XAML 可以为您完成这一切,如下所示:

    <Window x:Class="YourViewNamespace.SettingsWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:local="clr-namespace:YourViewProject"
      xmlns:vm="clr-namespace:YourVMProject;assembly=YourVMProject"
      DataContext="{x:Static vm:ViewModelLocator.Settings}"
      d:DataContext="{d:DesignInstance Type=vm:SettingsVM}" />
    
  6. 以这种方式设置DataContext可为您提供各种设计时优势,例如智能感知和自动完成。

希望对大家有所帮助。

于 2019-06-28T07:20:02.970 回答
1

我滚动了我自己的窗口加载器,在这个问题的答案中描述:

在应用程序中管理多个 WPF 视图

于 2010-04-28T01:33:09.200 回答
1

Karl Shifflett 创建了一个示例应用程序,用于使用服务方法和 Prism InteractionRequest 方法显示对话框。

我喜欢服务方法 - 它不太灵活,因此用户不太可能破坏某些东西:) 它也与我的应用程序的 WinForms 部分一致 (MessageBox.Show) 但是如果您打算显示很多不同的对话框,那么 InteractionRequest 是一个更好的方式去。

http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/

于 2011-12-12T17:28:47.890 回答
1

我知道这是一个老问题,但是当我进行这个搜索时,我发现了很多相关的问题,但我没有找到一个非常明确的答案。所以我自己实现了一个对话框/消息框/popin,我分享它!
我认为它是“MVVM 证明”,我尽量让它简单和适当,但我是 WPF 新手,所以请随时发表评论,甚至提出拉取请求。

https://github.com/Plasma-Paris/Plasma.WpfUtils

你可以像这样使用它:

public RelayCommand YesNoMessageBoxCommand { get; private set; }
async void YesNoMessageBox()
{
    var result = await _Service.ShowMessage("This is the content of the message box", "This is the title", System.Windows.MessageBoxButton.YesNo);
    if (result == System.Windows.MessageBoxResult.Yes)
        // [...]
}

如果你想要更复杂的 popin ,或者像这样:

var result = await _Service.ShowCustomMessageBox(new MyMessageBoxViewModel { /* What you want */ });

它显示了这样的事情:

2

于 2016-10-19T15:30:28.017 回答
1

我已经写了一篇关于这个主题的相当全面的文章,并且还为 MVVM Dialogs 开发了一个弹出库。严格遵守 MVVM 不仅是可能的,而且在正确实施时非常干净,并且可以轻松扩展到本身不遵守它的第三方库:

https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM

于 2018-04-05T11:12:30.947 回答
0

在询问任务或对话框的视图模型应该是什么样子时,我正在思考一个类似的问题。

我目前的解决方案如下所示:

public class SelectionTaskModel<TChoosable> : ViewModel
    where TChoosable : ViewModel
{
    public SelectionTaskModel(ICollection<TChoosable> choices);
    public ReadOnlyCollection<TChoosable> Choices { get; }
    public void Choose(TChoosable choosen);
    public void Abort();
}

当视图模型决定需要用户输入时,它会为用户提取一个SelectionTaskModel带有可能选择的实例。基础设施负责调出相应的视图,该视图会在适当的时候Choose()根据用户的选择调用函数。

于 2009-01-18T10:07:48.790 回答
0

我在同样的问题上苦苦挣扎。我想出了一种在 View 和 ViewModel 之间进行交互的方法。您可以开始从 ViewModel 向 View 发送消息,告诉它显示一个消息框,它会报告结果。然后 ViewModel 可以响应 View 返回的结果。

我在我的博客中展示了这一点:

于 2009-03-28T08:52:15.017 回答
0

抱歉,但我必须插话。在找到 Prism 项目中的 Prism.Wpf.Interactivity 命名空间之前,我已经通过了几个建议的解决方案。您可以使用交互请求和弹出窗口操作来滚动自定义窗口,或者为了更简单的需求,内置通知和确认弹出窗口。这些创建了真正的窗口并按此方式进行管理。您可以在对话框中传递具有您需要的任何依赖项的上下文对象。自从我找到这个解决方案后,我们就在我的工作中使用它。我们这里有许多高级开发人员,没有人想出更好的办法。我们之前的解决方案是将对话服务放入叠加层并使用演示者类来实现它,但是您必须为所有对话视图模型等提供工厂。

这不是微不足道的,但也不是超级复杂。它内置于 Prism 中,因此是最好的(或更好的)实践恕我直言。

我的2美分!

于 2018-05-07T17:33:49.937 回答
-1

编辑:是的,我同意这不是一个正确的 MVVM 方法,我现在正在使用类似于blindmeis 建议的东西。

你可以做到这一点的方法之一是

在您的主视图模型中(您打开模式的地方):

void OpenModal()
{
    ModalWindowViewModel mwvm = new ModalWindowViewModel();
    Window mw = new Window();
    mw.content = mwvm;
    mw.ShowDialog()
    if(mw.DialogResult == true)
    { 
        // Your Code, you can access property in mwvm if you need.
    }
}

在您的模态窗口视图/视图模型中:

XAML:

<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
<Button Margin="2" VerticalAlignment="Center" Name="cancelButton" IsCancel="True">Cancel</Button>

视图模型:

public ICommand OkCommand
{
    get
    {
        if (_okCommand == null)
        {
            _okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
        }
        return _okCommand ;
    }
}

void DoOk(Window win)
{
    <!--Your Code-->
    win.DialogResult = true;
    win.Close();
}

bool CanDoOk(Window win) { return true; }

或类似于此处发布的WPF MVVM:如何关闭窗口

于 2012-01-27T10:20:47.497 回答