Caliburn ShowDialog and MessageBox

6

我正在使用Caliburn制作一个MVVM的小型演示应用程序。

现在我想以MVVM的方式显示一个消息框。

对于对话框,我创建了一个事件,在ShellView(根视图)中处理这个事件,然后只需使用WindowManager.ShowDialog和Dialogs ViewModel类型显示对话框。这对我来说似乎符合MVVM的标准。

但是如何以MVVM的方式显示消息框并获取其结果(确定或取消)?

我已经看到这个问题,但它没有什么答案。

Eisenberg先生回答说:

“Caliburn内置了调用自定义消息框的服务。”

有人能告诉我他的意思吗?我在示例中没有看到它。

3个回答

7

如你所提到的,你只需要准备视图模型(例如ConfirmationBoxViewModel)和一个合适的视图。你将需要创建两个动作(在从 Screen 继承视图模型之后,这是必要的以使用TryClose。你总是可以实现IScreen,但那会更麻烦):

public void OK()
{
    TryClose(true);
}

public void Cancel()
{
    TryClose(false);
} 

然后在你的另一个视图模型中:

var box = new ConfirmationBoxViewModel()
var result = WindowManager.ShowDialog(box);
if(result == true)
{
// OK was clicked
}

请注意,对话框关闭后,如果需要从对话框中获取其他数据(例如所选项、显示名称等),可以访问视图模型属性。


非常感谢!好的,这是制作一个看起来像消息框的对话框。但是有没有一种简单的方法来封装一个MessageBox?有没有一种通过事件实现解耦的方式?MessageBox非常灵活,我很想以某种方式使用它。 - Mare Infinitus
我并不这么认为,至少我所知道的没有真正简单的方法。考虑到你可以创建一个易于定制和使用的对话框式视图模型和视图,我不认为有任何理由包装默认的 MessageBox... - Patryk Ćwiek

5
在这篇由框架协调员编写的文章《Billy Hollis混合式Shell》中,作者展示了一种处理对话框和消息框的好方法,但是他使用了依赖注入 (当然你可以不用DI,但它会让事情变得更简单)。主要思想是让您的主窗口,也就是应用程序 Shell 实现一个类似这样的接口:
public interface IDialogManager
    {

        void ShowDialog(IScreen dialogModel);
        void ShowMessageBox(string message, string title = null, MessageBoxOptions options = MessageBoxOptions.Ok, Action<IMessageBox> callback = null);

    }

然后他将此接口注册到IoC容器中,之后你可以自己想象,如果你没有时间的话,可以查看附带文章的源代码


非常感谢!我唯一遇到的问题是如何返回用户在消息框(或对话框)中选择的选项。需要仔细研究一下。 - Mare Infinitus
1
好的,这涉及到Silverlight和MEF,对我来说不是很容易理解。 - Mare Infinitus
@MareInfinitus 你可以下载HelloScreensWPF - Ibrahim Najjar
你太棒了!非常感谢!这应该与Caliburn.micro一起分发! - Mare Infinitus
3
@IbrahimNajjar回答中提到的所有链接都已过期。你是否有更新后的链接? - Santhosh
显示剩余3条评论

0

当根/主/外壳视图模型实现某种DialogService接口时,每个需要显示对话框的其他视图模型最终都会依赖于根视图模型。有时这可能不是理想的,例如,如果它可能导致依赖循环:
DialogService(又名RootViewModel)->SomeViewModel -> RootViewModel

打破此依赖链(并实际上反转它)的更深入方法如下:

  • 实现一个行为,检测Window.OnSourceInitialized事件,并将其附加到主视图Window组件上。这是窗口句柄可用时触发的事件。在事件发生时,行为将通过附加属性通知传递给某些处理程序:
<my:WindowSourceBehavior InitListener="{Binding WindowListener}" />

public class WindowSourceBehavior : Behavior<Window>
{
  // ...
  // boilerplate code for IWindowListener InitListener dependency property
  // ...

  attachedWindow.SourceInitialized += (sender, evt) =>
  {
     // ...
     InitListener.SourceInitialized(sender as Window);
  }
}
  • DialogService 根据行为请求公开处理程序或接口:
public class DialogService : IWindowListener
{
  // ...
  public void SourceInitialized(Window rootWindow) { /* ... */ }
}
  • 在根视图模型中,(间接)作为依赖项获取DialogService。在构造期间,将视图模型绑定属性WindowListener设置为DialogService处理程序/接口:
public MainViewModel(IWindowListener dialogServiceInDisguise)
{
  WindowListener = dialogServiceInDisguise;
}

public IWindowListener WindowListener { get; private set; }

这样做,DialogService 就能够获取根窗口的控制权,而需要显示对话框的任何视图模型都不会在主视图模型上创建(间接)依赖。

我没有看到被接受的答案和主/外壳 ViewModel 之间存在(间接)依赖关系。隐藏很多东西也不是最好的选择,我喜欢明确依赖和所做的事情。 - Mare Infinitus
如果我理解正确的话,那么您有一个实现了IDialogManager接口的主视图模型,我猜想该接口将被注入到需要显示对话框的每个其他视图模型中。因此,许多视图模型最终会依赖于根视图模型。顺便说一下,在这种情况下,它不会是对根视图模型的显式依赖,因为它们不需要那个。再次强调,这将是对IDialogManager接口的伪装依赖,而不知道它实际上是根视图模型。我认为这是正确的。您认为呢? - superjos
不,DialogManager只是被注入的,因此没有(直接或间接)依赖于rootviewmodel。只有dialogmanager,它将ViewModels与视图结合起来,就像caliburn那样。DialogManager本身不是rootviewmodel,而是另一个仅进行对话框管理的类。因此,在我看来,这是一种非常清晰的以MVVM方式拥有对话框的方法(因为ViewModels只看到其他ViewModels,没有对视图的依赖)。尽管如此,正如之前所说,DialogManager不是rootviewmodel。但是你的解决方案肯定很有用。 - Mare Infinitus
这是让我得出结论的部分:“您可以让主窗口[...]实现公共接口IDialogManager”。这不就是说根视图模型实现了DialogManager接口吗? - superjos
如果你实现了接口,那么它就被实现了。但在被接受的答案中的代码中,它是被注入的,也就是说它使用了一些对话管理器,而不是它本身就是一个对话管理器。 - Mare Infinitus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接