简体   繁体   English

MVVM showDialog与所有者并在后台工作人员完成时关闭

[英]MVVM showDialog with owner and close when background worker finishes

I'm just trying to get a grasp of MVVM pattern in WPF (currently without any framework). 我只是想了解WPF中的MVVM模式(当前没有任何框架)。

Scenario: 场景:

I have a main window, I click a button "Start work" that is bound to some command in the viewmodel. 我有一个主窗口,单击与视图模型中的某些命令绑定的“开始工作”按钮。 Progress dialog should open with "Cancel" button, it should show on the center of the owner window(so I need to pass the owner), I press cancel and I invoke "CancelAsync" method on background worker. 进度对话框应使用“取消”按钮打开,它应显示在所有者窗口的中央(因此我需要通过所有者),按取消,然后在后台工作程序上调用“ CancelAsync”方法。

The principle of MVVM is that the view model should never know anything about the view and in my case I'm violating this rule. MVVM的原理是,视图模型永远不应该对视图一无所知,在我的情况下,我违反了该规则。

Code-behind (No MVVM) solution: 后台代码(无MVVM)解决方案:

Main window part: 主窗口部分:

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();
}

Progress window part: 进度窗口部分:

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

My attempt to convert this code to MVVM (this is wrong) 我试图将此代码转换为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();
    }
}

How to solve this? 如何解决呢? I did what I wanted in code-behind in a matter of 20 minutes, I wanted to try MVVM pattern and it takes me few hours to solve simple problem. 我花了20分钟的时间完成了我想做的代码隐藏工作,我想尝试MVVM模式,花了几个小时才能解决简单的问题。

I was looking at some solutions with EventAggregator but that requires using a framework like Prism, Caliburn.Micro. 我当时在看一些使用EventAggregator的解决方案,但这需要使用Prism,Caliburn.Micro之类的框架。 So I get some kind of communication between VM and the View. 这样我就可以在VM和View之间进行某种通信。

You can pass an interface to MainViewModel which contains the needed methods 您可以将接口传递给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();
}
}

Messenger approach 信使方式

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);
                }
          }
      });
}

Action property on view model approach 视图模型方法的动作属性
1) Add action property on the viewmodel 1)在viewmodel上添加action属性
2) Wire it up in the view code behind 2)将其连接到后面的视图代码中
3) Invoke action it in the viewmodel logic where needed 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;
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM