Databind Combobox in WPF - wpf

I'm trying to databind a combobox in WPF for the first time and I can't get it to happen.
The image below shows my code, can you please tell me what I am missing? I only want graphic stuff in the xaml.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Patient p = new Patient();
this.cbPatient.DataContext = p.SelfListAll();
this.cbPatient.DisplayMemberPath = "Name";
this.cbPatient.SelectedValuePath = "PatientIDInternal";
}
...

Short explanation: Just make the following change to your XAML:
<ComboBox ItemsSource="{Binding Path=patientList}" />
Then, in your Window_Loaded event handler, just add
this.DataContext = this
Then make a new member called patientList of type ObservableCollection<Patient>.
Long explanation:
You don't have a binding set up. You need to create one through XAML like this:
<ComboBox ItemsSource="{Binding Path=patientList}" />
Then, the combobox will look for a member or property called "patientList" on the object that is set as its DataContext. I'd recommend using an ObservableCollection for patientList.
Alternatively, to create one in code, you can follow the examples here:
http://msdn.microsoft.com/en-us/library/ms752347.aspx#specifying_the_binding_source
Binding myBinding = new Binding("patientList");
myBinding.DataContext = someObject; //whatever object has 'patientList' as a member
mycombobox.SetBinding(ComboBox.ItemsSourceProperty, myBinding);
This will set a binding on the mycombobox ComboBox with a path of patientList and a DataContext of someObject. In other words, mycombobox will show the contents of someObject.patientList (which would ideally be some ObservableCollection, so that updates to the collection notify the binding to update).

You need to actually add the binding, e.g.:
Binding binding = new Binding();
binding.Source = MySourceObject;
binding.Path = new PropertyPath("MyPropertyPath");
binding.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(cbPatient, SomeDependencyProperty, binding);

Ok, here is the answer to how to populate a combobox in WPF. First, thanks to everyone above who made suggestions. The part I was missing was that I was not populating the ItemsSource property but the DataContext property. Again, thanks to everyone for their help.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Patient p = new Patient();
this.cbPatient.ItemsSource = p.SelfListAll();
this.cbPatient.DisplayMemberPath = "Name";
this.cbPatient.SelectedValuePath = "PatientIDInternal";
this.cbPatient.SelectedIndex = 0;
}

You need to set the ItemsSource property relative to the DataContext:
cbPatient.SetBinding(ItemsSourceProperty, new Binding());
EDIT
The ItemsSource property of the ComboBox is the property that should point to the collection of items to be shown.
The collection you are interested in, is in the DataContext.
The Binding is an object that keeps track of changes of the collection and reports them to the ComboBox and its Path is relative to the object in the DataContext.
Because the Binding also needs to know the ComboBox you use the static SetBinding method that ties the connection between ComboBox and the Binding.
As in your code the collection itself is in the DataContext, the Path is empty.
The ItemsSource property should point to the collection of Patients. Because the collection of Patients is already in the DataContext, the Binding's Path property is empty.
Suppose an class named Hospital has two properties: Patients and Docters (and perhaps more: Rooms, Appointments, ...) and you set the DataContext of the ComboBox to an instance of Hospital. Then you would have to set the Binding's Path Property to "Patients"
Now the ComboBox will display each item (Patient) in the collection. To specify how a single Patient should be displayed you need to set the ItemTemplate property of the ComboBox.

Related

UserControl's DataContext update WPF

I have my own usercontrol named FlashControl in the mainwindow. I set the DataContext by following code in the mainwondow
(FlashControl.Content as FrameworkElement).DataContext = null;
(FlashControl.Content as FrameworkElement).DataContext = this.DataContext;
FlashControl.DataContext = this.DataContext;
My problem is whenever my datacontext change I need to call the above code to reset usercontrol's datacontext. Why Usercontrol's DataContext not updated automatically when main DataContext change? How to do automatic update? Am I missing something?
If you want automatic update dont set DataContext directly but Bind it to the value you want.
You should bind in xaml but if you want to do in code behind then you can do:
Binding myBinding = new Binding("DataContext");
myBinding.Source = this;
BindingOperations.SetBinding(FlashControl, FrameworkElement.DataContextProperty, myBinding);

Object reference not set to an instance of an object when creating two ComboboxItems functions in wpf

I´m all out of ideas here
The thing is that Im using two comboboxes and I want to get values from both comboboxes to show content in DataGrid in wpf.
I have this function that gets values from both comboboxes. This works well.
private void cboxYearChange(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem typeItemYear = (ComboBoxItem)comboBox2.SelectedItem;
string valueYear = typeItemYear.Content.ToString();
ComboBoxItem typeItemMonth = (ComboBoxItem)comboBox1.SelectedItem;
string valueMonth = typeItemMonth.Content.ToString();
}
But then I want to create another function to check for changes on the other combobox:
private void cboxMonthChange(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem typeItemYear = (ComboBoxItem)comboBox2.SelectedItem;
string valueYear = typeItemYear.Content.ToString();
ComboBoxItem typeItemMonth = (ComboBoxItem)comboBox1.SelectedItem;
string valueMonth = typeItemMonth.Content.ToString();
}
I can build, but when I run this I get the Object reference not set to an instance of an object error on the ComboBoxItem typeItemYear = (ComboBoxItem)comboBox2.SelectedItem; line in the cboxMonthChange function
What am I missing here ?
SelectedItem is null until something is selected. Unless they both change at the same time (which is not possible as these events are fired in sequence), either the type cast on comboBox1.SelectedItem or comboBox2.SelectedItem will throw an exception.
Check if SelectedItem is set the methods.
Or use another cast, like:
ComboBoxItem item1 = comboBox1.SelectedItem as ComboBoxItem;
if (item1 != null)
{
// do something
}
Hope this helps :-)
1) you should not refer to control's name within the code whenever possible.
So you can know, for instance, which ComboBox was changed within a SelectionChanged
handler by casting the Sender to a ComboBox.
2) but in such a simple case, just use public properties and bind them to
your ComboBox : all will get done with no code.
<ComboBox x:Name="YearSelectCB" SelectedItem="{Binding SelectedYear}">
<ComboBox x:Name="MonthSelectCB" SelectedItem="{Binding SelectedMonth}">
(you can set the DataContext of the window in several ways, for instance in the
window loaded event handler (DataContext=this) )

WPF: Avoid CustomCombobox to fire SelectedItem Binding when changing ItemsSource to CompositeCollection

I have a Custom Control derived from a ComboBox where I use a CompositeCollection to "merge" the original ItemsSource with additional Objects.
The problem is, that
CompositeCollection comp = new CompositeCollection();
SomeLogic();
ItemsSource = comp;
Setting the ItemsSource to the composed Collection is setting SelectedItem to null and invoke the Binding TwoWay Binding to the ViewModel. The SelectedItem bound property in my ViewModel will then be 'null'.
I am currently workarounding this by restoring the SelectedItem after assigning the ItemsSource:
Object priorSelectedItem = SelectedItem;
ItemsSource = comp;
SelectedItem = priorSelectedItem;
However this just fixes the value of SelectedItem in my ViewModel, a nasty sideeffect is that the when the Object changes some logic is run in the Setter. E.G. setting a
_dataHasChanged = true;
Flag.
So if there is any way I can
a) Prevent the SelectedItem to get reset whilst changing the ItemsSource
or
b) Prevent the SelectedItem-Binding to be Invoked whilst changing the ItemsSource
from within the Custom Control (Don't want to take care of 20 ViewModels because there is a flaw in the Control) I would greatly appreciate any input on how to do so :-)
I've managed to prevent this behavior by saving the SelectedItem-Binding in a private variable in OnApplyTemplate(), then clearing it and Set it back to the variable value after the new ItemsSource has been applied.
private Binding _selectedItemBinding;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_selectedItemBinding = BindingOperations.GetBinding(this, ComboBox.SelectedItemProperty);
BindingOperations.ClearBinding(this, ComboBox.SelectedItemProperty);
if (BindingOperations.IsDataBound(this, ComboBox.SelectedItemProperty))
this.SetBinding(ComboBox.SelectedItemProperty, "dummy");
...
}
private void AdaptItemSource()
{
Object priorSelectedItem = SelectedItem;
ItemsSource = comp;
SelectedItem = priorSelectedItem;
BindingOperations.SetBinding(this, ComboBox.SelectedItemProperty, _selectedItemBinding);
}
This did the Trick for me

Updating IValueConverter through code

I'm trying to figure out if its possible to update an IValueConverter through the code behind.
My situation is that I've got two ComboBoxes. Once the first one is updated, I change the ItemsSource property of the second to be one of a variety of enums. I've grabbed an EnumToFriendlyNameConverter from CodeProject, but I'm not sure how to set it.
If I set the converter in the ItemsSource (see below) then it gets ignored when I next set the items source.
ItemsSource="{Binding Converter={StaticResource enumItemsConverter}}"
I found that it is possible by using an ItemTemplate but then I have to manually place in a label, which then has a different style to my other combobox. Getting the styles right just seems like a lot of work...
When you change the ItemsSource you just have to apply the converter again or modify the ItemsSource instead of replacing it.
e.g. create a new binding:
private void ChangeItemsSouce(IEnumerable newItems)
{
Binding binding = new Binding();
binding.Source = newItems;
binding.Converter = new EnumToFriendlyNameConverter();
comboBox.SetBinding(ComboBox.ItemsSourceProperty, binding);
}
Or modify the existing binding:
private void ChangeItemsSouce(IEnumerable newItems)
{
var binding = comboBox.GetBindingExpression(ComboBox.ItemsSourceProperty);
binding.ParentBinding.Source = newItems;
}

How to stop automatic refresh of a WPF ListBox Databinded on a EntityFramework object

I have a Xaml Page with a Databinded ListBox and a detail grid to create or update selected element.
My Page.DataContext is binded on a ADO.NET Entity Data Model table ("Univers").
private void Page_Loaded(object sender, RoutedEventArgs e)
{
SEPDC = new Models.SEP();
universViewSource = new CollectionViewSource();
universViewSource.Source = SEPDC.Univers.Execute(System.Data.Objects.MergeOption.AppendOnly);
DataContext = universViewSource;
}
The Xaml code of the ListBox :
<ListBox DisplayMemberPath="Nom" ItemsSource="{Binding}" Name="universListBox" SelectedValuePath="IdUnivers"/>
When i select an element in the ListBox, the grid detail automatically display the information of the selected element
Here the "Nom" TextBox witch use TwoWay databinding :
<TextBox Name="nomTextBox" Text="{Binding Path=Nom, Mode=TwoWay}" />
When i modify the TextBox "Nom", the ListBox is automatically updated. Great ... But i haven't call the SaveChanges method of my SEPDC DataContext object ...
How can i stop the automatic refresh of my ListBox until i explicit call the SaveChanges method and if possible, without use the Binding UpdateSourceTrigger=Explicit option ?
Regards.
You can use two separate entity data context (SEPDC) objects. Your ListBox is bound to one and your detail grid to the other. When the SelectedValue changes in the ListBox, find the same entity in the detail grid's entity data context and set it. After saving changes from the detail grid's entity data context, refresh the one for the ListBox.
I use this technique but i have to recreate the ListBox SEPDC each time i refresh the ListBox.
List<Models.Univers> list;
using (Models.SEP dc = new Models.SEP())
list = dc.Univers.Execute(System.Data.Objects.MergeOption.AppendOnly).ToList();
universListBox.DataContext = list;
The Refresh method doesn't work.
Regards

Resources