Пузырьковый RoutedEvent или RoutedCommand для ViewModel

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

public class TabViewModel : DependencyObject
{
      public object Content
      {
          get { return (object)GetValue(ContentProperty); }
          set
          {
              SetValue(ContentProperty, value);
          }
      }

}

Вот TabControl:

<TabControl 
     ItemsSource={Binding MyCollectionOfTabViewModels}" 
     ItemContainerStyle="{StaticResource TabItemStyle}" />

А вот и стиль

<Style TargetType="TabItem" x:Key="TabItemStyle">
     <Setter Property="Content" Value="{Binding Content}"/>
</Style>

Мы создаем экземпляр usercontrol и устанавливаем для свойства "Content" свойства TabViewModel так, чтобы usercontrol отображался в области содержимого TabItem.

MyCollectionOfViewModels.Add(new TabViewModel() 
{ 
     Content = new MyUserControl();
});

Мой вопрос заключается в том, что я хотел бы разрешить MyUserControl (или любому из его субэлементов управления), добавленному в свойство Content элемента TabViewModel, разрешить вызывать событие, которое обрабатывает TabViewModel.

Кто-нибудь знает, как я это сделаю?

Мы экспериментировали с использованием RoutedEvents и RoutedCommands, но не смогли заставить что-либо работать на 100% и обеспечить его совместимость с MVVM. Я действительно думаю, что это можно сделать с помощью RoutedEvent или RoutedCommand, но я не могу заставить это работать.

Примечание: я удалил часть соответствующего специфичного для Prism кода, но если вам интересно, почему мы делаем что-то настолько глупое, то это потому, что мы пытаемся сохранять независимость от контроля с помощью Prism RegionManager.

3 ответа

Решение

Вы можете добавить свойство State к вашему TabViewModel и проверить события DependencyPropertyChanged.

Итак, представьте следующее перечисление:

public enum TabViewModelState
{
    True,
    False,
    FileNotFound
}

Затем добавьте свойство State в вашу TabViewModel этого перечисления:

public static readonly DependencyProperty StateProperty =
    DependencyProperty.Register("State", typeof(TabViewModelState), typeof(TabViewModel), new PropertyMetadata(OnStateChanged));

private static void OnStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    TabViewModel viewModel= (TabViewModel)obj;

    //Do stuff with your viewModel
}

Используйте двустороннюю привязку к этому свойству в вашем элементе управления:

<CheckBox Checked="{Binding Path=State, Converter={StaticResource StateToBooleanConverter}, Mode=TwoWay}" />

И, наконец, что не менее важно, реализуйте конвертер, который будет преобразовывать исходное значение, необходимое для элемента управления, в исходное значение и обратно (в моем примере boolean <-> TabViewModelState):

public class StateToBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        TabViewModelState state = (TabViewModelState) value;
        return state == TabViewModelState.True;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool result = (bool) value;
        return result ? TabViewModelState.True : TabViewModelState.False;
    }
}

Итак, теперь у вас есть свойство State, которое управляется пользовательским интерфейсом и генерирует измененные события, когда вам нужно ответить.

Надеюсь это поможет!

Вы должны взглянуть на поведение присоединенной команды Марлона Греча, которое позволяет вам присоединить команду ViewModel к событию GUI.

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

Если бы у вас была команда делегата "GoCommand" в вашей ViewModel, привязка вашей кнопки UserControls выглядела бы так:

<Button Command="{Binding GoCommand}" >Go</Button>

Я прошел через тот же мыслительный процесс, думая, что UserControl должен выдать команду, но это не так - привязка просто подключится, когда найдет команду в ViewModel.

Надеюсь, это поможет, и я не упустил момент!;)

Другие вопросы по тегам