Reputation: 8146
I'm trying to bind an ObservableCollection
(C#) to a ComboBox
(XAML). A million articles, question/answers, and posts on the internet suggest that this is a totally simple task. So far, I have to disagree with them:
XAML
<Window
x:Class = "ComboTest.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
Title = "MainWindow"
Height = "350"
Width = "525"
DataContext = "{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<ComboBox
Name = "URICombo"
ItemsSource = "{Binding URIs}"
SelectedIndex = "0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Window>
C#
namespace ComboTest
{
public partial class MainWindow : Window
{
public class URIPairing
{
public string URI { get; set; }
public string Name { get; set; }
public URIPairing(string _Name, string _URI)
{
this.Name = _Name;
this.URI = _URI;
}
}
public ObservableCollection<URIPairing> URIs { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
this.URIs = new System.Collections.ObjectModel.ObservableCollection<URIPairing>();
this.URIs.Add(new URIPairing( "DEV" , "Some URL" ));
this.URIs.Add(new URIPairing( "SANDBOX" , "Some URL" ));
this.URIs.Add(new URIPairing( "QA" , "Some URI" ));
}
}
}
When run, the application shows a simple, empty ComboBox.
Debugging shows that the ObservableCollection
is populating correctly, and looking at Visual Studio's "Apply Data Binding..." panel, I can see that the DataContext is null and that no paths are available to bind to.
I sincerely hope that I'm not making some silly typo (I've copy/pasted every like-namespace I can find with no luck); I'm at an absolute loss, otherwise, though.
Upvotes: 0
Views: 2027
Reputation: 81323
You forgot to set DataContext
to itself:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
OR
May be in XAML:
<Window DataContext="{Binding RelativeSource={RelativeSource Self}}"/>
Of course your combobox declaration should be this:
<ComboBox ItemsSource="{Binding URIs}"/>
UPDATE (For anyone who stumbled upon this post in future)
Generally bindings are done with ViewModels, so I don't feel any UI component should implement INPC
. But, that's completely opinion based.
Anyhow if you set DataContext
from XAML, it wasn't working because XAML gets parsed with line InitializeComponent()
but at that time list wasn't initialized so binding fails silently (you can check that in Output window of VS2010, you will see binding failure message over there).
That being said, if you want it to work from XAML as well (without implementing INPC), all you have to do is initialize list before InitializeComponent()
and it will work from both sides.
public MainWindow()
{
URIs = new ObservableCollection<URIPairing>();
InitializeComponent();
this.URIs.Add(new URIPairing("DEV", "Some URL"));
this.URIs.Add(new URIPairing("SANDBOX", "Some URL"));
this.URIs.Add(new URIPairing("QA", "Some URI"));
}
Upvotes: 3
Reputation: 10865
The reason that didn't work is because you don't have INotifyPropertyChanged
implemented in your MainWindow
. When you did a binding to the DataContext
to RelativeSource Self
on the Window, initially when you run the application it tried to grab the URIs
property which is not yet set and then you reset the DataContext
in the constructor to this
which would mark it as new one and it is already late, thus you need to call INPC
.
An easy and cleaner fix is to remove either the setting of the DataContext
in the constructor or the binding in the XAML
but not both.
public partial class MainWindow : Window, INotifyPropertyChanged
{
// Implementation of the INPC
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public class URIPairing
{
public string URI { get; set; }
public string Name { get; set; }
public URIPairing(string _Name, string _URI)
{
this.Name = _Name;
this.URI = _URI;
}
}
private ObservableCollection<URIPairing> _uris;
public ObservableCollection<URIPairing> URIs { get { return _uris; } set { _uris = value; OnPropertyChanged(); } }
public MainWindow()
{
InitializeComponent();
DataContext = this;
this.URIs = new System.Collections.ObjectModel.ObservableCollection<URIPairing>();
this.URIs.Add(new URIPairing( "DEV" , "Some URL" ));
this.URIs.Add(new URIPairing( "SANDBOX" , "Some URL" ));
this.URIs.Add(new URIPairing( "QA" , "Some URI" ));
}
}
This will propagate the new value to the User Interface
.
Upvotes: 1
Reputation: 222720
You have missed the main part assigning to the combobox.
this.URIs = new stem.Collections.ObjectModel.ObservableCollection<URIPairing>();
this.URIs.Add(new URIPairing("DEV", "Some URL"));
this.URIs.Add(new URIPairing("SANDBOX", "Some URL"));
this.URIs.Add(new URIPairing("QA", "Some URI"));
URICombo.ItemsSource = URIs;
URICombo.DisplayMemberPath = "Name";
Upvotes: 1