Можно ли использовать ICommand в модели представления

В большинстве приложений WPF mvvm мы используем ICommand в модели представления. Но это относится к System.Windows.Input. поэтому модель представления теперь тесно связана с пространством имен System.Windows.Input. согласно моему пониманию, модель просмотра должна быть в состоянии использовать в обычном приложении C# winform или приложении asp.net.

Обычно мы используем следующие строки кода для команды с реализацией RelayCommand.

private RelayCommand testCommand;// or private ICommand testCommand;

public ICommand TestCommand
{
    get
    {
        return testCommand ?? 
            (testCommand = new RelayCommand(param => Test()));
    }
}

public void Test()
{

}

Я чувствую, что нам нужно удалить все ICommand и вместо этого использовать RelayCommand. Таким образом, мы можем исключить пространство имен System.Windows из модели представления. поэтому окончательный код будет выглядеть так,

private RelayCommand testCommand;

public RelayCommand TestCommand
{
    get
    {
        return testCommand ?? 
            (testCommand = new RelayCommand(param => Test()));
    }
}

public void Test()
{

}

Любые предложения по этому подходу? или есть ли способ исключить пространство имен System.Windows из модели представления?


person Chamika Sandamal    schedule 12.03.2013    source источник
comment
@BoltClock ICommand это System.Windows.Input.ICommand.   -  person Reed Copsey    schedule 12.03.2013
comment
@Reed Copsey: /facepalm Как я мог это пропустить.   -  person BoltClock    schedule 12.03.2013
comment
ICommand находится внутри System.Windows.Input   -  person Chamika Sandamal    schedule 12.03.2013
comment
Есть какие-нибудь предложения по этому подходу? не волноваться так сильно?   -  person    schedule 12.03.2013
comment
Вам повезет, если вы сможете повторно использовать ViewModel между технологиями XAML (WPF, Silverlight, WinRT, Phone), поэтому вероятность их использования между платформами, не поддерживающими ICommand, крайне мала. Я бы не беспокоился об этом.   -  person Cameron MacFarland    schedule 13.03.2013


Ответы (3)


Любые предложения по этому подходу?

Это по-прежнему не отделяет вас от System.Windows.Input, поскольку RelayCommand по-прежнему должен реализовывать ICommand, даже если он реализует его косвенно.

Реализация ICommand в ViewModel является одной из тех вещей, которые обычно требуются для того, чтобы быть прагматичными. В идеале ICommand (или аналогичный интерфейс) должен быть реализован в пространстве имен, не зависящем от XAML. При этом он поддерживается непосредственно в Portable Class Library, поэтому он не привязан к конкретному фреймворку (WPF, Silverlight, Phone и т. д.) так сильно, как XAML в целом.

person Reed Copsey    schedule 12.03.2013
comment
Переносимые библиотеки классов доступны только в Visual Studio 2012. :( - person Chamika Sandamal; 12.03.2013
comment
@ChamikaSandamal Они работают в VS 2010 с инструментами переносимых библиотек. См.: msdn.microsoft.com/en -нас/библиотека/vstudio/ - person Reed Copsey; 12.03.2013

Довольно просто избежать связывания ViewModel с ICommand, если хотите. Вероятно, это неплохая идея, WPF, вероятно, когда-нибудь пойдет по пути MFC. Перебор? возможно, но вот а как:

На ваш взгляд:

<StackPanel>
    <Button Command="{Binding Path=MyCommand}"> Do it! Kill me Now!</Button>
    <TextBlock Text="{Binding Path=Message}"></TextBlock>
</StackPanel>

Вставьте свою ViewModel в свой DataContext, возьмите на себя ответственность за собственные команды из вашей модели представления:

public class ViewModel : INotifyPropertyChanged
{
    public string Message { get; set; }
    public object MyCommand { get; set; }


    public void OnMyCommand(object parameter)
    {
        Message += "I Ran something" + Environment.NewLine;
    }

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

    // Injected Native Command handler
    public ViewModel(ICommandFactory factory)
    {
        MyCommand = factory.CreateInstance(OnMyCommand, CanMyCommand);
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Примечание. Я использую FODY для вплетать обработчик изменения свойства. Кстати, INotifyPropertyChanged — это System.dll.

Теперь свяжите этот контракт:

public interface ICommandFactory
{
    object CreateInstance(Action<object> action, Func<object, bool> predicate);
}

... к чему-то, что даст вам собственный объект Command;

public class NativeCommand : ICommand
{
    private readonly Action<object> _action;
    private readonly Func<object, bool> _predicate;

    public NativeCommand(Action<object> action, Func<object, bool> predicate)
    {
        _action = action;
        _predicate = predicate;
    }

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

    public void Execute(object parameter)
    {
        _action(parameter);
    }

    public event EventHandler CanExecuteChanged;
}


public class NativeCommandFactory : ICommandFactory
{
    public object CreateInstance(Action<object> action, Func<object, bool> predicate)
    {
        return new NativeCommand(action, predicate);
    }
}

Bind<ICommandFactory>().To<NativeCommandFactory>();

Вуаля, несвязанные команды.

работает

Также обратите внимание, что ваша инъекция выполняется при первоначальном запуске приложения. Ваша ViewModel отделена от любого выбранного вами контейнера IoC.

person Meirion Hughes    schedule 12.03.2013
comment
FODY — это большая библиотека, и я не в восторге от ее использования. спасибо за предложение. :) - person Chamika Sandamal; 13.03.2013
comment
Хм, это только для краткости. Вы можете сами реализовать обработчики изменения свойств... О, и fody - это не библиотека, это IL Weaving... ни связи, ни ссылки. - person Meirion Hughes; 13.03.2013

Ну, в теории вы во многом правы. Было бы хорошо, если бы ICommand был полностью независимым от платформы пользовательского интерфейса.

Но с практической точки зрения, если вы используете MVVM в приложении WPF, есть большая вероятность, что вы в любом случае достаточно зависите от возможностей привязки данных и шаблонов данных WPF. Попытка вставить пользовательский интерфейс WinForms поверх чего-то подобного, скорее всего, потребует значительных дополнительных усилий.

В прошлом я работал над некоторыми довольно крупными проектами WPF/MVVM. Мы считали MVVM способом отделения конкретных деталей пользовательского интерфейса от кода — не для того, чтобы мы могли переключиться на WinForms/ASP.NET/что-то еще, а для того, чтобы мы могли изменить внешний вид нашего пользовательского интерфейса (т.е. отредактировать XAML) без необходимости изменять ViewModels. В этом отношении MVVM работал отлично.

Если вас действительно беспокоит совместное использование кода в нескольких типах проектов, возможно, лучше попытаться поместить ваш общий код в типичную библиотеку классов типа «Бизнес-уровень», а не в модель представления.

person Marty    schedule 12.03.2013
comment
Я склонен придерживаться аналогичного подхода. Одна вещь, которую я пытаюсь рассмотреть, заключается в том, создает ли конкретная «зависимость пользовательского интерфейса» препятствие для модульного тестирования модели представления. RelayCommand не создает здесь проблем. Однако такие вещи, как внедрение DispatcherTimer, потенциально могут. - person Dan Bryant; 13.03.2013