How to bind data with combobox - c#

I need to know in the simplest form, how to bind data (list of string) to a ComboBox in XAML without using ComboBox.ItemSource = object in the code behind.
I mean what is this:
{Binding Path="What Comes here"}
let's say I have:
class Window1 : UserControl {
List<String> list = new List<String>();// And assign Value to this list
...}
I've tried
{Binding Path=list} or {Binding list}
but nothing has been bind. So how should it be done? (I can't access this combobox in the code behind because Microsoft has limited SilverLight DatGrid to an extent that I can't do it)

Few rules for binding to work properly:
You can bind only with public properties (at least for instance objects) and not with fields.
Be default binding engine looks for property path in DataContext of control where binding is applied on.
If you want to bind to property which doesn't exist in DataContext (or DataContext is not set for control), use RelativeSource markup extension to guide binding engine to resolve property path.
Coming back to your problem statement where you need to bind to List created in code behind, you have to do following changes to code:
// Not sure why you termed it as Window1 if it's actually an UserControl.
public partial class Window1 : UserControl
{
public Window1()
{
InitializeComponent();
MyStrings = new List<string>(new[] { "A", "B", "C", "D" });
DataContext = this; // Rule #2
}
public List<string> MyStrings { get; set; } // Rule #1
}
XAML:
<ComboBox ItemsSource="{Binding MyStrings}"/>
In case you don't set DataContext in constructor of UserControl and still want to bind to property, bind using RelativeSource (Rule #3)
<ComboBox ItemsSource="{Binding MyStrings, RelativeSource={RelativeSource FindAncestor
, AncestorType=UserControl}}"/>
Additional Points
Use ObservableCollection<string> in place of List<string> in case you want to add more items to the list after initialization and want UI to update accordingly.
Read more about MVVM here - Understanding the basics of MVVM pattern. Generally all binding stuff stay in separate class ViewModel so that it can be tested w/o any dependency on UI stuff.

Take a look over here: Model-View-ViewModel (MVVM) Explained
I'm not a SilverLight developer but as far as I know it's just a downstiped version of full wpf.
Try using a ViewModel and define your list in there. Then you can bind the ComboBox.ItemsSource property to it:
public class SomeViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> listOfAwesomeStrings;
public ObservableCollection<string> ListOfAwesomeStrings
{
get { return listOfAwesomeStrings; }
set
{
if (value.Equals(listOfAwesomeStrings))
{
return;
}
listOfAwesomeStrings= value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Set the DataContext of your view like this and also fill your list:
var viewModel = new SomeViewModel();
viewModel.ListOfAwesomeStrings = new ObservableCollection<string>();
viewModel.ListOfAwesomeStrings.Add("Option 1");
viewModel.ListOfAwesomeStrings.Add("Option 2");
viewModel.ListOfAwesomeStrings.Add("Option 3");
this.DataContext = viewModel;
Finally bind to your property in xaml:
<ComboBox ItemsSource="{Binding ListOfAwesomeStrings}" />

Related

Binding ComboBox ItemsSource does not work in WPF

This is kinda strange cause every example I found there says I'm doing things the right way yet I was unable to get my ComboBox binding to work in WPF.
I just created an empty WPF Application.
public List<string> myCollection { get; set; }
public MainWindow()
{
DataContext = this;
InitializeComponent();
myCollection = new List<string> {"test1", "test2", "test3", "test4"};
}
And here is my xaml for this:
<Window x:Class="WpfApplication2.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">
<Grid>
<ComboBox ItemsSource="{Binding Path=myCollection}" Height="23" HorizontalAlignment="Left" Margin="66,56,0,0" Name="comboBox1" VerticalAlignment="Top" Width="319" />
</Grid>
I have tried Binding myCollection, Binding Path=myCollection, I have tried with and without setting DataContext.
Nothing seems to be working.
I have run out of ideas and every example I find out there says this is the correct way and it should be working so thanks for any help i advance.
Set the datacontext after InitializeComponent
InitializeComponent();
myCollection = new List<string> { "test1", "test2", "test3", "test4" };
DataContext = this;
at the end of your constructor
comboBox1.ItemsSource = myCollection;
Sajeetheran answer works because the initialize of the XAML objects looks at the current state and binds to what is there at that time but will fail if one changes the property to something else. I would term that a one-time work-around.
I just wanted to make this using bindings
For most WPF scenarios one wants to use the INotifyPropertyChange mechanision to allow for dynamic changes to be handled by the XAML bindings. If one wants to truly use the power of bindings it goes hand in hand with INotifyPropertyChange. Otherwise Dimitri's answer is just as valid as Sajeetharan's.
The binding does not know of the change because the reference of myCollection does not notify the world of a change in status; hence the data doesn't get displayed.
To facilitate such notification the class used to hold the property needs to adhere to INotifyPropertyChanged and the property myCollection needs to send a notify event. (Note in your case its the main window which is technically workable, but in the MVVM paradigm one wants to seperate the view from the dat and a ViewModel class is used to hold the actual data and provide this notificaiton).
public MainWindow : INotifyPropertyChanged
Then provide the event that will be subscribed to by the binding targets to the item on the DataContext :
public event PropertyChangedEventHandler PropertyChanged;
Then the mechanism to provide the change event.
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the property that has changed.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Then provide the change for the myCollection
private List<string> _myCollection;
public List<string> myCollection
{
get { return _myCollection; }
set { _myCollection= value; OnPropertyChanged("myCollection"); }
}
public List<string> myCollection { get; set; }
public MainWindow()
{
myCollection = new List<string> {"test1", "test2", "test3", "test4"};
DataContext = this;
InitializeComponent(); //-- call it at the end
}
You have to InitializeComponent after assigning data context.

Binding ObservableCollection in viewmodel to listbox

I'm very new to MVVM and bindings and I'm trying to learn to work with it.
I run into the problem of binding my viewmodel to the view in particular binding an observable collection to a listbox.
this is what my viewmodel looks like:
namespace MyProject
{
using Model;
public class NetworkViewModel: INotifyPropertyChanged
{
private ObservableCollection<Person> _networkList1 = new ObservableCollection<Person>();
public ObservableCollection<Person> NetworkList1 //Binds with the listbox
{
get { return _networkList1; }
set { _networkList1 = value; RaisePropertyChanged("_networkList1"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public NetworkViewModel()
{
_networkList1 = new ObservableCollection<Person>()
{
new Person(){FirstName="John", LastName="Doe"},
new Person(){FirstName="Andy" , LastName="Boo"}
};
}
}
in the view I have
namespace MyProject
{
public partial class Networking : Window
{
public Networking()
{
InitializeComponent();
this.DataContext = new NetworkViewModel();
lb1.ItemsSource = _networkList1;
}
}
}
and in the XAML I have
<ListBox x:Name="lb1" HorizontalAlignment="Left" ItemsSource="{Binding NetworkList1}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock >
<Run Text="{Binding Path=FirstName}"/>
<Run Text="{Binding Path=LastName}"/>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It seems like you might have a typo in your view model.
RaisePropertyChanged("_networkList1");
You want to raise the property changed notification for the public property not the private variable.
RaisePropertyChanged("NetworkList1");
This might be preventing your view from updating properly.
In addition to Gaurav answer, if _networkList1 is a private field in your NetworkViewModel class, how is it possible to get access to it in Networking window? I mean what's the meaning of the following line?
lb1.ItemsSource = _networkList1;
when you define a Property (NetworkList1), you have to use it in order to get advantages of its features (e.g. to get RaisePropertyChanged working). Otherwise what's the point, you could have just defined a field (_networklist1). So changing
_networkList1 = new ObservableCollection<Person>()
to
NetworkList1 = new ObservableCollection<Person>()
results in actually setting NetworkList1 and therefore RaisePropertyChanged("NetworkList1") to be fired. (however if you want to just show data in a your listbox this is unnecessary)
and if i'm getting it right, changing this:
public partial class Networking : Window
{
public Networking()
{
InitializeComponent();
this.DataContext = new NetworkViewModel();
lb1.ItemsSource = _networkList1;
}
}
to
public partial class Networking : Window
{
public NetworkViewModel MyViewModel { get; set; }
public Networking()
{
InitializeComponent();
MyViewModel = new NetworkViewModel();
this.DataContext = MyViewModel;
}
}
should get your binding to work.
*Note that when you set DataContext to NetworkViewModel, then the binding in
<ListBox x:Name="lb1" HorizontalAlignment="Left" ItemsSource="{Binding NetworkList1}">
works, because NetworkList1 is a Property of NetworkViewModel.
Do not call RaisePropertyChanged() method on ObservableCollection<T>, for god's sake. This is a common mistake in a majority of cases (however, there are cases, where you need to reset ObservableCollection<T> using new keyword, but they are kinda rare).
This is a special type of collection which notifies UI internally about all the changes of its content (like add, remove etc.). What you need is to set the collection using new keyword once in a lifetime of your ViewModel, and then manipulate your items via Add(T item), Remove(T item), Clear() methods etc.
and UI will get notified about it and updated automatically.

WPF UserControl Binding Problem

I like to create a UserControl with own Header Property.
public partial class SomeClass: UserControl, INotifyPropertyChanged
{
public SomeClass()
{
InitializeComponent();
}
private string header;
public string Header
{
get { return header; }
set
{
header = value;
OnPropertyChanged("Header");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
in UserContol xaml:
Label Name="lbHeader" Grid.Column="0" Content="{Binding Path=Header}"
If I set the value: AA2P.Header = "SomeHeeaderText"; than the label.Caption will not changed. How can I solve that problem?
In Windows xaml:
uc:SomeClass x:Name="AA2P"
If I give directly a value to label (lbHeader.Content = header;) instead of OnPropertyChanged("Header"); its work but, why it does not work with OnPropertyChanged?
I need to use DataContext for somethig else. I try to use dependency property but something is wrong.
public partial class tester : UserControl
{
public tester()
{
InitializeComponent();
}
public string Header
{
get { return (string)GetValue(MyDependencyProperty); }
set { SetValue(MyDependencyProperty, value); }
}
public static readonly DependencyProperty MyDependencyProperty =
DependencyProperty.Register("MyDependencyProperty", typeof(string), typeof(string));
}
<UserControl ... x:Name="mainControl">
<TextBlock Text="{Binding ElementName=mainControl, Path=MyDependencyProperty}"/>
</UserControl>
<Window ...>
<my:tester Header="SomeText" />
</Window>
It does not work. What I do wrong?
Thanks!
The easiest approach is to just the DataContext of your object. One way of doing that is directly in the constructor like this:
public SomeClass()
{
InitializeComponent();
DataContext = this;
}
Setting the DataContext will specify where new data should be fetched from. There are some great tips and information in the article called WPF Basic Data Binding FAQ. Read it to better understand what the DataContex can be used for. It is an essential component in WPF/C#.
Update due to update of the question.
To my understanding you should change the first argument of DependencyProperty.Register to the name of the property that you want to bind to, here "Header" as well as the second argument to the type of your class, here SomeClass. That would leave you with:
public static readonly DependencyProperty MyDependencyProperty =
DependencyProperty.Register("Header", typeof(SomeClass), typeof(string));
But i seldom use dependency properties so I am not positive that this is it, but its worth a try..
If you need the Data context for something else. You can also utilize the ElementName property in the Binding.
<UserControl
x:Class="MyControl.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="mainControl">
<TextBlock Text="Binding ElementName=mainControl, Path=MyDependencyProperty}"/>
</UserControl>
[Edit]
I should add something. Make the "Header" property a dependency property, this will make your live much easier. In UI Controls you should make property almost always a dependency property, every designer or user of your control will thank you.
The UserControl itself needs the DataContext of where it is used later. But the controls inside the UserControl need the UserControl as their DataContext, otherwise they also will inherit the DataContext from the later usage context. The trick is to set the DataContext of the UserControl's child to that of the UserControl, so it now can use the dependency properties of the UserControl.
<UserControl x:Class="MyControl.MyUserControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=UserControl,AncestorLevel=1}}">...</Grid>
</UserControl>
If you do this this way the children of the Grid can have simple {Binding dp's name} without additionally ElementName parameters.

WPF ItemsSource works in code-behind but not in XAML

I have a simple combobox with a checkbox inside as such:
<ComboBox Height="23" HorizontalAlignment="Left" Margin="158,180,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" ItemsSource="{Binding collection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding Name}"></CheckBox>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The datacontext is simply the code behind, and to test it I use the following code:
public ObservableCollection<Foo> collection { get; set; }
private void button1_Click(object sender, RoutedEventArgs e)
{
collection = new ObservableCollection<Foo>();
this.comboBox1.ItemsSource = collection;
Foo f = new Foo("DSD");
collection.Add(f);
}
When I set the ItemsSource as I have in the code, then it works fine, but I want to set the ItemsSource in the Xaml, however it does not work using the Xaml above. I have also tried setting it to Path = "". Anybody know why?
Thanks
You need to assign DataContext to the control. something like:
var window = new Window1();
window.DataContext = new WindowDC();
window.Show();
where Window1 class contains the combobox, and WindowDC is like:
public class WindowDC
{
public ObservableCollection<Foo> collection { get; set; }
}
That's how this will work.
What you actually do is that you place collection into control class, and set your datacontext for combobox only.
But, for testing purposes, you can still set Combox.Datacontext in control constuctor.
Bindings in WPF always have a Source. If you don't specify the source in the binding itself, then it will implicitly use the DataContext of the control or an ancestor of it. So if you want to bind to properties in your codebehind file, you have to set the DataContext to an object of the class which contains the collection property. In your case this is the instance of the Window (this).
DataContext = this;
As the commentor pointed out, it's not considered good style putting business logic or data inside the code behind file. So consider writing a separate class which contains your collection property and which you can use to initalize your DataContext. If you are writting bigger applications you should take a look at patterns like MVVM, which uses databinding to provide a better separation between your view and your model.
Edit: Changed ordering and incorporated feedback
Make sure there exist a public property collection in your code behind.
in the code behind also do this.DataContext = this
Finally implement INotifyPropertyChanged to tell the view that you have changed the collection once you add items in it
public ObservableCollection<Foo> Collection
{
get
{
return collection;
}
set
{
collection = value;
OnPropertyChanged("Collection");
}
private void button1_Click(object sender, RoutedEventArgs e)
{
collection = new ObservableCollection<Foo>();
//this.comboBox1.ItemsSource = collection;
Foo f = new Foo("DSD");
collection.Add(f);
OnPropertyChanged("Collection");
}
It is working when you are setting combo's item source in code behind because the source of combo is getting updated like wise to set the item source in XAML you have to make a property with INotifyPropertyChanged that keep update the combo's itemsource every time you update your collection via this property..
private ObservableCollection<Foo> _Collection;
public ObservableCollection<Foo> Collection
{
get
{
return collection;
}
set
{
collection = value;
OnPropertyChanged("Collection");
}
Now as you are filling collection on button click you just have to set that collection in the property as..
_Collection = new ObservableCollection<Foo>();
Foo f = new Foo("DSD");
_Collection .Add(f);
Collection = _Collection ; //here property call OnPropertyChange
like wise you can provide data to any control. It is jsut the game of INotifyPropertyChanged property.
Hope this will help you

Bind a control to a single value in a collection/array in WPF

In WPF I have a collection of bool? values and I want to bind each of these to a separate checkbox programmatically. I want the bindings to be TwoWay so that changing the value of the individual item in the collection in code updates the check box and vice versa.
I have spent ages trying to figure out how to do this and I am completely stuck. With the following code the checkbox only gets the right value when the window is loaded and that's it. Changing the check box doesn't even update the value in the collection. (UPDATE: this appears to be a bug in .NET4 as the collection does get updated in an identical .NET3.5 project. UPDATE: Microsoft have confirmed the bug and that it will be fixed in the .NET4 release.)
Many thanks in advance for your help!
C#:
namespace MyNamespace
{
public partial class MyWindow : Window, INotifyPropertyChanged
{
public MyWindow()
{
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public List<bool?> myCollection = new List<bool?>
{ true, false, true, false, true, false };
public List<bool?> MyCollection
{
get { return myCollection; }
set { myCollection = value; }
}
}
}
XAML:
<CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}">
There are a few things that need changing here to get this to work. Firstly you'll need to wrap your boolean value in an object that implements the INotifyPropertyChanged interface in order to get the change notification that you are looking for. Currently you are binding to boolean values in your collection which do not implement the interface. To do this you could create a wrapper class like so :
public class Wrapper: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool val = false;
public bool Val
{
get { return val; }
set
{
val = value;
this.OnPropertyChanged("Val");
}
}
public Wrapper(bool val)
{
this.val = val;
}
}
You'll then want to create these objects in your form instead of a list of booleans. You may also want to use an observable collection instead of a list so that notification of items being added and removed are sent. This is shown below:
public Window1()
{
InitializeComponent();
this.DataContext = this;
}
private ObservableCollection<Wrapper> myCollection = new ObservableCollection<Wrapper>()
{new Wrapper(true), new Wrapper(false), new Wrapper(true)};
public ObservableCollection<Wrapper> MyCollection
{
get { return myCollection; }
}
The next thing to do is to display a list of check boxes in your ui. To do this WPF provides itemscontrols. ListBox is an itemscontrol so we can use this as a starting point. Set the itemssource of a listbox to be MyCollection. We then need to define how each Wrapper object is going to be displayed in the list box and this can be done with a datatemplate which is created in the windows resources. This is shown below :
<Window.Resources>
<DataTemplate x:Key="myCollectionItems">
<CheckBox IsChecked="{Binding Path=Val, Mode=TwoWay}"></CheckBox>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Path=MyCollection}" ItemTemplate="{StaticResource myCollectionItems}"></ListBox>
</Grid>
This should get you up and running with a simple demo of checkboxes that have values bound to a list of booleans.
What makes you think it's not working? It's working for me :)
Here's my test XAML:
<UniformGrid>
<CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}"/>
<ListBox ItemsSource="{Binding MyCollection}"/>
<Button Content="Test" Click="Button_Click"/>
</UniformGrid>
Here's my code behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
}
(the rest is the same as yours)
I placed a breakpoint on Button_Click and checked MyCollection[0] it was updated according to the IsChecked value of the CheckBox.
Try changing your collection type from List<bool?> to ObservableCollection<bool?> perhaps that is the reason you think it's not working for you (the fact that changes to the collection are not reflected anywhere else in your view).
Change your List<bool?> to an ObservableCollection<bool?>. A List does not raise the change notifications that WPF needs to update the UI. An ObservableCollection does. This handles the case where the list entry is changed and the CheckBox needs to update accordingly.
In the other direction, it works for me even with a List<bool?> -- i.e. toggling the checkbox modifies the value in the collection. Your binding syntax is certainly correct.

Categories

Resources