INotifyPropertyChange confusion

烈酒焚心 提交于 2021-02-10 19:38:21

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!