1

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

2 Answers2

2

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;
    }
}
Paul Gibson
  • 622
  • 1
  • 9
  • 23
  • Yes, assigning the view model that way is better then using a singleton with XAML binding! – JanDotNet Aug 01 '17 at 15:41
  • By app level code is this where you mean? public partial class App : Application If I put that code there, I get this error: Error CS0236 A field initializer cannot reference the non-static field, method, or property 'App.vm' –  Aug 01 '17 at 16:14
  • @BBAdamThomas Yes. I just checked some code I was thinking of when I answered, and it is in the App_Startup method in my App:Application code.I have edited my answer to show the full code so that you can see what I've done. – Paul Gibson Aug 01 '17 at 16:36
  • @PaulGibson Is it possible to get this working if the windows are opened at different times? Thanks –  Aug 02 '17 at 09:37
  • @BBAdamThomas Yes it is. What you will need to do is have a way to keep the view model reference so that you can pass it in to the window when it is created. If the first window or the view model has the method that creates the second window that works pretty easily. Otherwise the Application itself could keep a reference to the view model (perhaps not stricly MVVM, but can work). – Paul Gibson Aug 02 '17 at 21:28
1

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!

JanDotNet
  • 3,746
  • 21
  • 30
  • I'm trying your answer to see if it is better for what I want my app to do, but I can't get it to work. I'm getting the error: resource local:ViewModel.Instance could not be resolved. Any help? Thanks –  Aug 02 '17 at 10:09
  • Ups, there was an error in the XAML. Please try the updated version. – JanDotNet Aug 02 '17 at 10:14
  • Cannot find the static member 'Instance' on the type 'ViewModel' –  Aug 02 '17 at 10:50
  • hmm, have you added the `Instance` property to the view model? – JanDotNet Aug 02 '17 at 11:04
  • Fixed it, the instance needed to be static. It works exactly how I wanted it to now so thanks! Are there any downsides to using a singleton? –  Aug 02 '17 at 11:25
  • Yes: [What is so bad about singletons](https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons). But the most down side don't occure in your use case. – JanDotNet Aug 02 '17 at 11:38
  • Usually, a private constructor is added so that nobody can create another instance of the singleton. – JanDotNet Aug 02 '17 at 11:39