Работая с шаблоном MVVM, у меня есть пара классов моделей представлений, представляющих двухуровневую иерархию данных, каждый из которых имеет соответствующий UserControl
, представляющий его представление. Оба класса модели представления реализуют INotifyPropertyChanged
, а модель представления корневого уровня предоставляет свойство, относящееся как к ее собственному представлению, так и к дочернему представлению.
Представление корневого уровня получает модель представления корневого уровня в качестве своего контекста данных и явно присваивает контекст данных содержащемуся в нем представлению. Однако также необходимо привязать одно из свойств дочернего представления к вышеупомянутому общему свойству. Вот как я пытался добиться этого, но это не работает:
<UserControl x:Name="rootView">
<StackPanel>
<!-- other controls here -->
<my:ChildView
DataContext="{Binding Path=SelectedChild}"
EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
</StackPanel>
</UserControl>
Хотя ошибок привязки во время выполнения нет и дочернее представление правильно привязывается к соответствующему экземпляру модели дочернего представления, его свойство EditingMode
никогда не устанавливается. Я провел тесты, чтобы убедиться, что соответствующее свойство модели представления изменяется и что оно уведомляет об этом изменении через INotifyPropertyChanged
, но привязка не может его обнаружить.
Есть ли лучший способ объявить эту привязку, или я сделал более простую архитектурную ошибку?
Большое спасибо за ваш совет,
Тим
Обновление: В соответствии с запросом я публикую некоторый код, чтобы показать очень упрощенную версию моих представлений и моделей представлений, а также результаты проведенного мной эксперимента, которые могут дать некоторые дополнительные подсказки.
// The relevant parts of the ParentViewModel class
public class ParentViewModel : INotifyPropertyChanged
{
// Although not shown, the following properties
// correctly participate in INotifyPropertyChanged
public ChildViewModel SelectedChild { get; private set; }
public ContentEditingMode EditingMode { get; private set; }
}
// The relevant parts of the ChildViewModel class
public class ChildViewModel : INotifyPropertyChanged
{
// No properties of ChildViewModel affect this issue.
}
// The relevant parts of the ParentView class
public partial class ParentView : UserControl
{
// No properties of ParentView affect this issue.
}
// The relevant members of the ChildView class
public partial class ChildView : UserControl
{
public static readonly DependencyProperty EditingModeProperty =
DependencyProperty.Register(
"EditingMode",
typeof(ContentEditingMode),
typeof(PostView)
);
public ContentEditingMode EditingMode
{
get { return (ContentEditingMode)GetValue(EditingModeProperty); }
set { SetValue(EditingModeProperty, value); }
}
}
// The enumeration used for the EditingMode property
public enum ContentEditingMode
{
Html,
WYSYWIG
}
Мое намерение состоит в том, что DataContext
экземпляра родительского представления будет назначен экземпляр ParentViewModel
, а он, в свою очередь, присвоит значение своего свойства SelectedChild
DataContext
вложенного ChildView
. Все это вроде бы работает правильно, но возникает проблема, потому что привязка между ParentViewModel.EditingMode
и ChildView.EditingMode
не работает.
В попытке проверить, есть ли проблема с моим выражением привязки, я ввел TextBlock
рядом с ChildView
и привязал его аналогично свойству ParentViewModel.EditingMode
:
<UserControl x:Name="rootView">
<StackPanel>
<!-- other controls here -->
<TextBlock Text="{Binding ElementName=rootView, Path=DataContext.EditingMode}" />
<my:ChildView
DataContext="{Binding Path=SelectedChild}"
EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
</StackPanel>
</UserControl>
В этом тесте TextBlock
корректно обновляется каждый раз, когда изменяется исходное свойство. Однако, если я установлю точку останова на установщике ChildView.EditingMode
, она никогда не сработает.
Я сбит с толку!
SetValue
. Если вы хотите отслеживать настройку свойств зависимостей, вам необходимо реализовать функцию обратного вызова и использовать перегрузкуRegister
, которая позволяет указать ее. - person Robert Rossney   schedule 03.07.2010