Binding FrameworkElementFactory to data object property - wpf

I have a ListBox whose DataTemplate is created in code using 3 FrameworkElementFactory objects(A StackPanel with 2 appended children(CheckBox and TextBox)). The item object in the collection that is bound to the ItemsSource of the ListBox is basically the same type of Item object that you would typically see with any type of ListControl. What I'm trying to do is bind the CheckBox's IsChecked property in the DataTemplate to a boolean property on the Item object. The ListBox supports 3 modes, single select, multiselect, and multicheck. The mode I'm trying to implement is multicheck so that the IsChecked property of the CheckBox is bound to the Selected property of the item object. This creates a behavior where the item is only considered selected when the CheckBox's IsChecked property on the ListBoxItem is true, not when the WPF ListBoxItem's IsSelected property is true. What should happen is that the boolean property on the data object should be bound to the IsChecked property, and when the IsChecked property is changed the Selected property on the item object will update, and will thus update a SelectedItems collection behind the scenes.
Here is some simplified code that I have just described.
ListBox innerListBox = new ListBox();
//The ItemsSource of the ListBox being set to the collection of items
this.innerListBox.ItemsSource = this.Manager.ItemManagers;
this.innerListBox.ItemTemplate = this.GetMultipleCheckTemplate();
public System.Windows.DataTemplate GetMultipleCheckTemplate()
{
DataTemplate dt = new DataTemplate;
FrameworkElementFactory factorySP = new FrameworkElementFactory(typeof(StackPanel));
FrameworkElementFactory factoryCB = new FrameworkElementFactory(typeof(CheckBox));
factoryCB.SetBinding(CheckBox.IsCheckedProperty, new Binding("Selected");
RoutedEventHandler clickHandler = new RoutedEventHandler(ItemCheckBox_Click);
factoryCheckBox.AddHandler(CheckBox.ClickEvent, clickHandler, true);
factorySP.AppendChild(factoryCB);
FrameworkElementFactory factoryTB = new FrameworkElementFactory(typeof(TextBlock));
factoryTB .SetBinding(TextBlock.TextProperty, new Binding("Description");
factorySP.AppendChild(factoryTB);
template.VisualTree = factorySP;
return template;
}
There is some code that I'm not including that is the event handler on the CheckBox. If there is a multiple selection on the Wpf ListBox, then all of the CheckBoxes in the range would be toggled to the value of the CheckBox that was clicked. I can manually set the Selected property on the Item to the IsChecked property of the sender and everything works fine, I would however think that databinding should just work and I wouldn't have to do this manually. Would the databinding in this case be asynchronous or do I need to do something explicitly?

Related

Cannot clear selection of ListView

In my listview, when I select on of the item I will show a new view and set the listview to not select any item by set SelectedItem to be null. But the listview still select the old item that I selected. According to this link, I have set
IsSynchronizedWithCurrentItem="True"
But it still same. My item list is compose from the ItemViewModel that inherited from MvxViewModel
Are you using ListView(ListBox) or any custom control?
A class inherited from MS ListBox (such as ListView) has a static method to unselect all selected items:
ListBox.UnselectAll()
To unselect only one item you can cast selected item to ListBoxItem object and call:
ListBoxItem item = (ListBoxItem)obj;
item.IsSelected = false;
Have you tried it in your code behind? Or you want to achieve this declaratively by XAML markup?

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

Set focus to List item on Listview

I have a Listview her ItemsSource property is binded to ViewModel, when i add a new item to List (ObservableCollection) i need set focus to the new item.
Bind the SelectedItem property of the ListView to a property in your ViewModel and when you add a new item to the collection set the property in your ViewModel to reference that new item and the binding will take care of the rest.

Databind Combobox in 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.

Silverlight DataGrid binding issues after refreshing or setting selectedIndex=-1

I have a datagrid and a combobox on the form. The combobox is bound to the selectedItem of the datagrid.
I load things fine and if i select different rows the combobox is updated correcly.
If however I set datagrid.selectedIndex=-1 after it loads (so that the first row is not selected) the combobox binding no longer works. This is a problem.
I also have another scenario where the exact thing occurs. If i filter the datagrid, the binding to the combobox also stops working.
I am binding the datagrid to a CollectionViewSource like the following where _codes is an ObservableCollection
_ocvsCode = (CollectionViewSource)this.Resources["cvsCode"];
_ocvsCode.Source = _codes;
dataGrid1.ItemsSource = _ocvsCode.View;
I don't know why the binding to the combobox is failing after some operation on the datagrid.
The appropriate solution in this case is to bind the datagrid selecteditem to some variable, and to then bind the other controls to that variable as well. It is generally bad practice to bind UIElement properties directly to other UIElement properties. This will also make debugging the problem you seem to be having with coercing the selecteditem property to the combo-box.
I have come across the same problem, where a ComboBox is bound to a the value of the SelectedItem of a DataGrid.
The ComboBox control breaks when the data it is binding becomes null, and never recovers. I'm not sure why that is, but it seems to me to be a bug. When the DataGrid sorts a column, it first sets its SelectedItem to null, performs the sort, and then resets SelectedItem to the original value. When the SelectedItem becomes null, the ComboBox breaks.
Here's my work around:
Create a SelectedItem property on your class that is being used for the DataContext. Perform a check on the setter that prevents it from being set to null. Bind against this property with your DataGrid and ComboBox.
public YourItem SelectedItem
{
get { return _selectedItem; }
set
{
if (value == _selectedItem || value == null)
return;
_selectedItem = value;
RaisePropertyChanged("SelectedItem");
}
}

Resources