C# 获取查找视图模型以触发模态对话框

C# 获取查找视图模型以触发模态对话框,c#,wpf,mvvm,binding,C#,Wpf,Mvvm,Binding,让我的viewmodel触发自定义查找控件以弹出实质上表示该查找viewmodel的模式对话框的正确方法是什么?自定义查找控件的数据上下文是父记录视图模型的数据上下文。lookup控件还有另一个DependencyProperty,该属性将其绑定到父记录视图模型上的lookupviewmodel属性,这表示一个子lookupviewmodel 方法1)我目前在lookupviewmodel上使用自定义控件知道要侦听的事件 方法2)我尝试在lookupviewmodel上属性的setter中抛出一

让我的viewmodel触发自定义查找控件以弹出实质上表示该查找viewmodel的模式对话框的正确方法是什么?自定义查找控件的数据上下文是父记录视图模型的数据上下文。lookup控件还有另一个DependencyProperty,该属性将其绑定到父记录视图模型上的lookupviewmodel属性,这表示一个子lookupviewmodel

方法1)我目前在lookupviewmodel上使用自定义控件知道要侦听的事件

方法2)我尝试在lookupviewmodel上属性的setter中抛出一个验证异常,查找控件的文本属性也被绑定。然后我在自定义查找控件中钩住了ErrorEvent。但是,如果用户在对话框中“更正”了值,而在这种情况下,原始值会保持不变。更糟糕的是,即使在我调用Validation.ClearInvalid之后,仍会触发另一个ErrorEvent,以某种方式将错误添加回来。因此,在所有ViewModel都具有正确数据的意义上,这里的一切都是有效的,只是textbox似乎忽略了在ErrorEvent中,基础数据源上的绑定文本属性已更改。所以在处理错误的过程中,我似乎无法纠正错误

方法2中的另一个子问题是Validation.ClearInvalid不会删除红色错误边框。我也必须手动清除错误模板。是这样吗


我想找到一种方法,在控件中使用自然错误处理,使其弹出模式对话框。

这不是使用事件的目的。事件的存在是为了促进解耦:引发事件的对象不应该知道或关心侦听它的对象在做什么。您期望一个事件能够从属性的setter内部更改属性的值——或者更糟的是,您的事件处理程序正在调用引发它正在处理的事件的属性setter,这意味着您必须做一些非常粗糙的事情来避免堆栈溢出

您的描述不是很清楚(您同时描述了您遇到的问题和您尝试的无效解决方案,这令人困惑),但听起来您尝试做的更像:

if (IsValid(value))
{
   _Property = value;
}
else
{
   _Property = GetValueFromDialog();
}
问题是,您不希望视图模型中的代码弹出对话框,因为这会创建一个无法在WPF应用程序之外测试的视图模型

在这种情况下,答案是使用依赖项注入。创建名为
IDialogService
的接口:

interface IDialogService
{
   object GetValueFromDialog();
}
现在将此属性添加到视图模型中:

public IDialogService DialogService { get; set; }
上述代码变为:

if (IsValid(value))
{
   _Property = value;
}
else
{
   _Property = DialogService.GetValueFromDialog();
}
创建一个用于WPF应用程序的对话服务,该服务实际抛出对话并获得结果。在应用程序中实例化视图模型时,请执行以下操作:

MyViewModel vm = new MyViewModel { DialogService = new WpfDialogService(); }
因此,在您的应用程序中,属性设置器将弹出对话框,并获得您期望的结果

对于单元测试,创建一个模拟对话框,如下所示:

public class MockDialogService : IDialogService
{
   private object Result;

   public MockDialogService(object result)
   {
      Result = result;
   }

   public object GetValueFromDialog() { return Result; }
}
然后,您可以编写一个测试,如:

MyViewModel vm = new MyViewModel { DialogService = MockDialogService(ExpectedResult) };
vm.Property = InvalidValue;
Assert.AreEqual(ExpectedResult, vm.Property);

以上内容实际上更多的是解决方案的草图,而不是解决方案-根据应用程序使用对话框的方式,您可能需要比此处所示更多的功能。如果你看一下MVVM框架,你会发现很多框架都实现了这样或那样的对话服务

您可以使用MVVMLight或Prism之类的框架,它允许您以完全解耦的方式在不同实体之间传递有效负载。MVVMLight与Prism相比非常轻。它有一个Messanger的概念,充当系统范围的事件总线。类似地,在Prism中也有EventAggregator

我读过DialogService甚至Mediator。我想我只是被撕裂了,特别是考虑到在这个话题上有这么多的争论。谁应该负责抛出GUI元素、视图或视图模型?如果我使用DialogService,那么无论在什么地方使用什么控件,如果属性设置程序失败,它都会抛出一个对话框。如果设计师没有实现对话框呢?我试图找到一种方法,使viewmodels不以任何方式关心gui元素的形状或形式。他们验证,如果失败,视图决定是否要采取行动。我可能最终需要DialogService用于其他类型的对话框,但我不想轻易放弃验证查找对话框。我现在做的是我的LookupViewModel有一个bool ShowModalDialog属性,自定义查找控件通过InotifyPropertyChanged监视该属性。如果验证失败,查找视图模型会将其设置为true,并显示模式,用户可以选择新值或取消。对话框结果返回到将ShowModalDialog设置为true的属性设置器,如果结果不是true,则抛出验证异常。视图模型请求对话框并处理响应。WPF对话框服务本质上是视图的一部分。关注点很好地分开了:视图模型不知道对话框是如何实现的,它只知道它需要用户的响应并得到响应。问题是,通过DialogService,视图模型至少需要知道期望返回的值。如果需要返回多个属性呢?我喜欢这个当前版本的地方在于LookupViewModel现在能够触发模式显示,并且数据(用户选择)通过正常的绑定/命令机制返回到模型中。我猜在某种程度上,我的自定义查找控件本身只是一种单向对话服务触发器,只是查找的一个尾部。然而,测试需要什么?使用dialog服务,需要模拟对话框。在我当前的实现中,我不需要模拟任何东西,我可以测试验证是否失败,也可以测试验证是否失败