带有所有者的MVVM showDialog,当后台工作者完成时关闭

时间:2018-04-01 15:42:35

标签: c# wpf mvvm

我只是想在WPF中掌握MVVM模式(目前没有任何框架)。

情境:

我有一个主窗口,我点击一个按钮“开始工作”,该按钮绑定到viewmodel中的某个命令。进度对话框应该打开“取消”按钮,它应该显示在所有者窗口的中心(所以我需要通过所有者),我按下取消,我在后台工作者上调用“CancelAsync”方法。

MVVM的原则是视图模型永远不应该知道关于视图的任何信息,在我的情况下我违反了这条规则。

代码隐藏(无MVVM)解决方案:

主窗口部分:

private void Button_Click(object sender, RoutedEventArgs e)
{
    backgroundWorker.RunWorkerAsync();

    progressWindow = new ProgressWindow(backgroundWorker);
    progressWindow.Owner = this;
    progressWindow.ShowDialog();
}

private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    progressWindow.Close();
}

进度窗口部分:

private void btnCancel_Click(object sender, RoutedEventArgs e)
{
    backgroundWorker.CancelAsync();
}

我尝试将此代码转换为MVVM(这是错误的)

public class MainViewModel
{
    public ICommand DoSomething { get; }
    private BackgroundWorker backgroundWorker;

    private PleaseWaitView pleaseWaitView;

    public MainViewModel()
    {
        backgroundWorker = new BackgroundWorker() { WorkerSupportsCancellation = true };
        backgroundWorker.DoWork += BackgroundWorker_DoWork;
        backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;

        var pleaseWaitViewModel = new PleaseWaitViewModel(backgroundWorker);
        pleaseWaitView = new PleaseWaitView();
        pleaseWaitView.Owner = Application.Current.MainWindow;
        pleaseWaitView.DataContext = pleaseWaitViewModel;

        DoSomething = new ActionCommand<object>(DoSomethingImpl);
    }

    private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        pleaseWaitView.Close();
    }

    private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        // Some work
        Thread.Sleep(5000);
    }

    private void DoSomethingImpl(object parameter)
    {
        pleaseWaitView.ShowDialog();
    }
}

如何解决这个问题?我在20分钟内做了我想要的代码隐藏,我想尝试MVVM模式,我需要几个小时来解决简单的问题。

我正在使用EventAggregator查看一些解决方案,但这需要使用像Prism,Caliburn.Micro这样的框架。所以我在VM和View之间进行了某种沟通。

3 个答案:

答案 0 :(得分:1)

您可以将接口传递给MainViewModel,其中包含所需的方法

interface IMainView
{
    void Init(PleaseWaitViewModel viewModel);
    void ShowDialog();
    void Close();
}

public class MainViewModel
{
     private IMainView _view;
     public MainViewModel(IMainView view)
     {
         _view = view;


        backgroundWorker = new BackgroundWorker() { WorkerSupportsCancellation = true };
        backgroundWorker.DoWork += BackgroundWorker_DoWork;
        backgroundWorker.RunWorkerCompleted += 
        BackgroundWorker_RunWorkerCompleted;

        var pleaseWaitViewModel = new PleaseWaitViewModel(backgroundWorker);
        _view.Init(pleaseWaitViewModel);
     }

    private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    _view.Close();
}

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // Some work
    Thread.Sleep(5000);
}

private void DoSomethingImpl(object parameter)
{
    _view.ShowDialog();
}
}

答案 1 :(得分:1)

使用Messenger方法

public class PersonsViewModel
{
        private RelayCommand _addPersonCommand = null;
        public RelayCommand AddPersonCommand
        {
            get
            {
                return _addPersonCommand ?? (_addPersonCommand = new RelayCommand(
                    () =>
                    {
                        Action<Person> callback = (person) =>
                        {
                            _persons.Add(person);
                            RaisePropertyChanged("Persons");
                        };

                        Messenger.Default.Send<NotificationMessageAction<Person>>(new NotificationMessageAction<Person>(this, new Person(), "myNotification", callback), this);          
                    }));
            }
        }
}

private PersonsViewModel _viewModel = null;
public PersonsView()
{
     InitializeComponent();

     DataContext = _viewModel = new PersonsViewModel();
     Messenger.Default.Register<NotificationMessageAction<Person>>(this, _viewModel, message => 
     {
          if(message.Notification == "myNotification")
          {
                Person person = (Person)message.Target;
                Action<Person> callback = message.Execute;
                ModalView view = new ModalView(person);
                if(true == view.ShowDialog())
                {
                      callback.Invoke(view.Person);
                }
          }
      });
}

答案 2 :(得分:1)

视图模型方法的动作属性
1)在viewmodel上添加action属性
2)将其连接到后面的视图代码中 3)在viewmodel逻辑中调用它所需的动作

    using System;
    using System.Windows;
    using System.Windows.Input;

    namespace WpfApp1
    {
        /// <summary>
        ///     Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();

                // Wire up CancelAction in the View
                var windowToClose = new Window();
                var castedContext = (ViewModel) DataContext;
                castedContext.CancelAction = () => windowToClose.Close();
            }
        }

        public class ViewModel
        {
            private ICommand _doSomethingCommand;
            public Action CancelAction { get; set; }

            public ICommand DoSomethingCommand
            {
                get
                {
                    if (_doSomethingCommand != null)
                        return _doSomethingCommand;

                    _doSomethingCommand = new MyCommandImplementation(() =>
                    {
                        // Perform Logic

                        // If need to cancel - invoke cancel action
                        CancelAction.Invoke();
                    });
                    return _doSomethingCommand;
                }
            }
        }

        // Stubbed out for the sake of complete code
        public class MyCommandImplementation : ICommand
        {
            public MyCommandImplementation(Action action)
            {
                throw new NotImplementedException();
            }

            public bool CanExecute(object parameter)
            {
                throw new NotImplementedException();
            }

            public void Execute(object parameter)
            {
                throw new NotImplementedException();
            }

            public event EventHandler CanExecuteChanged;
        }
    }