WPF MVVM 关闭窗口

3

我使用MVVM创建了一个WPF应用程序,但在关闭/打开窗口方面遇到了困难。在我的登录窗口中,我使用以下方法通过按钮单击关闭登录窗口并打开WindowOPHome窗口:

            WindowOPHome dashboard = new WindowOPHome();
            dashboard.Show();
            Application.Current.MainWindow.Close();

一切都运行良好,登录窗口关闭,WindowOPHome窗口打开。当我尝试通过类似于登录窗口/WindowOPHome操作的按钮单击来关闭WindowOPHome窗口并打开WindowMainAdmin窗口时,WindowMainAdmin窗口仅在短暂的一瞬间出现,然后消失,而WindowOPHome永远不会离开视线。以下是关闭WindowOPHome和打开WindowMainAdmin的代码:

        WindowMainAdmin dashboard = new WindowMainAdmin();
        dashboard.Show();
        Application.Current.MainWindow.Close();

任何帮助都将不胜感激!如果您需要其他代码,请告诉我。非常感谢!
4个回答

4
我建议您明确关闭要关闭的窗口,而不是假设它是当前的主窗口。
使用MVVM有很多不同的方法,您可以使用附加的行为或通过命令参数将窗口传递给视图模型,如下所示:
在视图的按钮xaml中:
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"

在视图模型中的命令执行方法中:

if (parameter is System.Windows.Window)
{
    WindowMainAdmin dashboard = new WindowMainAdmin();
    dashboard.Show();
    (parameter as System.Windows.Window).Close();
}

.
或者你可以迭代所有的窗口,直到找到你想要的那个窗口。

foreach( Window window in Application.Current.Windows ) {
    if(window is WindowOPHome)
    {
        window.Close();
        break;
    }  
}

如果您需要打开多个窗口实例,除了关闭第一个类型的窗口之外,您可能还需要检查其他属性。

您甚至可以将其调整为每个窗口类中的静态关闭方法。


太好了!这非常简单,我可以适应未来的使用,就像你说的那样,当我需要保持其他窗口并选择我需要关闭的窗口时。谢谢! - Progolfer79
这是整个StackOverFlow中最简单可行的解决方案。谢谢@cjmurph - Casperonian

1
我认为当您创建管理员窗口时,程序会将其视为当前主窗口并关闭它。为了避免这种情况,您可以显式地关闭所需的窗口。我建议实现一个MainViewModel来管理所有窗口。此示例假定您只想打开一个窗口。
在视图中(任何窗口):
private void OnClose(object sender, RoutedEventArgs e)
{
    //ICommand Implemnation that informs MainViewModel of UserInput 
    //In this case, the command ShowOPHome is an Enum
    inputhandler.Execute(MyCommands.ShowOPHome);
}

在 ViewModel 中:
BaseWindow dashboard;
....
public void ShowWindow(MyCommands Param)
{
    //Verify Parameter
    ....
    if(!(dashboard is null))
        dashboard.Close();
    switch(Param)
    {
        case MyCommands.ShowOPHome:
            dashboard = new WindowOPHome();
            break;
        case MyCommands.ShowMainAdmin:
            dashboard = new WindowMainAdmin();
            break;
    } 
    dashboard.Show();

}

输入处理程序:

public class Inputhandler : ICommand
{
    ...
    public class Execute(object Data)
    {
        ...
            mainViewModel.ShowWindow(MyCommands.ShowOPHome);
        ...
    }
    ...
}

1
你可以使用一些辅助类创建一个纯的MVVM解决方案来解决这个问题。
public static class ViewCloser
{
    public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached(
        "DialogResult",
        typeof(bool?),
        typeof(ViewCloser),
        new PropertyMetadata(DialogResultChanged));

    private static void DialogResultChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
    {
        var view = target as Window;

        if (view == null)
            return;

        if (view.IsModal())
            view.DialogResult = args.NewValue as bool?;
        else
            view.Close();
    }

    public static void SetDialogResult(Window target, bool? value)
    {
        target.SetValue(DialogResultProperty, value);
    }
}


public static class WindowExtender
{
    public static bool IsModal(this Window window)
    {
        var fieldInfo = typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic);
        return fieldInfo != null && (bool)fieldInfo.GetValue(window);
    }
}

在应用程序中,首先在ViewModel中创建一个属性。
private bool? _viewClosed;

public bool? ViewClosed
{
    get { return _viewClosed; }
    set { 
            _viewClosed = value);
            RaisePropertyChanged("ViewClosed");
        }
}

然后在视图中使用我们的辅助类将其绑定。

<Window x:Class="
        ...
        vhelpers:ViewCloser.DialogResult="{Binding ViewClosed}"
        ...
        >

0
这些都是很好的解决方案!我选择了cjmurph的解决方案,因为它非常简单(适合我的头脑)并且我可以轻松地将其适应于未来的使用。请参见下面实现的代码。再次感谢大家!
XAML
Command="{Binding BtnMainAdminPageGO}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"

WindowOPMainViewModel

    private ICommand _btnMainAdminPage;

    public ICommand BtnMainAdminPageGO
    {
        get
        {
            if (_btnMainAdminPage == null)
            {
                _btnMainAdminPage = new RelayCommand(param => this.BtnMainAdminPage(), null);
            }

            return _btnMainAdminPage;
        }
    }

    private void BtnMainAdminPage()
    {
        WindowMainAdmin dashboard = new WindowMainAdmin();
        dashboard.Show();
        foreach(Window window in Application.Current.Windows)
        {
            if (window is WindowOPHome)
            {
                window.Close();
                break;
            }
        }

    }

稳住,老大!你把我给你的两个选项搞混了。将参数传递给您的命令执行方法,您不需要迭代窗口集合。在声明RelayCommand时,请执行以下操作:new RelayCommand(param => BtnMainAdminPage(param), null); 然后将执行方法更改为private void BtnMainAdminPage(object param){ WindowMainAdmin dashboard = new WindowMainAdmin(); dashboard.show(); if(param is System.windows.Window) (param as System.Windows.Window).Close();} - cjmurph

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