views:

239

answers:

2

I have been wrestling with getting databinding to work in WPF for a little over a week. I did get valuable help here regarding the DataContext, and I did get databinding to work via DependencyProperties. While I was learning about databinding, I came across numerous discussions about INotifyPropertyChanged and how it is better than DPs in many ways. I figured that I would give it a shot and try it out.

I am using Josh Smith's base ViewModel class and my ViewModel is derived from it. However, I'm having a bit of trouble getting databinding to work, and am hoping that someone here can tell me where I'm going wrong.

In my ViewModel class, I have an ObservableCollection. In my GUI, I have a combobox that is bound to this OC, i.e.

<ComboBox ItemsSource="{Binding PluginNames}" />

The GUI's DataContext is set to the ViewModel, i.e.

private ViewModel _vm;

public GUI()
{
  InitializeComponent();
  _vm = new ViewModel();
  this.DataContext = _vm;
}

and the ViewModel has the OC named "PluginNames":

public class ViewModel
{
  public ObservableCollection<string> PluginNames;  // this gets instantiated and added to elsewhere
}

When the GUI is loaded, a method is called that instantiates the OC and adds the plugin names to it. After the OC is modified, I call RaisePropertyChanged( "PluginNames"). I was expecting that since the WPF databinding model is cognizant of INotifyPropertyChanged, that this is all I needed to do and it would "magically work" and update the combobox items with the plugins that got loaded... but it doesn't.

Can someone please point out what I've done wrong here? Thanks!

UPDATE: I'm not sure why, but now instead of not doing any apparent updating, it's not finding the property at all. I think I'm being really stupid and missing an important step somewhere.

+4  A: 

It looks like you've exposed field not property. Bindings works for properties only... Change it to:

public class ViewModel
{
  public ObservableCollection<string> PluginNames {get; private set;} 
}
Anvaka
Don't use automatic properties - they don't raise the PropertyChanged event.
itowlson
absolutely right. argh! I'll get better at this stuff eventually. :)
Dave
Is the right SO etiquette here to accept the oldest correct answer? I assume so.
Dave
You can use automatic properties for an `ObservableCollection<T>`. The setter should probably be private though.
sixlettervariables
sixlettervariables: that's true **if** you never instantiate a new collection -- but in that case you should be using a `readonly` field and read-only property instead.
itowlson
@D. Matsumoto: You should choose the most useful and complete "correct" answer, not merely the oldest. If the answers were equally helpful, THEN I'd pick the oldest.
Reed Copsey
@itowlson: I find using auto properties absolutely fine if it's readonly. I've updated the answer to reflect this.
Anvaka
+6  A: 

When you're working with INotifyPropertyChanged, there are two things:

  1. You'll need to use properties, not fields
  2. You should always raise the property changed event when you set hte properties.

You'll want to rework this so it looks more like:

private ObservableCollection<string> pluginNames;
public ObservableCollection<string> PluginNames
{
    get { return pluginNames; }
    set {
        this.pluginNames = value;
        RaisePropertyChanged("PluginNames"); // This should raise the PropertyChanged event - use whatever your VM class does for this
    }
}

That should cause everything to repopulate.

Reed Copsey
In fact, there's probably no reason to have a setter on the property, since it's a collection (and an observable one, at that).
Matt Hamilton
oh jeez. That's right! thank you. Sorry for my stupidity.
Dave
Yeah - but he purposely mentioned that he was setting this at runtime, so I included that.
Reed Copsey
Reed, I like the way you presented that... I definitely should expose the setter and call RaisePropertyChanged there, like all of the other examples I've seen today. Perfect!
Dave
@D. Matsumoto: This is the standard way to handle properties when you're using INotifyPropertyChanged. Depending on the ViewModel base class you're using, the exact syntax may change a bit (RaisePropertyChanged can be done in different ways), but you'll want to follow this general pattern. Also, it's common to only raise the property changed if the new value is different than the original ...
Reed Copsey
awesome, sounds good. Once I get more comfortable with this, I'll try to advance to the "stringless" way to deal with property names -- I think people are using expression trees to get compiler errors if the symbols don't match up.
Dave
You can basically use expression trees to avoid the "magic strings". It doesn't give you compiler errors, but just eliminates the need for the text ("PluginNames")
Reed Copsey