Пузырьковый 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.
Надеюсь, это поможет, и я не упустил момент!;)