44

所以我的第一次尝试是从后面的代码中完成所有事情,现在我正在尝试重构我的代码以使用 MVVM 模式,遵循盒子信息中 MVVM 的指导。

我已经创建了一个视图模型类来匹配我的视图类,并且我正在将代码从后面的代码中移到从命令开始的视图模型中。

我的第一个障碍是尝试实现一个“关闭”按钮,如果数据没有被修改,它会关闭窗口。我已经安装了一个 CloseCommand 来替换 'onClick' 方法,除了代码尝试运行的位置之外,一切都很好this.Close()。显然,由于代码已从窗口移至普通类,“this”不是窗口,因此不可关闭。但是,根据 MVVM,viewmodel 不知道视图,所以我不能调用view.Close().

有人可以建议我如何从 viewmodel 命令关闭窗口吗?

4

12 回答 12

67

我个人使用一种非常简单的方法:对于与可关闭视图相关的每个 ViewModel,我创建了一个基本 ViewModel,如下例所示:

public abstract class CloseableViewModel
{
    public event EventHandler ClosingRequest;

    protected void OnClosingRequest()
    {
        if (this.ClosingRequest != null)
        {
            this.ClosingRequest(this, EventArgs.Empty);
        }
    }
}

然后在继承自的 ViewModel 中,CloseableViewModel只需调用命令即可。this.OnClosingRequest();Close

在视图中:

public class YourView
{
    ...
    var vm = new ClosableViewModel();
    this.Datacontext = vm;
    vm.ClosingRequest += (sender, e) => this.Close();
}
于 2012-08-14T08:31:42.133 回答
29

您不需要将 View 实例传递给您的 ViewModel 层。您可以像这样访问主窗口 -

Application.Current.MainWindow.Close()

如上所述,我认为在 ViewModel 类中访问您的主窗口没有问题。根据 MVVM 原则,您的 View 和 ViewModel 之间不应存在紧密耦合,即它们应该在工作时忽略其他操作。在这里,我们没有从 View 向 ViewModel 传递任何东西。如果您想寻找其他选项,这可能会对您有所帮助 -使用 MVVM 关闭窗口

于 2012-08-14T08:09:23.777 回答
27

我在单击按钮时从视图模型中关闭窗口的解决方案如下:

在视图模型中

public RelayCommand CloseWindow;
Constructor()
{
    CloseWindow = new RelayCommand(CloseWin);
}

public void CloseWin(object obj)
{
    Window win = obj as Window;
    win.Close();
}

在View中,设置如下

<Button Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=WindowNameTobeClose}" Content="Cancel" />
于 2013-08-29T06:33:00.817 回答
13

我通过创建一个名为 DialogResult 的附加属性来做到这一点:

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 && (bool?)e.NewValue == true) 
                window.Close();
    }

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

然后在窗口标签中将这个写给你 XAML

WindowActions:DialogCloser.DialogResult="{Binding Close}"

终于在 ViewModel

    private bool _close;
    public bool Close
    {
        get { return _close; }
        set
        {
            if (_close == value)
                return;
            _close = value;
            NotifyPropertyChanged("Close");
        }
    }

如果将 Close 更改为 true,则窗口将关闭

Close = True;
于 2012-08-14T07:16:18.610 回答
6

这里是最简单纯粹的 MVVM 方案

视图模型代码

public class ViewModel
{
    public Action CloseAction { get; set; }

    private void CloseCommandFunction()
    {
        CloseAction();
    }
}

这是 XAML 查看代码

public partial class DialogWindow : Window
{
    public DialogWindow()
    {
        ViewModel vm = new ViewModel();
        this.DataContext = vm;

        vm.CloseAction = new Action(() => this.Close());
    }
}
于 2017-09-07T12:56:06.940 回答
5

该解决方案快速简便。缺点是层之间存在一些耦合。

在您的视图模型中:

public class MyWindowViewModel: ViewModelBase
{


    public Command.StandardCommand CloseCommand
    {
        get
        {
            return new Command.StandardCommand(Close);
        }
    }
    public void Close()
    {
        foreach (System.Windows.Window window in System.Windows.Application.Current.Windows)
        {
            if (window.DataContext == this)
            {
                window.Close();
            }
        }
    }
}
于 2013-10-02T13:48:34.017 回答
4

MVVM-light 带有自定义消息通知以避免窗口处理每个通知消息

在视图模型中:

public class CloseDialogMessage : NotificationMessage
{
    public CloseDialogMessage(object sender) : base(sender, "") { }
}

private void OnClose()
{
    Messenger.Default.Send(new CloseDialogMessage(this));
}

在窗口构造函数中注册消息:

Messenger.Default.Register<CloseDialogMessage>(this, nm =>
{
    Close();
});
于 2017-01-21T07:33:20.320 回答
2

这与 eoldre 的回答非常相似。它在功能上是相同的,因为它通过相同的 Windows 集合查找具有视图模型作为其数据上下文的窗口;但我使用了 RelayCommand 和一些 LINQ 来实现相同的结果。

public RelayCommand CloseCommand
{
    get
    {
        return new RelayCommand(() => Application.Current.Windows
            .Cast<Window>()
            .Single(w => w.DataContext == this)
            .Close());
    }
}
于 2014-06-03T21:06:20.883 回答
2

使用 MVVM-light 工具包:

在视图模型中:

 public void notifyWindowToClose()
{
    Messenger.Default.Send<NotificationMessage>(
        new NotificationMessage(this, "CloseWindowsBoundToMe")
    );
}

在视图中:

 Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
    if (nm.Notification == "CloseWindowsBoundToMe")
    {
        if (nm.Sender == this.DataContext)
            this.Close();
    }
});
于 2016-07-19T10:15:16.397 回答
0

这取自 ken2k 答案(谢谢!),只需将CloseCommand也添加到 baseCloseableViewModel中。

public class CloseableViewModel
{
    public CloseableViewModel()
    {
        CloseCommand = new RelayCommand(this.OnClosingRequest);
    }

    public event EventHandler ClosingRequest;

    protected void OnClosingRequest()
    {
        if (this.ClosingRequest != null)
        {
            this.ClosingRequest(this, EventArgs.Empty);
        }
    }

    public RelayCommand CloseCommand
    {
        get;
        private set;
    }
}

您的视图模型,继承它

public class MyViewModel : CloseableViewModel

然后在你查看

public MyView()
{
    var viewModel = new StudyDataStructureViewModel(studyId);
    this.DataContext = viewModel;

    //InitializeComponent(); ...

    viewModel.ClosingRequest += (sender, e) => this.Close();
}
于 2014-08-27T20:32:20.157 回答
0

给个方法,请检查

https://stackoverflow.com/a/30546407/3659387

简短的介绍

  1. 从 INotifyPropertyChanged 派生您的 ViewModel
  2. 在 ViewModel 中创建一个可观察的属性 CloseDialog,每当您想关闭对话框时更改 CloseDialog 属性。
  3. 在视图中为此属性更改附加一个处理程序
  4. 现在你几乎完成了。在事件处理程序中使 DialogResult = true
于 2015-05-30T13:10:31.800 回答
0

首先给你的窗口起一个名字,比如

x:Name="AboutViewWindow"

在我的关闭按钮上,我定义了命令和命令参数,例如

CommandParameter="{Binding ElementName=AboutViewWindow}"
Command="{Binding CancelCommand}"

然后在我的视图模型中

private ICommand _cancelCommand;        
public ICommand CancelCommand       
{
   get          
     {
        if (_cancelCommand == null)
           {
              _cancelCommand = new DelegateCommand<Window>(
                    x =>
                    {
                        x?.Close();
                    });
            }

            return _cancelCommand;          
     }      
}
于 2017-10-12T20:28:19.890 回答