从 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。
我正在反序列化多个 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。