我试图学习WPF和MVVM问题,但遇到了障碍。
这个问题与这个问题类似,但不完全相同(handling-dialog -in-wpf-with-mvvm)…
我有一个使用MVVM模式编写的“Login”表单。
该表单有一个ViewModel,其中包含用户名和密码,它们使用普通的数据绑定绑定到XAML中的视图。
它还有一个“登录”命令,该命令被绑定到表单上的“登录”按钮,agan使用正常的数据绑定。
当“Login”命令触发时,它会调用ViewModel中的一个函数,该函数会触发并通过网络发送数据以登录。当这个函数完成时,有2个动作:
登录无效-我们只是显示一个消息框,一切正常
登录是有效的,我们需要关闭login表单,并让它返回true作为它的dialgresult…
问题是,ViewModel对实际的视图一无所知,所以它如何关闭视图并告诉它返回一个特定的dialgresult ??我可以在代码背后粘贴一些代码,和/或通过视图模型传递视图,但这似乎会击败整个点MVVM完全…
更新
最后,我违背了MVVM模式的“纯度”,让视图发布了一个Closed事件,并公开了一个Close方法。ViewModel会调用view。close。视图仅通过接口了解,并通过IOC容器连接,因此不会损失可测试性或可维护性。
公认的答案是-5票,这似乎相当愚蠢!虽然我很清楚通过“纯粹”解决问题所获得的良好感觉,但我肯定不是唯一一个认为200行事件、命令和行为只是为了避免以“模式”和“纯粹”为名的一行方法有点荒谬的人....
这可能很晚了,但我遇到了同样的问题,我找到了一个适合我的解决方案。
我不知道如何创建一个没有对话框的应用程序(也许这只是一个思维障碍)。所以我在僵局与MVVM和显示一个对话框。所以我看到了这篇CodeProject文章:
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
这是一个UserControl,基本上允许一个窗口位于另一个窗口的可视树中(在xaml中不允许)。它还公开了一个名为isdisplays的布尔型DependencyProperty。
你可以设置一种样式,通常是在一个资源字典中,当控件的Content属性!= null时通过触发器显示对话框:
<Style TargetType="{x:Type d:Dialog}">
<Style.Triggers>
<Trigger Property="HasContent" Value="True">
<Setter Property="Showing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
在你想要显示对话框的视图中,只需这样做:
<d:Dialog Content="{Binding Path=DialogViewModel}"/>
在你的ViewModel中,你所要做的就是将属性设置为一个值(注意:ViewModel类必须支持INotifyPropertyChanged,以便视图知道发生了什么)。
像这样:
DialogViewModel = new DisplayViewModel();
为了匹配ViewModel和View,你应该在资源词典中有这样的东西:
<DataTemplate DataType="{x:Type vm:DisplayViewModel}">
<vw:DisplayView/>
</DataTemplate>
通过所有这些,您可以得到一行代码来显示对话框。您遇到的问题是,仅使用上述代码无法真正关闭对话框。这就是为什么你必须在DisplayViewModel基类中放入一个事件,而不是上面的代码,写这个
var vm = new DisplayViewModel();
vm.RequestClose += new RequestCloseHandler(DisplayViewModel_RequestClose);
DialogViewModel = vm;
然后,您可以通过回调处理对话框的结果。
这可能看起来有点复杂,但一旦奠定了基础,就非常简单了。同样,这是我的实现,我相信还有其他人:)
希望这能帮到你,它救了我。
这是一个简单而干净的解决方案——您向ViewModel添加一个事件,并指示窗口在该事件触发时关闭自己。
要了解更多细节,请参阅我的博客文章,从ViewModel关闭窗口。
XAML:
<Window
x:Name="this"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions">
<i:Interaction.Triggers>
<i:EventTrigger SourceObject="{Binding}" EventName="Closed">
<ei:CallMethodAction
TargetObject="{Binding ElementName=this}"
MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Window>
ViewModel:
private ICommand _SaveAndCloseCommand;
public ICommand SaveAndCloseCommand
{
get
{
return _SaveAndCloseCommand ??
(_SaveAndCloseCommand = new DelegateCommand(SaveAndClose));
}
}
private void SaveAndClose()
{
Save();
Close();
}
public event EventHandler Closed;
private void Close()
{
if (Closed != null) Closed(this, EventArgs.Empty);
}
注意:这个例子使用了Prism的DelegateCommand(参见Prism: commands),但是可以使用任何ICommand实现。
您可以使用这个官方包中的行为。
我最终混合了Joe White的回答和Adam Mills的回答中的一些代码,因为我需要在编程创建的窗口中显示用户控件。因此,DialogCloser不需要在窗口上,它可以在用户控件本身上
<UserControl ...
xmlns:xw="clr-namespace:Wpf"
xw:DialogCloser.DialogResult="{Binding DialogResult}">
而DialogCloser会找到用户控件的窗口,如果它没有附加到窗口本身。
namespace Wpf
{
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d.GetWindow();
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(DependencyObject target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
public static class Extensions
{
public static Window GetWindow(this DependencyObject sender_)
{
Window window = sender_ as Window;
return window ?? Window.GetWindow( sender_ );
}
}
}
这里有很多关于MVVM利弊的评论。就我而言,我同意Nir的观点;这是一个适当使用模式的问题,MVVM并不总是适合。人们似乎愿意牺牲所有最重要的软件设计原则,只是为了让它适合MVVM。
也就是说,. .我觉得你的案子可以做点重构。
在我遇到的大多数情况下,WPF可以让你在没有多个Windows的情况下工作。也许你可以尝试使用框架和页面来代替带有对话框的Windows。
在你的情况下,我的建议是让LoginFormViewModel处理LoginCommand,如果登录无效,将LoginFormViewModel上的属性设置为适当的值(false或一些枚举值,如UserAuthenticationStates.FailedAuthentication)。对于成功登录(true或其他enum值),也可以执行相同的操作。然后使用DataTrigger来响应各种用户身份验证状态,并可以使用简单的Setter来更改Frame的Source属性。
让你的登录窗口返回一个对话框,我认为这是你感到困惑的地方;dialgresult实际上是ViewModel的一个属性。在我使用WPF的有限经验中,当某些事情感觉不对时,通常是因为我在考虑如何在WinForms中做同样的事情。
希望这能有所帮助。