Контекстно-зависимая команда с MVVM

У меня есть пользовательский компонент, который в основном представляет собой текстовое поле с прикрепленной кнопкой. Кнопка должна выполнять действие над текстовым полем; например, нажатие кнопки может заполнить текстовое поле некоторой случайной строкой.

Текстовые поля привязаны к свойствам в ViewModel. В основном это выглядит так:

Три пользовательских компонента с прикрепленной кнопкой каждый

Каков наилучший способ настроить команду, общую для компонента?

Что я сделал до сих пор, так это то, что у меня есть один общий RelayCommand в моей ViewModel, который ожидает параметр. Каждая кнопка имеет свою команду, установленную для этой единственной команды, и я использую свойство CommandParameter, чтобы добавить некоторую информацию о том, о каком компоненте текстового поля я на самом деле говорю. Затем ViewModel использует эту информацию, чтобы найти правильное свойство и изменить его значение (обновление текстовых полей через привязку).

Хотя это работает нормально, мне не нравится, что мне приходится вручную вставлять информацию о соответствующем текстовом поле или контексте. В идеале я хотел бы, чтобы команда выполнялась в контекстной области, которая уже знает, о каком текстовом поле или связанном свойстве идет речь. Есть ли способ сделать это?

Еще одна проблема, с которой я столкнулся, заключается в том, что я хочу привязать действие кнопки к ключевой команде. Поэтому, когда я выделяю текстовое поле и нажимаю сочетание клавиш, я хочу, чтобы оно вело себя так, как если бы я нажал кнопку правильно, т. е. выполнил команду и передал правильную контекстную информацию. Моей альтернативой было бы поместить это в код программной части и, по сути, извлечь параметр команды из текущего фокуса, но я бы предпочел более чистое решение.

Есть ли хороший способ заставить это работать с MVVM?


person poke    schedule 04.09.2012    source источник


Ответы (2)


Как насчет чего-то в этом роде:

public class TextBoxAndCommandPresenter : INotifyPropertyChanged
{
    private readonly Action<TextBoxAndCommandPresenter> _action;

    public TextBoxAndCommandPresenter(string description,
                                      Action<TextBoxAndCommandPresenter> action)
    {
        Description = description;
        _action = action;
    }

    public string Description { get; private set; }
    public string Value { get; set; }
    public ICommand Command
    {
        get { return new DelegateCommand(() => _action(this)); }
    }
}

Используется следующим образом:

var presenter = new TextBoxAndCommandPresenter("Field 1",
                                               p => p.Value = "hello world");

С XAML:

<UserControl ...>
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type TextBoxAndCommandPresenter}">
            <StackPanel Orientation="Horizontal">
                <Label Content="{Binding Description}"/>
                <TextBox Text="{Binding Value}"/>
                <Button Command="{Binding Command}">Click</Button>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>

    <ContentPresenter Content="{Binding}"/>
</UserControl>
person Scroog1    schedule 04.09.2012
comment
Хм, а есть идеи, как решить проблему с привязкой клавиш? - person poke; 06.09.2012
comment
Вы можете добавить флаг IsSelected на презентаторе, а затем команду на родительском презентаторе, которая вызывает Command на презентере, для которого IsSelected установлено значение True, и привязать к этому нажатие клавиши. - person Scroog1; 06.09.2012

Поскольку у меня уже был собственный элемент управления для текстового поля и комбинации кнопок, создание UserControl не было для меня необходимым вариантом. Мой элемент управления предоставляет привязываемые свойства для команды кнопки и параметра команды, и на данный момент я придерживаюсь того, что объяснил в вопросе; используя параметр команды для обновления соответствующего свойства в модели представления, которое затем обновляется с помощью привязки данных.

В зависимости от того, насколько повторяющимся он станет позже, я мог бы инкапсулировать это либо в несколько пользовательских элементов управления, либо создать аналогичный помощник, как показал Scroog1.

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

Я знаю, что у меня есть стандартная команда, связанная с кодом программной части окна, которая ищет элемент, на котором в данный момент находится фокус, проверяет, относится ли он к типу моего пользовательского элемента управления, а затем просто выполняет базовую команду. Таким образом, код программной части, по сути, просто делегирует выполнение команды сфокусированному элементу управления.

Хотя это не идеальное решение, так как я бы предпочел иметь некоторую «контекстную чувствительность» для команд, на данный момент это работает нормально и по-прежнему правильно отделяет представление от логики.

person poke    schedule 27.09.2012