WPF DependencyProperty OnPropertyChanged возвращает значение по умолчанию как NewValue, если используется DataTemplate для UserControl - PullRequest
0 голосов
/ 03 апреля 2020

У меня есть приложение, в котором я устанавливаю содержимое 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
{
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...