user8400471
user8400471

Reputation:

WPF DataBinding not updating with INotifyPropertyChanged

I'm trying to create a very simple data binding app for practice but I can't get it to work, I've looked at a lot of different solutions but none of them help and I can't figure out the problem.

MainWindow.xaml:

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>

<Grid>
    <TextBlock Text="{Binding BindText, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>

Window1.xaml:

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>

<Grid>
    <TextBox Text="{Binding BindText, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>

ViewModel:

using System.ComponentModel;

namespace bindtest
{
    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string bindText = "Hello";
        public string BindText
        {
            get { return bindText; }
            set
            {
                bindText = value;
                OnPropertyChanged("BindText");
            }
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

The text displays correctly when it first loads but then won't update. The text in MainWindow is meant to update when the text in window1 changes. Any solutions? Thanks

Upvotes: 2

Views: 359

Answers (2)

JanDotNet
JanDotNet

Reputation: 4016

Since you are creating your view model via:

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>

you have 2 distinct instances of the view models. You have to bind the same instance of your view models against the views.

How to bind the same instance against 2 views?

The simplest way in your case is, to create a singleton:

public class ViewModel : INotifyPropertyChanged
{
     public ViewModel Instance {get; } = new ViewModel();
     // ....
}

and bind to it:

<Window DataContext="{Binding Source={x:Static local:ViewModel.Instance}}" /* ... */>

Note that it is not the best way....


You should use PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); or

var handler = PropertyChanged;
if (handler != null) 
    handler(this, new PropertyChangedEventArgs(propertyName)

to ensure that the handler wasn't unsubscribed beween checking for null and invoking the event handler!

Upvotes: 1

Paul Gibson
Paul Gibson

Reputation: 634

As JanDotNet suggests, you need to use a single instance of the view model. So in your app level code for instance you would do something like:

public partial class App : Application
{
    private void App_Startup(object sender, StartupEventArgs e)
    {
        try
        {
            ViewModel vm = new ViewModel();
            MainWindow w = new MainWindow(vm);
            Window1 w1 = new Window1(vm);
            w.Show();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

And then your window constructors modified like so:

public partial class MainWindow : Window
{
    pulic MainWindow(ViewModel vm)
    {
         InitializeComponent();
         DataContext = vm;
    }
}

Upvotes: 2

Related Questions