简体   繁体   中英

INotifyPropertyChange confusion

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?

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.

为确保您的窗口共享相同的视图模型,请删除 Steven 记录的代码段并在您的窗口的构造函数中调用以下代码:

DataContext = SheepViewModel.GetDetails();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM