14

I am new to WPF.

I am trying to bind collection of string to combobox.

public ObservableCollection<string> ListString {get; set;}

Binding and datacontext are set as follows

<Window 
        x:Class="Assignment2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:validators="clr-namespace:Assignment2"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}, Path=.}">
    <Grid>
        <ComboBox  Height="23" HorizontalAlignment="Left" Margin="109,103,0,0" Name="StringComboBox" VerticalAlignment="Top" Width="120" SelectionChanged="StringComboBox_SelectionChanged">
            <ComboBox.ItemsSource>
                <Binding Path="ListString" BindsDirectlyToSource="True" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"></Binding>
            </ComboBox.ItemsSource>
        </ComboBox>

I came to know that this is happening because collection is updating. If I write

public MainWindow()
        {

            InputString = "";
            ListString = new ObservableCollection<string>();
            ListString.Add("AAA");
            ListString.Add("BBB");
            ListString.Add("CCC");
          InitializeComponent();

        }

It does work but if I am move InitializeComponent() above at first line as follows, it doesn't work.

  public MainWindow()
            {
               InitializeComponent();
                InputString = "";
                ListString = new ObservableCollection<string>();
                ListString.Add("AAA");
                ListString.Add("BBB");
                ListString.Add("CCC");                
            }

What Should I do??

Ganesh Satpute
  • 3,664
  • 6
  • 41
  • 78
  • 2
    One works and the other does not. I would go with the option that works. – paparazzo Oct 24 '13 at 12:56
  • 2
    @Blam I am trying to generalize this problem to solve my other problem in which list is coming from WCF service. Still you suggests to go with the option that works?? – Ganesh Satpute Oct 24 '13 at 13:46

4 Answers4

21

Solved the problem. Implemented INotifyPropertyChanged as follows

public partial class MainWindow : Window, INotifyPropertyChanged

modified the accessors as follows

    private ObservableCollection<string> listString;
    public ObservableCollection<string> ListString 
    {
        get
        {
            return listString;
        }
        set
        {
            listString = value;
            NotifyPropertyChanged("ListString"); // method implemented below
        }
    }

and added the following event and method to raise the event

public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string name)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this,new PropertyChangedEventArgs(name));
    }
}

and it works B)

JeremyK
  • 1,075
  • 1
  • 22
  • 45
Ganesh Satpute
  • 3,664
  • 6
  • 41
  • 78
1

what happens if you change your code to

<Window 
    x:Class="Assignment2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:validators="clr-namespace:Assignment2"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <ComboBox  Height="23" HorizontalAlignment="Left" Margin="109,103,0,0" Name="StringComboBox" VerticalAlignment="Top" Width="120" SelectionChanged="StringComboBox_SelectionChanged"
               ItemsSource="{Binding ListString, Mode=OneWay}"/>

cs.

  public MainWindow()
        {
           InitializeComponent();
            InputString = "";
            ListString = new ObservableCollection<string>();
            ListString.Add("AAA");
            ListString.Add("BBB");
            ListString.Add("CCC"); 

           this.DataContext=this;    
      }           

btw: setting the ItemsSource with mode=twoway makes no sense to me. your combobox will never "create a new itemssource" for your viewmodel.

EDIT: i think your first solution works because of setting the DataContext in xaml. i assume that DataContext="{Binding RelativeSource={RelativeSource Self}, Path=.}" is execute when calling InitializeComponent(); and because your ListString property is just an autoproperty and not implement INotifyPropertyChanged - your mainwindowview does not get notified that your ctor creates a new ListString property.

  public ObservableCollection<string> ListString {get{return _list;}; set{_list=value; OnPropertyChanged("ListString");}}

should work with both of your approaches, but you have to implement INotifyPropertyChanged for your MainWindow class.

blindmeis
  • 22,175
  • 7
  • 55
  • 74
  • I came to know that this is happening because of, when InitializeComponent is called it binds that control to that particular object reference AND when reference is changed itself then it doesn't work. – Ganesh Satpute Oct 24 '13 at 13:42
  • if you want set liststring=new OberservableCollection more then once, you have to implement INotifyPropertyChanged for your class. the problem with InitializeComponent(); respectively this.DataContext=this; see my EDIT – blindmeis Oct 24 '13 at 19:02
1

You can set the item source for combobox in code behind or set datacontext again after your list is populated or you can go with inotifychanged to raise property change.

public MainWindow()
        {
            InitializeComponent();
            InputString = "";
            ListString = new ObservableCollection<string>();
            ListString.Add("AAA");
            ListString.Add("BBB");
            ListString.Add("CCC");
            StringComboBox.ItemsSource = ListString;

        }
Jason
  • 143
  • 1
  • 5
  • 16
0

Seems to me the problem was "newing up" ListString. Making it a property (the selected answer) is one way around that. Alternatively inlining the instantation, or putting it before InitializeComponent I believe would be ok.

If newing up is expected to occur often it might be helpful to encapsulate the ObservableCollection in a manager class. I found this question after troubleshooting my own issues with such a setup. I got it working by implementing INotifyCollectionChanged and forwarding the event like so

/// <summary>
/// Maintains an observable (i.e. good for binding) collection of resources that can be indexed by name or alias
/// </summary>
/// <typeparam name="RT">Resource Type: the type of resource associated with this collection</typeparam>
public class ResourceCollection<RT> : IEnumerable, INotifyCollectionChanged
    where RT : class, IResource, new()
{
    public event NotifyCollectionChangedEventHandler CollectionChanged
    {
        add { Ports.CollectionChanged += value; }
        remove { Ports.CollectionChanged -= value; }
    }

    public IEnumerator GetEnumerator() { return Ports.GetEnumerator(); }

    private ObservableCollection<RT> Ports { get; set; }
    private Dictionary<string, RT> ByAlias { get; set; }
    private Dictionary<string, RT> ByName { get; set; }
}
Assimilater
  • 944
  • 14
  • 33