2

I have a combobox in my MainWindow.xaml file like so:

<ComboBox Name="material1ComboBox" 
          HorizontalAlignment="Center" 
          MinWidth="100" 
          ItemsSource="{Binding ViewProperties.MaterialDropDownValues}" 
          SelectedValue="{Binding ViewProperties.Material1SelectedValue}"
          SelectionChanged="Material1ComboBoxSelectionChanged">
 </ComboBox>

I've assigned the datacontext in the codebehind using this.datacontext = this.

I created a ViewProperties that is accessed as a property in the MainWindow and is a class that implements INotifyPropertyChanged and contains the MaterialDropDownValues as a property.

I even changed the the MaterialDropDownValues to be an ObservableCollection.

The problem is that the databinding works on initialisation however if the MaterialDropDownValues property is changed the combobox values are not updated.

I have the following in the ViewProperties class:

    public ObservableCollection<string> MaterialDropDownValues 
    { 
        get { return this.materialDropDownValues; }
        set 
        { 
            this.materialDropDownValues = value;

            OnPropertyChanged("MaterialDropDownValues");
        } 
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

Any ideas why this is not working? All the other answers I could find advised to implement INotifyPropertyChanged and make the property an observablecollection.

Steve Greatrex
  • 15,789
  • 5
  • 59
  • 73
Seth
  • 8,213
  • 14
  • 71
  • 103
  • 2
    Note that if you're just adding/removing data from the list, you shouldn't rebind the whole collection. You should just do something like: `materialDropDownValues.Remove(value);`. – Merlyn Morgan-Graham Oct 11 '11 at 22:10
  • In the past, when I have had this problem it has sometimes been caused by other, (code behind) code programatically setting the ItemsSource directly (thereby killing the binding). – bigtlb Oct 14 '11 at 01:05
  • Note: The entire list can change at once it is not just individual items that need to be added or removed. – Seth Oct 14 '11 at 22:42

5 Answers5

1

Solution 1:

Dont recreate this.materialDropDownValues try to do

 this.materialDropDownValues.Clear();
 foreach(var mystring in myStrings) 
       this.materialDropDownValues.Add(mystring);

for all new items. If this doesnt work then try solution 2...

Solution 2:

As per my experience, I think ObservableCollection of primitive types like int, string, bool, double etc. does not refresh on Property Change notification if ItemsControl.ItemTemplate is not specified.

   <ComboBox Name="material1ComboBox"
             HorizontalAlignment="Center"
             MinWidth="100"
             ItemsSource="{Binding ViewProperties.MaterialDropDownValues}"
             SelectedValue="{Binding ViewProperties.Material1SelectedValue}"
             SelectionChanged="Material1ComboBoxSelectionChanged">
         <ComboBox.ItemTemplate>
             <DataTemplate DataType="{x:Type System:String}">
                 <TextBlock Text="{Binding}" />
             </DataTemplate> 
         </ComboBox.ItemTemplate>
    </ComboBox>

This is because the itemscontrol's items container creates non-observable item containers in it for primitive data by simply copying item.ToString(). In the code above the {Binding} should update the data changes when the whole items source is changed.

Let me know if this works.

WPF-it
  • 19,625
  • 8
  • 55
  • 71
0

Posting this in case anyone else runs into this. I came up this as the best search result matching my symptoms, but it turns our that none of the answers worked above for me.

I was using WinUI3 and apparently it uses the newer x:Bind syntax for it's XAML. Apparently x:Bind defaults it's Mode to OneTime which is why it wouldn't update after the first value (I also tried Binding but couldn't get that to work) From: <TextBlock Text="{x:Bind MyField}" x:Phase="1" Margin="0,5,0,5"/> To: <TextBlock Text="{x:Bind MyField, Mode=OneWay}" x:Phase="1" Margin="0,5,0,5"/>

So if you are using x:Bind, make sure set Mode=OneWay AND implement INotifyPropertyChanged and then things should work

0

When I bump into things like this, the first thing I do is play around with the binding mode. Something like:

 ItemsSource="{Binding Path=ViewProperties.MaterialDropDownValues, Mode=TwoWay}"

That sometimes trips you up. The other thing I would make sure of is that if you're instantiating new ViewProperties object(s) following your initial load, you notify change on that. If you don't, the XAML will be referring to an outdated version of the object while your code behind/view model is operating on a different one.

Erik Dietrich
  • 6,080
  • 6
  • 26
  • 37
  • I doubt this has anything to do with the direction of the binding - `ItemsSource` will default to `OneWay`, which would at least mean that the items would get displayed. As a side note, if you look at the Debug window in Visual Studio whilst running it will generally show you a (benign) error message if a `Binding` cannot be resolved for some reason, so this is a pretty good first diagnostic step – Steve Greatrex Oct 11 '11 at 21:38
0

Edit in response to comments

None of the below solved the problem, but is left as a reference.


Original Answer

The problem is that you have not specified the DataContext for your view, which is where WPF looks for Binding values by default.

Provided that your ViewProperties property on MainWindow is public you can simply change your binding to:

ItemsSource="{Binding ViewProperties.MaterialDropDownValues, 
    RelativeSource={RelativeSource FindAncestor, AncestorType=Window}"

This causes WPF to look for the property value on the first occurence of Window that it finds above the combobox in the visual tree.

Alternatively, you can just set the Window.DataContext property to your instance of ViewProperties and change the binding to the following:

ItemsSource="{Binding MaterialDropDownValues}"

Either of these will work, but I would suggest using the latter as it is closer to the MVVM pattern that is generally preferred in WPF/XAML applications.

Steve Greatrex
  • 15,789
  • 5
  • 59
  • 73
  • I did specify the datacontext to be the MainWindow in code (as per answer). However, you are saying I haven't specified the right datacontext for that binding? – Seth Oct 11 '11 at 21:37
  • Ah yes, you did mention that. I've never seen an example of setting `this.DataContext = this` before, so I can't say whether or not it should be expected to work. In any case, either of the methods I described should resolve the problem. – Steve Greatrex Oct 11 '11 at 21:39
  • Just done a quick test, and `this.DataContext = this` seems to work fine for me... – Steve Greatrex Oct 11 '11 at 21:43
  • It works the first time for me, it's only when the property gets changed the dropdown values don't update. Does it work in both cases for you? – Seth Oct 11 '11 at 21:56
  • Yes, they both work fine, but I think I spotted the problem...see my updated answer – Steve Greatrex Oct 11 '11 at 22:01
  • Steve +1 for your help. However, I don't think I articulated this clearly in my question but I had only implemented INotifyPropertyChanged for just the ViewProperties and not the MainWindow as well. Therefore, it had to be notified when the ViewProperties member of the MainWindow changed and not just members of that class changed. – Seth Oct 11 '11 at 22:14
  • Taking the reference to PropertyChanged is not the issue: I was unable to recreate Seth's issue even with it in place. Taking the reference is important for thread safety; otherwise something that previously subscribed to the event could unsubscribe between the null check and calling the handler, resulting in a null reference exception. – DocMax Oct 11 '11 at 23:29
  • Note: don't take a reference to the event handler - Taking a reference is idiomatic. http://stackoverflow.com/questions/3757234/question-on-eventhandler-from-josh-smiths-mvvm-sample-application – Ritch Melton Oct 12 '11 at 00:14
0

what happens if you change your xaml to

<ComboBox Name="material1ComboBox" 
      HorizontalAlignment="Center" 
      MinWidth="100" 
      DataContext="{Binding ViewProperties}"
      ItemsSource="{Binding MaterialDropDownValues}" 
      SelectedValue="{Binding Material1SelectedValue}"
      SelectionChanged="Material1ComboBoxSelectionChanged">
</ComboBox>

nevertheless you should just instantiate your collection once and just use remove, add and clear when you use a OberservableCollection.

blindmeis
  • 22,175
  • 7
  • 55
  • 74