WPF: Команды и отношение ViewModels в MVVM - PullRequest
4 голосов
/ 09 апреля 2011

Я пытаюсь вызвать одну и ту же команду из двух разных моделей представлений, но я застрял при их создании (как модели команд, так и модели представлений).

Сначала я создал ViewModel1 класс модели вида:

public class ViewModel1 : DependencyObject
{
    ...

    // The command property
    public ProcessMyString ProcessMyStringCommand { get; set; }

    public ViewModel1()
    {
        // Command gets instantiated 
        this.ProcessMyStringCommand = new ProcessMyString(this);
    }

    internal void ProcessMyString()
    {
        // This is where the actual processing method is called
        // somewhere from the business logic...
        ...
    }

И класс команд ProcessMyString:

public class ProcessMyString : ICommand
{
    private ViewModel1 viewModel;

    public ProcessMyString(ViewModel1 viewModel)
    {
        this.viewModel = viewModel;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

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

    public void Execute(object parameter)
    {
        viewModel.ProcessMyString();
    }
}

Затем я создал второй класс модели представления ViewModel2, но когда понял, что для этой модели представления также потребуется использовать ту же команду , ее конструктор

public ProcessMyString(ViewModel1 viewModel)

не будет работать, потому что он принимает параметр ViewModel1, и мне нужно иметь возможность передавать обе модели представления. Затем я решил создать класс ViewModelBase и сделать так, чтобы обе модели представления были расширены. Конечно, я также изменил конструктор команды:

// Constructor's parameter is now ViewModelBase
public ProcessMyString(ViewModelBase viewModel)

Но это означало, что метод команды Execute(object parameter) теперь вызывает метод из ViewModelBase. Это не хороший подход к приложениям, поскольку вызовы ViewModel для ProcessMyString() должны быть зарезервированы только для классов ViewModel1 и ViewModel2. Если бы у меня был класс ViewModel3, я бы не хотел, чтобы он звонил ProcessMyString(), и если бы я не расширил его с ViewModelBase, это было бы хорошо.

Но что произойдет, если мне понадобится команда, которая разделяется между ViewModel2 и ViewModel3?

Суммирующий вопрос: как мне организовать свои команды и модели представления, чтобы модели представления могли использовать одни и те же команды?

Ответы [ 2 ]

7 голосов
/ 09 апреля 2011

Во-первых, в качестве личного предпочтения я стараюсь минимизировать количество наследования, которое я использую с моими ViewModel. Сложный код пользовательского интерфейса в нетривиальных приложениях может быть достаточно сложным, чтобы любой, кроме оригинального автора, мог следовать за ним. Последнее, что вы хотите сделать, это усложнить его, добавив сложную объектную модель.

Прелесть WPF с использованием интерфейса ICommand заключается в том, что вы должны иметь возможность предпочесть более композиционный подход, а не модель наследования, и использовать интерфейс для обмена общими свойствами.

Вот краткий обзор того, как я могу подойти к этому сценарию:

public class ProcessStringCommand : ICommand
{
   private readonly IProcessStringViewModel m_viewModel;

   public ProcessStringCommand(IProcessStringViewModel vm)
   {
      m_viewModel = vm;
   }

   public void Execute(object param)
   {
      ProcessString(m_viewModel.ProcessString);
   }

   public bool CanExecute(object param)
   { 
      return true; 
   }

   private void ProcessString(string processString)
   {
      // Put logic here
   }
}

public interface IProcessStringViewModel
{
   public string ProcessString { get; }
}

public class ViewModel1 : ViewModelBase, IProcessStringViewModel
{
   private readonly ICommand m_command;
   private readonly string m_processString;

   public ViewModel1()
   {
      m_command = new ProcessStringCommand(this);
   }

   public string ProcessString
   {
      get { return m_processString; }
   }

   public ICommand ProcessStringCommand
   {
      get { return m_command; }
   }
}

public class ViewModel2 : ViewModelBase, IProcessStringViewModel
{
   private readonly ICommand m_command;
   private readonly string m_processString;       

   public ViewModel2()
   {
      m_command = new ProcessStringCommand(this);
   }

   public string ProcessString
   {
      get { return m_processString; }
   }

   public ICommand ProcessStringCommand
   {
      get { return m_command; }
   }
}

public class ViewModel3 : ViewModelBase
{
   // Whatever you need here.
}
1 голос
/ 09 апреля 2011

Я опубликую этот ответ с предположением, что класс ProcessMyString не нужен и должен быть заменен универсальной командой.

Прежде всего, скачайте библиотеку MVVM Light . После этого распакуйте его куда-нибудь и добавьте ссылку на эту библиотеку:

(папка с библиотекой) \ Mvvm Light Инструментарий \ Binaries \ WPF4 \ GalaSoft.MvvmLight.WPF4.dll

Он содержит класс RelayCommand, который вам нужен.

Сначала создайте базовый класс, содержащий вашу команду:

public abstract class ProcessStringViewModel : DependencyObject
{
    // The command property
    public RelayCommand ProcessMyStringCommand { get; set; }
}

Я бы удалил наследование из класса DependencyObject, но, может быть, вы его как-нибудь используете, так что пусть будет.

Класс ViewModel1 можно переписать следующим образом:

public class ViewModel1 : ProcessStringViewModel
{
    public ViewModel1()
    {
        // Command gets instantiated 
        this.ProcessMyStringCommand = new RelayCommand(() => this.ProcessMyString());
    }

    internal void ProcessMyString()
    {
    }
}

Класс ViewModel2 может вызывать другую функцию, но команда такая же:

public class ViewModel2 : ProcessStringViewModel
{
    public ViewModel2()
    {
        this.ProcessMyStringCommand = new RelayCommand(SomeOtherFunction);
    }

    private void SomeOtherFunction()
    {
        MessageBox.Show("Call of some function");
    }
}

Если вы решили не использовать базовый класс и наследование - вы можете удалить базовый класс, скопировать свойство в каждый производный класс, и оно будет работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...