Binding ComboBox ItemsSource does not work in WPF - c#

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.

Related

WPF ItemsControl doesn't refresh when I use PropertyChanged

I have list of strings and a property for it, only with get that returns List<string>. So, when I add something to my list and call OnPropertyChanged("NameOfProperty") it does not refresh my ItemsControl in the view, but when I add something in the constructor it works.
MainWindow
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowModel();
}
MainWindowModel C#
private static List<string> messages = new List<string>();
public List<string> Messages
{
get
{
return messages;
}
}
// ...
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// ...
public void foo()
{
messages.Add("Hi");
OnPropertyChanged("Messages");
}
MainWindow XAML
<ScrollViewer Grid.Row="0" Grid.Column="1" Margin="10, 0, 0, 0" Grid.ColumnSpan="2">
<ItemsControl ItemsSource="{Binding Messages, Mode=OneWay}"/>
</ScrollViewer>
The expression
messages.Add("Hi");
does not change the value of the Messages property, and unless the property value has not actually changed, the PropertyChanged event is ignored.
In order to update the UI when a collection is modified (i.e. elements are added, moved or removed), the collection needs to implement the INotifyCollectionChanged interface. The framework provides the ObservableCollection<T> class that implements this interface.
public ObservableCollection<string> Messages { get; }
= new ObservableCollection<string>();
and just
Messages.Add("Hi");
Collection Properties which should notify the framework, that items were added or deleted must implement the interface INotifyCollectionChanged.
A default implementation is found in the ObservableCollection.
So you could change your Messages Property Type to ObservableCollection instead of List.
Changing lists to ObservableCollection should be enough.

How to bind data with combobox

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}" />

ListBox bound to List is not updated

This is the simplest example of a program that demonstrates the problem. I need to bind a List to ListBox. Yes, the data structure must be a list, not an ObservableCollection (but if it's needed, I can build a wrapper or something).
In this program there is a listbox and a button which removes the first item in the list. But when the item is removed, the ListBox contents are not updated even though oneway binding is configured. I need it to get updated on item removal.
class Data: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private List<string> items;
public List<string> Items { get { return items; } }
public Data(List<string> items)
{
this.items = items;
}
public void RemoveFirstItem()
{
items.Remove(items[0]);
RaisePropertyChanged("Items");
}
private void RaisePropertyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}
XAML code:
<Window x:Class="ListBoxTest.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>
<ListBox Name="TheListBox" Margin="0,0,0.138,46.231" ItemsSource="{Binding Path=Items, Mode=OneWay}"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="212,292,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
</Window>
Window C# code:
public partial class MainWindow : Window
{
private Data data;
public MainWindow()
{
InitializeComponent();
data = new Data(new List<string>{ "1", "2", "3", "4" });
this.DataContext = data;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
data.RemoveFirstItem();
}
}
Yes, the data structure must be a list, not an ObservableCollection (but if it's needed, I can build a wrapper or something)
It's pretty simple, The ListBox UI(Xaml) updating is all based on a few interfaces such as INotifyPropertyChanged, INotifyCollectionChanged and few more
These interfaces instruct the UI when an item they hold gets added / removed / refreshed ...
A List does not implement these interfaces(it implements things such as IEnumerable, ICollection...). So having your ListBox.ItemsSource bound to a collection of type List isn't going to get you the benefits of using an ObservableCollection with updates in the UI.
Sure, if you're forced to use a List and wouldn't mind how you're achieving the result, Every-time you remove an item from your List, reset the Window's DataContext (set the DataContext to null before the remove and again to data after the remove). However this is just insane if someone forced you to have to do that and you should prolly have a chat with them and explain why it's insane.
As for your wrapper idea, sure you can build your own custom collection that say implements IEnumerable, INotifyCollectionChanged, INotifyPropertyChanged and internally hold a List or whatever, but your ListBox.ItemSource should be of this type wrapper to get the updating behavior.
You haven't mentioned why exactly you are forced to not use an ObservableCollection<T>, however if you can you prolly should just use it than invent your own custom collection for simple stuff.

Binding UserControl to its own dependencyProperty doesn't work

Im having a problem where I can't create a User Control which uses properties of an custom object when the parent has set that object to data bind.
To try an explain what I mean here is the code.
Custom object:
public class MyObj
{
public string Text { get; set; }
public MyObj(string text)
{
Text = text;
}
}
User Control Code Behind:
/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl : UserControl
{
public static readonly DependencyProperty ObjectProperty =
DependencyProperty.Register("Object", typeof (MyObj), typeof (MyControl), new PropertyMetadata(default(MyObj)));
public MyObj Object
{
get { return (MyObj) GetValue(ObjectProperty); }
set { SetValue(ObjectProperty, value); }
}
public MyControl()
{
InitializeComponent();
}
}
User control XAML:
<UserControl x:Class="Test.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBlock Text="{Binding Object.Text}"/>
So all I expect is for MyControl to display a TextBlock with text showing whatever string is in MyObj.Text;
If I add the control in code, without any bindings, then this works Okay e.g.
MyControl myControl = new MyControl(){ Object = new MyObj("Hello World!") };
grid.Children.Add(myControl);
However if I try to use data binding this doesn't display anything, here is the code for MainWindow.
CodeBehind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private MyObj _Object;
public MyObj Object
{
get { return _Object; }
set
{
_Object = value;
OnPropertyChanged("Object");
}
}
public MainWindow()
{
InitializeComponent();
Object = new MyObj("HELLO");
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
Could anyone point me in the right direction, I guess it's something to do with using relative source binding on the UserControl but I'm not sure.
Thanks
I've personally never used a relative self binding on a UserControl, so I'm unsure if it works. You may try setting the x:Name of your UserControl, and use that in the binding.
<UserControl x:Class="Test.MyControl"
...
x:Name="window">
<TextBlock Text="{Binding ElementName=window, Path=Object.Text}"/>
</UserControl>
Note that if a data-binding fails to bind at runtime, you should also see a related error message in the Output window.
it's been a long time .. but since there is a new technique i would like to post it here.
Compiled Time Binding : this is a new type of binding introduced with windows 10. this binding has a lot of performance benefits classic binding.
And the extra benefit you need not set any DataContext the Page or Control itself is the DataContext you can bind to anything in the page or Control
<UserControl x:Class="Test.MyControl"
...
x:Name="window">
<TextBlock Text="{x:Bind Object.Text}"/>
</UserControl>
But does this work perfectly as you have imagined .. No!! not as u guessed.
and there is an answer to it .
Compiled time binding are by default set to OneTime as opposed to classic bindings that are se to OneWay.
so you need to explicitly set the mode to OneWay to ensure the value always updates.
<UserControl x:Class="Test.MyControl"
...
x:Name="window">
<TextBlock Text="{x:Bind Object.Text,Mode=OneWay}"/>
</UserControl>

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