ViewModel应该如何关闭表单?

我试图学习WPF和MVVM问题,但遇到了障碍。 这个问题类似,但不完全相同就像这个(handling-dialog -in-wpf-with-mvvm)

我有一个“login”;使用MVVM模式编写的表单。

这个表单有一个ViewModel,其中包含用户名和密码,它们使用普通的数据绑定绑定到XAML中的视图。 它还有一个&;Login"命令,该命令绑定到&;login &;按钮上的表单,agan使用正常的数据绑定

当&;Login"当命令触发时,它会调用ViewModel中的一个函数,该函数会触发并通过网络发送数据来登录。当这个函数完成时,有2个动作:

  1. 登录无效-我们只是显示一个消息框,一切正常

  2. 登录是有效的,我们需要关闭login表单并让它返回true作为它的DialogResult

问题是,ViewModel对实际的视图一无所知,所以它如何关闭视图并告诉它返回一个特定的dialgresult ??我可以在代码背后粘贴一些代码,和/或通过视图模型传递视图,但这似乎会击败整个点MVVM完全…


更新

最后我只是违背了“纯洁”。MVVM模式,并让视图发布Closed事件,并公开Close方法。然后ViewModel将调用view.Close。视图仅通过接口了解,并通过IOC容器连接,因此不会损失可测试性或可维护性。

公认的答案是-5票,这似乎相当愚蠢!虽然我很清楚“纯粹”解决问题所带来的良好感觉,但我肯定不是唯一一个认为200行事件、命令和行为只是为了避免以“模式”为名的一行方法的人。和“;purity"是不是有点可笑....

113524 次浏览

以下是我最初所做的,它确实有效,但它似乎相当冗长和丑陋(全局静态的任何东西都不好)

1: App.xaml.cs

public partial class App : Application
{
// create a new global custom WPF Command
public static readonly RoutedUICommand LoggedIn = new RoutedUICommand();
}

2: LoginForm.xaml

// bind the global command to a local eventhandler
<CommandBinding Command="client:App.LoggedIn" Executed="OnLoggedIn" />

3: LoginForm.xaml.cs

// implement the local eventhandler in codebehind
private void OnLoggedIn( object sender, ExecutedRoutedEventArgs e )
{
DialogResult = true;
Close();
}

4: LoginFormViewModel.cs

// fire the global command from the viewmodel
private void OnRemoteServerReturnedSuccess()
{
App.LoggedIn.Execute(this, null);
}

我后来删除了所有这些代码,只让LoginFormViewModel在它的视图上调用Close方法。它最终变得更好,也更容易理解。在我看来,模式的意义是让人们更容易理解你的应用程序在做什么,在这种情况下,MVVM使它比我没有使用它更难理解,现在是一个__abc1 -模式。

这里有很多关于MVVM利弊的评论。就我而言,我同意Nir的观点;这是一个适当使用模式的问题,MVVM并不总是适合。人们似乎愿意牺牲所有最重要的软件设计原则,只是为了让它适合MVVM。

也就是说,. .我觉得你的案子可以做点重构。

在我遇到的大多数情况下,WPF可以让你在没有多个__abc的情况下通过。也许你可以尝试使用__abc1和__abc2来代替带有__abc3的Windows。

在你的情况下,我的建议是让LoginFormViewModel处理LoginCommand,如果登录无效,将LoginFormViewModel上的属性设置为适当的值(false或一些枚举值,如UserAuthenticationStates.FailedAuthentication)。对于成功登录(true或其他enum值),也可以执行相同的操作。然后使用DataTrigger来响应各种用户身份验证状态,并可以使用简单的Setter来更改FrameSource属性。

有你的登录窗口返回DialogResult我认为是你感到困惑的地方;DialogResult实际上是ViewModel的一个属性。在我使用WPF的有限经验中,当某些事情感觉不对时,通常是因为我在考虑如何在WinForms中做同样的事情。

希望这能有所帮助。

我处理它的方法是在ViewModel中添加一个事件处理程序。当用户成功登录时,我将触发事件。在我的视图中,我将附加到这个事件,当它发射时,我将关闭窗口。

供参考,我遇到了同样的问题,我想我找到了一个不需要全局变量或静态变量的工作,尽管它可能不是最好的答案。我让你们自己决定。

在我的例子中,实例化要显示的窗口的ViewModel(让我们称之为ViewModelMain)也知道LoginFormViewModel(使用上面的情况作为例子)。

所以我所做的是在LoginFormViewModel上创建一个属性,它的类型是ICommand(让我们称之为CloseWindowCommand)。然后,在窗口上调用. showdialog()之前,我将LoginFormViewModel上的CloseWindowCommand属性设置为我实例化的窗口的Window . close()方法。然后在LoginFormViewModel中,我所要做的就是调用CloseWindowCommand.Execute()来关闭窗口。

我认为这是一种变通/hack,但它在没有真正打破MVVM模式的情况下工作得很好。

你可以随意批评这个过程,我可以接受!:)

这可能很晚了,但我遇到了同样的问题,我找到了一个适合我的解决方案。

我不知道如何创建一个没有对话框的应用程序(也许这只是一个思维障碍)。所以我在僵局与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;

然后,您可以通过回调处理对话框的结果。

这可能看起来有点复杂,但一旦奠定了基础,就非常简单了。同样,这是我的实现,我相信还有其他人:)

希望这能帮到你,它救了我。

假设你的登录对话框是第一个被创建的窗口,在你的LoginViewModel类中试试这个:

    void OnLoginResponse(bool loginSucceded)
{
if (loginSucceded)
{
Window1 window = new Window1() { DataContext = new MainWindowViewModel() };
window.Show();


App.Current.MainWindow.Close();
App.Current.MainWindow = window;
}
else
{
LoginError = true;
}
}

从我的角度来看,这个问题很好,因为同样的方法不仅适用于“Login”窗口,而且适用于任何类型的窗口。我看了很多建议,没有一个适合我。请审查我的建议,是从MVVM设计模式文章

每个ViewModel类都应该继承自WorkspaceViewModel,它具有RequestClose事件和ICommand类型的CloseCommand属性。CloseCommand属性的默认实现将引发RequestClose事件。

为了关闭窗口,应该重写窗口的OnLoaded方法:

void CustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
CustomerViewModel customer = CustomerViewModel.GetYourCustomer();
DataContext = customer;
customer.RequestClose += () => { Close(); };
}

或者你应用的OnStartup方法:

    protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);


MainWindow window = new MainWindow();
var viewModel = new MainWindowViewModel();
viewModel.RequestClose += window.Close;
window.DataContext = viewModel;


window.Show();
}

我想RequestClose事件和CloseCommand属性在WorkspaceViewModel中的实现是非常清楚的,但我将显示它们是一致的:

public abstract class WorkspaceViewModel : ViewModelBase
// There's nothing interesting in ViewModelBase as it only implements the INotifyPropertyChanged interface
{
RelayCommand _closeCommand;
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
{
_closeCommand = new RelayCommand(
param => Close(),
param => CanClose()
);
}
return _closeCommand;
}
}


public event Action RequestClose;


public virtual void Close()
{
if ( RequestClose != null )
{
RequestClose();
}
}


public virtual bool CanClose()
{
return true;
}
}

RelayCommand的源代码:

public class RelayCommand : ICommand
{
#region Constructors


public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");


_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors


#region ICommand Members


[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}


public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}


public void Execute(object parameter)
{
_execute(parameter);
}


#endregion // ICommand Members


#region Fields


readonly Action<object> _execute;
readonly Predicate<object> _canExecute;


#endregion // Fields
}

不要因为那些消息来源而对我不好!如果我昨天就买了,就能省几个小时……

欢迎任何评论或建议。

我受到Thejuan的回答的启发,写了一个更简单的附加属性。没有样式,没有触发器;相反,你可以这样做:

<Window ...
xmlns:xc="clr-namespace:ExCastle.Wpf"
xc:DialogCloser.DialogResult="{Binding DialogResult}">

这几乎就像WPF团队正确地将dialgresult设置为依赖属性一样干净。只要在你的ViewModel上放一个bool? DialogResult属性,并实现INotifyPropertyChanged和voilà,你的ViewModel可以通过设置一个属性来关闭窗口(并设置其dialgresult)。MVVM应该是这样的。

下面是DialogCloser的代码:

using System.Windows;


namespace ExCastle.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 as Window;
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
}

我还发布了这个在我的博客上

为什么不直接将窗口作为命令参数传递呢?

c#:

 private void Cancel( Window window )
{
window.Close();
}


private ICommand _cancelCommand;
public ICommand CancelCommand
{
get
{
return _cancelCommand ?? ( _cancelCommand = new Command.RelayCommand<Window>(
( window ) => Cancel( window ),
( window ) => ( true ) ) );
}
}

XAML:

<Window x:Class="WPFRunApp.MainWindow"
x:Name="_runWindow"
...
<Button Content="Cancel"
Command="{Binding Path=CancelCommand}"
CommandParameter="{Binding ElementName=_runWindow}" />

你可以让ViewModel公开一个View注册到的事件。然后,当ViewModel决定关闭视图的时间时,它会触发导致视图关闭的事件。如果你想要传回一个特定的结果值,那么你会在ViewModel中有一个属性。

在众多的答案中,我想补充以下几点。假设您在ViewModel上有一个ICommand,并且您希望该命令关闭它的窗口(或任何与此相关的其他操作),您可以使用以下内容。

var windows = Application.Current.Windows;
for (var i=0;i< windows.Count;i++ )
if (windows[i].DataContext == this)
windows[i].Close();

它并不完美,可能很难测试(因为它很难模拟/存根静态),但它比其他解决方案更干净。

埃里克

Application.Current.MainWindow.Close()

这够了!

这是一个简单而干净的解决方案——您向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>

视图模型:

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(参见棱镜:指挥),但是任何ICommand实现都可以用于这个问题。

你可以使用官方包中的行为。

另一个解决方案是在视图模型中使用INotifyPropertyChanged创建属性,如dialgresult,然后在代码背后写:

public class SomeWindow: ChildWindow
{
private SomeViewModel _someViewModel;


public SomeWindow()
{
InitializeComponent();


this.Loaded += SomeWindow_Loaded;
this.Closed += SomeWindow_Closed;
}


void SomeWindow_Loaded(object sender, RoutedEventArgs e)
{
_someViewModel = this.DataContext as SomeViewModel;
_someViewModel.PropertyChanged += _someViewModel_PropertyChanged;
}


void SomeWindow_Closed(object sender, System.EventArgs e)
{
_someViewModel.PropertyChanged -= _someViewModel_PropertyChanged;
this.Loaded -= SomeWindow_Loaded;
this.Closed -= SomeWindow_Closed;
}


void _someViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == SomeViewModel.DialogResultPropertyName)
{
this.DialogResult = _someViewModel.DialogResult;
}
}
}
最重要的片段是_someViewModel_PropertyChangedDialogResultPropertyName可以是SomeViewModel中的某个公共const字符串

我使用这种技巧在视图控件中做一些更改,以防在ViewModel中很难做到这一点。OnPropertyChanged在ViewModel中,你可以在View中做任何你想做的事情。ViewModel仍然是“单元可测试的”,后面代码中的一小行代码没有什么区别。

我实现了Joe White的解决方案,但遇到了偶尔出现“只有在创建Window并显示为对话框后,才能设置dialgresult”错误的问题。

在视图关闭后,我一直保持ViewModel,偶尔我也会使用相同的VM打开一个新的视图。似乎在旧视图被垃圾收集之前关闭新视图会导致DialogResultChanged试图在关闭的窗口上设置DialogResult属性,从而引发错误。

我的解决方案是改变DialogResultChanged来检查窗口的IsLoaded属性:

private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null && window.IsLoaded)
window.DialogResult = e.NewValue as bool?;
}

做出此更改后,关闭对话框的任何附件都将被忽略。

在你需要关闭窗口的地方,简单地把这个放在视图模型中:

哈哈

  foreach (Window window in Application.Current.Windows)
{
if (window.DataContext == this)
{
window.Close();
return;
}
}

我最终混合了乔·怀特的回答和一些来自亚当·米尔斯的回答的代码,因为我需要在编程创建的窗口中显示用户控件。因此,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_ );
}
}
}

我会这样做:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;


// View


public partial class TestCloseWindow : Window
{
public TestCloseWindow() {
InitializeComponent();
Messenger.Default.Register<CloseWindowMsg>(this, (msg) => Close());
}
}


// View Model


public class MainViewModel: ViewModelBase
{
ICommand _closeChildWindowCommand;


public ICommand CloseChildWindowCommand {
get {
return _closeChildWindowCommand?? (_closeChildWindowCommand = new RelayCommand(() => {
Messenger.Default.Send(new CloseWindowMsg());
}));
}
}
}


public class CloseWindowMsg
{
}

我读了所有的答案,但我必须说,他们中的大多数都不够好,甚至更糟。

你可以用DialogService类很好地处理这个问题,它的职责是显示对话框窗口并返回对话框结果。我已经创建了示例项目演示它的实现和用法。

以下是最重要的部分:

//we will call this interface in our viewmodels
public interface IDialogService
{
bool? ShowDialog(object dialogViewModel, string caption);
}


//we need to display logindialog from mainwindow
public class MainWindowViewModel : ViewModelBase
{
public string Message {get; set;}
public void ShowLoginCommandExecute()
{
var loginViewModel = new LoginViewModel();
var dialogResult = this.DialogService.ShowDialog(loginViewModel, "Please, log in");


//after dialog is closed, do someting
if (dialogResult == true && loginViewModel.IsLoginSuccessful)
{
this.Message = string.Format("Hello, {0}!", loginViewModel.Username);
}
}
}




public class DialogService : IDialogService
{
public bool? ShowDialog(object dialogViewModel, string caption)
{
var contentView = ViewLocator.GetView(dialogViewModel);
var dlg = new DialogWindow
{
Title = caption
};
dlg.PART_ContentControl.Content = contentView;


return dlg.ShowDialog();
}
}

这样不是更简单吗?比EventAggregator或其他类似的解决方案更简单,更可读和持久,但并非最不容易调试?

正如你所看到的,在我的视图模型中,我已经使用了ViewModel第一种方法,在我的帖子中描述:WPF中从ViewModel调用View的最佳实践

当然,在现实世界中,DialogService.ShowDialog必须有更多的选项来配置对话框,例如他们应该执行的按钮和命令。有不同的方式这样做,但它超出了范围:)

虽然这并没有回答如何通过视图模型做到这一点的问题,但它确实展示了如何仅使用XAML +混合SDK来做到这一点。

我选择从Blend SDK下载并使用两个文件,这两个文件都可以通过NuGet从微软获得。这些文件是:

system . windows . interactive .dll和Microsoft.Expression.Interactions.dll

dll提供了很好的功能,比如在视图模型或其他目标上设置属性或调用方法的能力,并在内部提供了其他小部件。

一些XAML:

<Window x:Class="Blah.Blah.MyWindow"
...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
...>
<StackPanel>
<Button x:Name="OKButton" Content="OK">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
PropertyName="DialogResult"
Value="True"
IsEnabled="{Binding SomeBoolOnTheVM}" />
</i:EventTrigger>
</Button>
<Button x:Name="CancelButton" Content="Cancel">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
PropertyName="DialogResult"
Value="False" />
</i:EventTrigger>
</Button>


<Button x:Name="CloseButton" Content="Close">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<!-- method being invoked should be void w/ no args -->
<ei:CallMethodAction
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
MethodName="Close" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<StackPanel>
</Window>
注意,如果你只是想要简单的确定/取消行为,你可以使用IsDefault和IsCancel属性来摆脱w/,只要窗口显示为w/ window . showdialog () 我个人有问题w/一个按钮,有IsDefault属性设置为true,但它是隐藏的页面加载时。在显示后,它似乎不想很好地播放,所以我只是设置窗口。如上所示的dialgresult属性,它适用于我

这是简单的bug免费解决方案(与源代码),这是为我工作。

  1. INotifyPropertyChanged派生你的ViewModel

  2. 在ViewModel中创建一个可观察属性CloseDialog

    public void Execute()
    {
    // Do your task here
    
    
    // if task successful, assign true to CloseDialog
    CloseDialog = true;
    }
    
    
    private bool _closeDialog;
    public bool CloseDialog
    {
    get { return _closeDialog; }
    set { _closeDialog = value; OnPropertyChanged(); }
    }
    
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    
    private void OnPropertyChanged([CallerMemberName]string property = "")
    {
    if (PropertyChanged != null)
    {
    PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
    }
    

    } < / p >

  3. 在视图中附加此属性更改的处理程序

        _loginDialogViewModel = new LoginDialogViewModel();
    loginPanel.DataContext = _loginDialogViewModel;
    _loginDialogViewModel.PropertyChanged += OnPropertyChanged;
    
  4. Now you are almost done. In the event handler make DialogResult = true

    protected void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
    if (args.PropertyName == "CloseDialog")
    {
    DialogResult = true;
    }
    }
    

好吧,这个问题已经提了将近6年了,我仍然找不到我认为正确的答案,所以请允许我分享我的“2美分”……

实际上我有两种方法,第一种是简单的…右边的第二个,所以如果你正在寻找合适的,跳过#1跳到#2:

1. 简单快捷(但不完整)

如果我只有一个小项目,我有时只在ViewModel中创建CloseWindowAction:

        public Action CloseWindow { get; set; } // In MyViewModel.cs

无论谁装箱视图,或者在视图后面的代码中我设置了方法Action都会调用

(记住MVVM是关于View和ViewModel的分离…视图后面的代码仍然是视图,只要有适当的分离,你就没有违反模式)

如果某个ViewModel创建了一个新窗口:

private void CreateNewView()
{
MyView window = new MyView();
window.DataContext = new MyViewModel
{
CloseWindow = window.Close,
};
window.ShowDialog();
}

或者如果你想把它放在主窗口中,只需要把它放在视图的构造函数下面:

public MyView()
{
InitializeComponent();
this.DataContext = new MainViewModel
{
CloseWindow = this.Close
};
}

当你想关闭窗口时,只需调用ViewModel上的Action。


2. 正确的方法

现在正确的方法是使用棱镜 (IMHO),关于它的一切都可以是在这里找到

你可以创建一个互动请求,用你在新窗口中需要的任何数据填充它,午餐它,关闭它,甚至接收数据返回。所有这些都被封装并通过了MVVM。你甚至获取窗口是如何关闭的状态,就像如果用户CanceledAccepted (OK按钮)窗口和数据回来,如果你需要。它有点复杂,答案#1,但它更完整,是微软推荐的模式。

我给出的链接有所有的代码片段和示例,所以我不会在这里放置任何代码,只要阅读下载Prism快速入门的文章并运行它,它真的很容易理解,只是稍微啰嗦一点,使它工作,但好处比关闭一个窗口更大。

在你的View/任何UserControl(或你想关闭的Window)中创建一个Dependency Property。像下图:

 public bool CloseTrigger
{
get { return (bool)GetValue(CloseTriggerProperty); }
set { SetValue(CloseTriggerProperty, value); }
}


public static readonly DependencyProperty CloseTriggerProperty =
DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(ControlEventBase), new PropertyMetadata(new PropertyChangedCallback(OnCloseTriggerChanged)));


private static void OnCloseTriggerChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
//write Window Exit Code
}

并从ViewModel的财产中绑定它:

<Window x:Class="WpfStackOverflowTempProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"  Width="525"
CloseTrigger="{Binding Path=CloseWindow,Mode=TwoWay}"

属性在< em > VeiwModel < / em >:

private bool closeWindow;


public bool CloseWindow
{
get { return closeWindow; }
set
{
closeWindow = value;
RaiseChane("CloseWindow");
}
}

现在通过改变ViewModel中的CloseWindow值来触发close操作。:)

public partial class MyWindow: Window
{
public ApplicationSelection()
{
InitializeComponent();


MyViewModel viewModel = new MyViewModel();


DataContext = viewModel;


viewModel.RequestClose += () => { Close(); };


}
}


public class MyViewModel
{


//...Your code...


public event Action RequestClose;


public virtual void Close()
{
if (RequestClose != null)
{
RequestClose();
}
}


public void SomeFunction()
{
//...Do something...
Close();
}
}

行为是这里最方便的方式。

    一方面,它可以绑定到给定的视图模型(可以 信号"关闭窗体!")

  • 另一方面,它可以访问表单本身,因此可以订阅必要的特定于表单的事件,或显示确认对话框,或其他任何东西。

写作的必要行为第一次被认为是无聊的。但是,从现在开始,您可以通过精确的一行XAML片段在所需的每个表单上重用它。如果有必要,可以将其提取为单独的程序集,以便将其包含到您想要的任何下一个项目中。