从 Singleton class 更改 ViewModel 未更新 WPF C# 中的可视控件

ViewModel change from Singleton class is not updating visual controls in WPF C#

我正在反序列化多个 ViewModel,但是当它们发生变化时,View 中的可视控件(即 TextBlock)没有发生变化。在尝试找出问题的根源之后,实际的反序列化不是问题,但这是我为什么在另一个 ViewModel 中有一个 ViewModel 的动机。

主要区别在于,不仅仅是 VM 中的一个 属性 正在发生变化,整个 ViewModel 也在发生变化(作为反序列化的结果) .

以下代码是显示问题的简化示例。

MainWindow.xaml - 绑定 TextBlock 和按钮

<StackPanel Orientation="Vertical">
        <TextBlock x:Name="Test" FontSize="22" Text="{Binding ExampleText}"/>
        <Button x:Name="ButtonTest" FontSize="22" Width="100" Content="Load new VM" Click="LoadNewVM"/>
</StackPanel>

MainWindow 代码隐藏

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            //create a new instance of the ExampleViewModel and load into Singleton class MainWindowViewModel
            MainWindowViewModel.Instance.ExampleViewModel = new ExampleViewModel();

            //set the data context to this Singleton instance which does support NotifyPropertyChanged
            this.DataContext = MainWindowViewModel.Instance.ExampleViewModel;
        }


        //button click event, loads a new ViewModel into the Singleton class MainWindowViewModel with different text
        private void LoadNewVM(object sender, RoutedEventArgs e)
        {
            ExampleViewModel test = new ExampleViewModel();
            test.ExampleText = "New Text";
            MainWindowViewModel.Instance.ExampleViewModel = test;
        }
    }

MainWindowViewModel - 为了确保我没有混淆 ViewModel,我将其设为 Singleton。还是不行。请注意,此 ViewModel 包含 ExampleViewModel 实例。

 class MainWindowViewModel: INotifyPropertyChanged
    {

        #region ExampleViewModel
        private ExampleViewModel exampleViewModel;

        
        public ExampleViewModel ExampleViewModel
        {
            get { return this.exampleViewModel; }
            set
            {
                if (this.exampleViewModel != value)
                {
                    this.exampleViewModel = value;
                    NotifyPropertyChanged();
                }
            }
        }
        #endregion 

        #region Make class singleton
        private static readonly MainWindowViewModel instance = new MainWindowViewModel();

        //tell c# compiler not to mark type as beforefieldinit
        static MainWindowViewModel()
        {
        }

        private MainWindowViewModel()
        {
        }

        public static MainWindowViewModel Instance
        {
            get { return instance; }
        }
        #endregion



        #region PropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] string PropertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }
        #endregion

    }

ExampleViewModel - 我省略了 Notify属性Changed 为了简洁,但它在 class.

 class ExampleViewModel : INotifyPropertyChanged
    {

        public ExampleViewModel()
        {
            ExampleText = "Initial Text";
        }


        private string exampleText;
        public string ExampleText
        {
            get { return exampleText; }
            set
            {
                if (exampleText != value)
                {
                    exampleText = value;
                    NotifyPropertyChanged();
                    MessageBox.Show("Example Text is changing to " + exampleText);
                }
            }
        }
     }

当我 运行 这样做时,TextBlock 最初会按预期显示“初始文本”。然后当我按下按钮时,它调用 LoadNewVM 方法将文本更改为“新文本”。但是,视觉控制不会改变。我什至在 ExampleText 的 属性 setter 中放置了一个 MessageBox,它确实显示文本正在更改为“新文本”。然而 View 中的 TextBlock 并没有改变。任何帮助将不胜感激。

您没有创建到 DataContext 属性 的绑定,而是从 MainWindowViewModel.Instance.ExampleViewModel 属性 分配对象引用。 然后您更改 MainWindowViewModel.Instance.ExampleViewModel 属性 的值,但由于没有绑定,DataContext 无法“发现”它。

下面是帮助您理解问题本质的代码。

            //set the data context to this Singleton instance which does support NotifyPropertyChanged
            this.DataContext = MainWindowViewModel.Instance /*.ExampleViewModel */;
<StackPanel Orientation="Vertical" DataContext="{Binding ExampleViewModel}">
        <TextBlock x:Name="Test" FontSize="22" Text="{Binding ExampleText}"/>
        <Button x:Name="ButtonTest" FontSize="22" Width="100" Content="Load new VM" Click="LoadNewVM"/>
</StackPanel>

可以在 XAML 中完成赋值。 这比用非常无用的代码重载 Window 的代码隐藏要好。

<Window -------------
        -------------
        DataContext="{Binding ExampleViewModel, Source={x:Static local:MainWindowViewModel.Instance}}">

通过此分配,不再需要设置 StackPanel.DataContext。