У меня есть приложение, в котором я устанавливаю содержимое contentpresenter в зависимости от типа данных с помощью таблицы данных (см. MainWindow). Datatemplate - это пользовательский контроль, который на самом деле указывает тип данных c. (Небольшой пример ниже предназначен только для демонстрации, но в моем «реальном» приложении пользователь сможет переключаться между различными данными.)
У usercontrol (UserControl1) есть свойство DependencyProperty, которому я присваиваю значение (в мое приложение это на самом деле привязка к виртуальной машине, просто для простоты установите ее в пример). Установка значения все еще работает нормально. Однако в моем UserControl мне нужно реагировать на изменения свойства DependencyProperty, чтобы изменить представление моего UserControl (или более поздней версии CustomControl). Поэтому я реализовал метод OnPropertyChangend
. При запуске приложения OnPropertyChanged
работает так, как я ожидаю, и я получаю «правильное» новое значение моего DependencyProperty. Однако, если я изменю свою виртуальную машину (т.е. изменения таблицы данных) во время выполнения, нажав кнопку, OnPropertyChanged
вернет значение по умолчанию для DependencyProperty. В моем небольшом примере приложения я вижу, что значение установлено правильно, поскольку содержимое Textblock изменяется на правильное значение. Только кажется, что OnPropertyChanged
запускается до того, как значение моего DependencyProperty получит новое значение. Поэтому я не могу реагировать на новое значение.
Не совсем понятно, почему это происходит. Кажется, что-то связано с порядком, в котором WPF разрешает внутреннее содержимое? У кого-нибудь есть подсказка, как я могу исправить это поведение и получить доступ к текущему / последнему значению при смене виртуальной машины и не пропустить обновление? Как было сказано выше, мне нужно отреагировать на это значение.
Может быть, я делаю здесь что-то совершенно глупое. Подход, который я решил использовать здесь, плох? Являются ли DataTemplates неправильным подходом для переключения между двумя парами? Что будет лучшим подходом тогда? Тем не менее, я думаю, что не удастся избежать DependencyProperty и UserControl в моем приложении.
MainWindow.xaml
<!--MainWindow.xaml -->
<Grid>
<StackPanel>
<Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<ContentPresenter Content="{Binding ActiveVM}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type local:VM1}">
<local:UserControl1 MyProperty="Test1"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:VM2}">
<local:UserControl1 MyProperty="Test2"/>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</StackPanel>
</Grid>
MainWindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
vmParent = new VMParent();
DataContext = vmParent;
var vm1 = new VM1();
var vm2 = new VM2();
}
VMParent vmParent;
private void Button_Click(object sender, RoutedEventArgs e)
{
vmParent.ChangeActiveVM();
}
}
UserControl1.xaml
<!--UserControl1.xaml -->
<TextBlock Text="{Binding MyProperty, RelativeSource={RelativeSource AncestorType={x:Type local:UserControl1}}}"/>
UserControl1.cs
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(UserControl1), new PropertyMetadata("DefaultString", OnMyPropertyChangend));
private static void OnMyPropertyChangend(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == "DefaultString")
{
;
//xxxxxx
//unexpectedly i get stuck here
//Would expect/need NewValue to be Text1/Text2 to react to it
//xxxxxx
}
}
}
VMParent
class VMParent : INotifyPropertyChanged
{
public VMParent()
{
vm1 = new VM1();
vm2 = new VM2();
ActiveVM = vm1;
}
public event PropertyChangedEventHandler PropertyChanged;
VM1 vm1;
VM2 vm2;
public object ActiveVM
{
get => m_activeVM;
set { m_activeVM = value; OnPropertyChanged("ActiveVM"); }
}
private object m_activeVM;
protected internal void OnPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
public void ChangeActiveVM()
{
if (ActiveVM is VM1)
ActiveVM = vm2;
else
ActiveVM = vm1;
}
}
ВМ используются только для применения Datatemplate
class VM1
{
}
class VM2
{
}