问题
I am converting a VB.NET Winforms project to C# WPF.
I have a project with two WPF windows and a ViewModel as a referenced project.
public class SheepViewModel : INotifyPropertyChanged
{
private string _CurrentEventName;
public string CurrentEventName
{
get { return _CurrentEventName; }
set
{
_CurrentEventName = value;
OnPropertyChanged("CurrentEventName");
}
}
static SheepViewModel _details;
public static SheepViewModel GetDetails()
{
if (_details == null)
_details = new SheepViewModel();
return _details;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
Console.WriteLine(prop + " has changed");
}
}
The window where I display things looks like this.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:SheepViewModel;assembly=SheepViewModel"
xmlns:ShaderEffectLibrary="clr-namespace:ShaderEffectLibrary;assembly=ShaderEffectLibrary" x:Class="Sheep_Score_3._1.ScoreScreen"
mc:Ignorable="d"
Title="ScoreScreen" Height="540" Width="920" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<vm:SheepViewModel/>
</Window.DataContext>
<Viewbox x:Name="Stand1ViewBox" RenderTransformOrigin="0.5,0.5">
<Canvas x:Name="StandViewBox" Height="112.513" Margin="0,1100,2,0" VerticalAlignment="Bottom" RenderTransformOrigin="0.493,0.473" Background="#FF999999" Width="2268">
<TextBlock x:Name="CurrentEventName" Canvas.Left="270.9" TextWrapping="Wrap" Width="1104.815" FontFamily="Calibri" FontSize="29.333" FontStyle="Italic" Canvas.Top="-1.649" Text="{Binding Path=CurrentEventName}"/>
</Canvas>
</Viewbox>
</Window>
My MainWindow which is a control window looks like this
```xaml
<Window x:Class="Sheep_Score_3._1.MainWindow"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:SheepViewModel;assembly=SheepViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="433.689" Width="941.194">
<Window.DataContext>
<vm:SheepViewModel/>
</Window.DataContext>
<Grid Margin="0,0,0,0">
<TextBox x:Name="CurrentEventName" Height="23" Margin="131.01,163.013,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Width="327.151" Text="{Binding CurrentEventName, Mode=TwoWay}"/>
</Grid>
</Window>
If I type something into the control window text box it appears on the display window in the text block, so my bindings are working and I can see the INotifyPropertyChanged.PropertyChanged
event raised. That's all working great.
However, if I change the property programmatically, I can still see the INotifyPropertyChanged.PropertyChanged
event raised, but the text box and text block do not update with the new value.
SheepViewModel.SheepViewModel.GetDetails().CurrentEventName = "This is the new value";
Am I missing something here?
回答1:
I'm actually surprised that the text updates work for you; it definitely doesn't on my side. Here's why:
You have the following snippet in both windows:
<Window.DataContext>
<vm:SheepViewModel/>
</Window.DataContext>
This basically says: create a new instance of SheepViewModel and set it as the window's datacontext. Since you have this in both windows, each window will have a separate instance of the ViewModel. As the CurrentEventName
property is an instance property, the value won't be shared between the instances and won't update properly.
When you try to programmatically update the value, you call SheepViewModel.GetDetails()
. This creates yet another instance, totally unrelated to the ones used by the controls. Therefore, you don't see any updates.
What you want is all of the above to use one single viewmodel instance. To do this, you can set the DataContext of the windows from code behind. Instead of using above XAML snippet, you can use following constructor in your windows:
public DisplayWindow()
{
InitializeComponent();
this.DataContext = SheepViewModel.GetDetails();
}
This ensures that the windows refer to the singleton instance that is retrieved through the GetDetails
method.
回答2:
To guarantee that your windows share the same view model, remove the snippet noted by Steven and call the following in the constructor of your windows:
DataContext = SheepViewModel.GetDetails();
来源:https://stackoverflow.com/questions/35166810/inotifypropertychange-confusion