Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Wpf 使用服务定位器的MVVM模式对话框_Wpf_Mvvm_Modal Dialog - Fatal编程技术网

Wpf 使用服务定位器的MVVM模式对话框

Wpf 使用服务定位器的MVVM模式对话框,wpf,mvvm,modal-dialog,Wpf,Mvvm,Modal Dialog,我正在开发一个遵循MVVM模式的WPF应用程序。为了显示模态对话框,我尝试按照以下文章的建议进行操作。 但在这类文章中,我观察到,DialogService接口的ShowDialog方法是从MainWindowViewModel调用的 我申请的情况略有不同。 MainWindow.xaml包含一个用户控件,比如包含按钮Add的ChildView。 MainWindowViewModel包含另一个与ChildView绑定的ViewModel,如ChildVM。 ChildVM包含AddComma

我正在开发一个遵循MVVM模式的WPF应用程序。为了显示模态对话框,我尝试按照以下文章的建议进行操作。

但在这类文章中,我观察到,DialogService接口的ShowDialog方法是从MainWindowViewModel调用的

我申请的情况略有不同。 MainWindow.xaml包含一个用户控件,比如包含按钮Add的ChildView。 MainWindowViewModel包含另一个与ChildView绑定的ViewModel,如ChildVM。 ChildVM包含AddCommand,我需要在AddExecute方法时显示模态对话框 调用与AddCommand对应的命令。 我怎样才能做到这一点

编辑代码

     private Window FindOwnerWindow(object viewModel)
            {
                    FrameworkElement view = null;

        // Windows and UserControls are registered as view.
        // So all the active windows and userControls are contained in views
        foreach (FrameworkElement viewIterator in views)
        {
            // Check whether the view is an Window
            // If the view is an window and dataContext of the window, matches
            // with the viewModel, then set view = viewIterator
            Window viewWindow = viewIterator as Window;
            if (null != viewWindow)
            {
                if (true == ReferenceEquals(viewWindow.DataContext, viewModel))
                {
                    view = viewWindow;
                    break;
                }

            }
            else
            {
                // Check whether the view is an UserControl
                // If the view is an UserControl and Content of the userControl, matches
                // with the viewModel, then set view = userControl
                // In case the view is an user control, then find the Window that contains the
                // user control and set it as owner
                System.Windows.Controls.UserControl userControl = viewIterator as System.Windows.Controls.UserControl;
                if (null != userControl)
                {
                    if (true == ReferenceEquals(userControl.Content, viewModel))
                    {
                        view = userControl;
                        break;
                    }

                }
            }
        }
        if (view == null)
        {
            throw new ArgumentException("Viewmodel is not referenced by any registered View.");
        }

        // Get owner window
        Window owner = view as Window;
        if (owner == null)
        {
            owner = Window.GetWindow(view);
        }

        // Make sure owner window was found
        if (owner == null)
        {
            throw new InvalidOperationException("View is not contained within a Window.");
        }

        return owner;
        }

好的,如果我没弄错的话,您想从另一个ChildViewModel而不是MainWindowViewModel打开模式对话框吗

查看您链接的CodeProject文章的MainWindowViewModel的构造函数:

ViewModel具有具有以下签名的构造函数:

public主窗口视图模型(
IDialogService对话框服务,
IPersonService个人服务,
Func openFileDialogFactory)
这意味着,对于构建,您需要显示模式对话框的服务、另一个服务(personService),这在这里并不重要,还需要一个特定对话框打开文件的工厂openFileDialogFactory

为了使用服务(这是本文的核心部分),实现了一个简单的ServiceLocator,并定义了一个默认构造函数,该构造函数使用ServiceLocator获取ViewModel需要的服务实例:

public主窗口视图模型()
:这个(
ServiceLocator.Resolve(),
ServiceLocator.Resolve(),
()=>ServiceLocator.Resolve()
{}
这是可能的,因为ServiceLocator是静态的。或者,您可以使用ServiceLocator为构造函数中的服务设置本地字段。上述方法更好,因为如果您不想使用ServiceLocator,它允许您自己设置服务

您可以在自己的ChildViewModel中执行完全相同的操作

公共子视图模型(IDialogService dialogService) { _dialogService=dialogService; } 创建一个默认构造函数,该构造函数使用从ServiceLocator解析的服务实例调用上述构造函数:

publicChildViewModel():此(ServiceLocator.Resolve()){
现在,您可以在ChildViewModel中的任何位置使用该服务,如下所示:

\u dialogService.ShowDialog(这是vmForDialog);
为了找到视图的所有者窗口(该窗口本身不是视图),您需要修改DialogService的
FindOwnerWindow
方法来查找视图的父窗口,而不是将窗口作为视图本身。您可以使用VisualTreeHelper执行此操作:

专用窗口查找窗口(对象视图模型)
{
var view=views.SingleOrDefault(v=>ReferenceEquals(v.DataContext,viewModel));
如果(视图==null)
{
抛出新ArgumentException(“Viewmodel未被任何已注册视图引用”);
}
DependencyObject所有者=视图;
//遍历父对象,直到找到一个窗口,
//如果视图本身不是窗口
而(!(所有者是窗口))
{
所有者=VisualTreeHelper.GetParent(所有者);
如果(所有者==null)
抛出新异常(“未找到拥有视图的窗口”);
}
//确保找到所有者窗口
如果(所有者==null)
{
抛出新的InvalidOperationException(“视图不包含在窗口中”);
}
返回(窗口)所有者;
}
但是,您仍然需要注册UserControl,在UserControl上设置附加属性:

<UserControl x:Class="ChildView"
             ...
             Service:DialogService.IsRegisteredView="True">
   ...
 </UserControl>

...
据我所知,这是可行的

其他信息:

为了完成同样的事情,我使用了PRISM框架,它提供了很多功能,用于实现这种解耦、控制反转(IoC)和依赖注入(DI)。也许也值得你看一看

希望这有帮助


<强>编辑以考虑评论。<强>

< p>看看你是否喜欢这个想法…我使用的是Castle Windsor和Prism,因此您的里程数可能会有所不同,但另一个MVVM和IoC的概念应该是相同的

从要打开模式对话框的MainViewModel.cs开始

var view = ServiceLocator.Current.GetInstance<SomeDialogView>();
view.ShowDialog();
该死

但是等等,ServiceLocator不能给我主视图吗

var view = ServiceLocator.Current.GetInstance<SomeDialogView>();
view.Owner = ServiceLocator.Current.GetInstance<MainView>();  // sure, why not?
view.ShowDialog();
通过使用fluent除非()和If()使其更具辨别力

提供的主视图就是我们想要的


HTH

Hi@Anirban,跟随本文,看看模态对话框是如何工作的。我用这篇文章来帮助我创建我的应用程序。希望这有帮助!方法与我提供的链接相同。你找到解决方案了吗?这看起来不错,但在我看来,将ViewModel放在用户控件的内容中是一种误解。这并不是用户控件的真正意义所在。那你为什么不使用ContentControl呢?但我真的没有洞察力。我会考虑设置DATACONTEX,而不是内容,如果它对你有用。问题在于孩子VIEW模型对应于用户控件,而不是像MeMistoVIEW模型那样的窗口。dialogService.ShowDialog(这是vmForDialog)。。当dialogService试图找出dataContext为“this”的窗口时,它失败。请看一下DialogService实现的FindOwnerWindow方法。我希望我已经解决了这个问题。在我的应用程序中,任何用户控件的相应viewmodel都作为用户控件的内容与用户控件关联,而不是作为
var view = ServiceLocator.Current.GetInstance<SomeDialogView>();
view.Owner = ServiceLocator.Current.GetInstance<MainView>();  // sure, why not?
view.ShowDialog();
container.Register(Classes.FromAssemblyNamed("WhateverAssemblyTheyreIn")
  .InNamespace("WhateverNamespaceTheyreIn")
  .LifestyleTransient());
container.Register(Classes.FromAssemblyNamed("WhateverAssemblyTheyreIn")
  .InNamespace("WhateverNamespaceTheyreIn")
  .Unless(type => type == typeof(MainView))
  .LifestyleTransient());  // all as before but MainView.

container.Register(Classes.FromAssemblyNamed("WhateverAssemblyTheyreIn")
  .InNamespace("WhateverNamespaceTheyreIn")
  .If(type => type == typeof(MainView))
  .LifestyleSingleton());  // set MainView to singleton!