Control in an ItemTemplate of a ComboBox loses its binding - wpf

I have a ComboBox that uses an ItemTemplate as shown below. Somehow the Text property of the text box defined in the item template gets disconnected from the binding and stops being updated when the selected item changes.
The ComboBox.ItemsSource is bound to a DependencyProperty that is list of CatheterDefinition objects. The ComboBox.SelectedItem is bound to a DependencyProperty that is a single CatheterDefinition object.
<ComboBox
AutomationProperties.AutomationId="CatheterInfoModelFieldID"
VerticalAlignment="Center" HorizontalAlignment="Stretch"
ItemsSource="{x:Static PumpAndCatheter:CatheterInfoViewModel.CatheterModelDefinitions}"
SelectedItem="{Binding ElementName=UserControl, Path=ViewModel.SelectedCatheterModel, Mode=TwoWay, NotifyOnSourceUpdated=True}"
SourceUpdated="HandleModelSourceUpdated">
<ComboBox.ItemContainerStyle>
<!-- A style used to set the AutomationID based on the item goes here -->
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<!-- This line below is the location of the problem -->
<TextBlock Text="{Binding Converter={StaticResource CatheterModelDefinitionToStringConverter}}">
<!-- A style used to set the AutomationID based on the item goes here -->
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I have an automated test that produces a very strange behavior (I saw the same behavior a few time during the initial development of the code, but was unable to reproduce it manually) - The test that reproduces this selects an item form the ComboBox, then goes to another part of the application and takes some actions that end up saving this change in a data model. When the test returns to the screen with this ComboBox, it tries to select another item from the ComboBox. The SelectedItem changes, and the values that it is bound to change, BUT the text in the ComboBox does not change - somehow the binding to the Text property of the text box gets broken (or something)... The binding still executes (the converter still runs when the selection changes and it converts to the correct value), but the text property is never updated.
Thoughts? (I can't provide an example of this because it is a huge application and it is only reproducible under one test that I know of)

Broken bindings are most of the time caused by not calling (or not correctly calling) the OnPropertyChanged("PropName") method.
Without seeing your underlying implementation, I would say that this is most likely the source of the problem.

Related

Trying to get the currently selected item from a ComboBox DataTemplate inside a DataGrid off an DropDownClosed Event Handler

I previously had a ListView that was displaying an ObservableCollection of "Player" object properties, which I'm trying to convert into a DataGrid. I have most of it working, but currently having some issues with seeing changes on one particular property (Status), which is represented by a ComboBox. The idea is to allow players to override the "Status" value between a set of enums representing things like "Alive, Dead, Poisoned," etc. I've hooked up an EventHandler for when the ComboBox is closed and inside that handler, try to grab the sender object as a Player so I can send out the valid player values.
Here's a snippet of the XAML where I'm creating the ComboBox via a DataTemplate.
<DataGridTemplateColumn Header="Status">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox Name="cbStatus"
ItemsSource={Binding Source={StaticResource statusTypes}}"
SelecteItem="{Binding statusType, Mode=TwoWay}"
DropDownClosed="cbStatusType_DropDownClosed"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Inside the Event Handler, I'm using the following to try and grab an "Player" object based on the values coming back from that particular row of the GUI.
Player playerOverridden = (Player)(sender as FrameworkElement).DataContext;
However, when I'm debugging the new playerOverridden when the ComboBox closes and a new value is selected, I'm not seeing that value being captured in playerOverridden.
This is pretty much the exactly what I was doing in a ListView with GridViewColumn.CellTemplates and it was working just fine. Not sure why the Status value is coming back as whatever it was initially set to instead of what the player has selected from the ComboBox.
Had to set the UpdateSourceTrigger on the SelectedItemBinding to be PropertyChanged, it works. Not sure why this has to be set explicitly inside a DataGrid where it's not something I needed to do in the ListView.
SelectedItem={"Binding statusType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

WPF - Using CollectionViewSource is Causing Erroneous Setter Call

WPF, MVVM
I'm finding that if I use a CollectionViewSource with my ComboBox, when I close the window, an extra call to the SelectedValue Setter is executing, if SelectedValue is bound to a string property. If I set the ItemsSource binding directly to the VM, this call does not happen. The extra call is causing values to change in the VM, resulting in incorrect data. I have other ComboBoxes setup the same way, but they bind to integer values.
CollectionViewSource definition:
<CollectionViewSource x:Key="AllClientsSource" Source="{Binding AllClients}" >
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="ClientName" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
ComboBox with CollectionViewSource:
<ComboBox Grid.Column="2"
ItemsSource="{Binding Source={StaticResource AllClientsSource}}"
DisplayMemberPath="ClientName" SelectedValuePath="ClientId"
SelectedValue="{Binding Path=ClientId}"
Visibility="{Binding Path=IsEditingPlan, Converter={StaticResource BoolVisibility}}" />
ComboBox direct to VM (Forgoing sorting):
<ComboBox Grid.Column="2" ItemsSource="{Binding AllClients}"
DisplayMemberPath="ClientName" SelectedValuePath="ClientId"
SelectedValue="{Binding Path=ClientId}"
Visibility="{Binding Path=IsEditingPlan, Converter={StaticResource BoolVisibility}}" />
Can anyone tell me why there is an extra setter call using the CollectionViewSource? What's different about the string binding? Is there a way to properly work around it?
EDIT: I tried changing it up and using the SelectItem property on the ComboBox. Same result. So it seems that if the item is a scalar data type, it works as expected. If it's an object, you get an extra setter call with a null value. Again, if I remove the CollectionViewSource from the equation, it works as expected.
EDIT, AGAIN: I added a link to a sample project that illustrates the issue. Targets .Net 4.5.
Run the project.
Click to display View One
Select a Client and the client's name will display on the right.
Click to display View Two
Go back to View One - Note that the selected client is no longer selected.
Click to display View Three
Select a Region and the region's name is displayed on the right.
Go back to View Two
Go back to View Three - Note that the selected region is still selected.
The only difference between the views is that One and Two use a CollectionViewSource. Three binds directly to the ViewModel. When you move to a new tab from One or Two, the setter for the selected item is getting called with a null value. Why? What's the best work-around?
Thanks.
Apparently this is caused when the CollectionViewSource is removed from the visual tree... I moved the CollectionViewSource to the ViewModel and exposed it as a property and the issue is effectively worked-around.

Set WPF CheckBox in a ComboBox to Checked

I have a custom WPF control - essentially a combo-box with check-boxes. The combo-boxes are successfully bound to a list of available items.
This control is to represent a field on a form. It is possible for the form to be completed already, in which case, the control must be rendered with the selected items - i.e. any items previously selected must be rendered as a checked CheckBox; it is here I'm running into trouble.
My first thought was to simply bind the IsChecked property - I don't think this can be done as the list of currently-selected-items is different from the list of available items which the ComboBox is bound to.
Basically, how do I gain access to the CheckBox object to set the Checked property to true? I've looked into this extensively and I can't fathom this out.
I'm using the ItemContainerGenerator approach - so when the user clicks on the drop-down, it is intended that a handler will iterate through the CheckBoxes and set the relevant boxes to checked.
Here's the XAML:
<ComboBox x:Name="FieldOptions"
ItemsSource="{Binding}"
HorizontalAlignment="Stretch"
Height="30"
KeyDown="FieldOptions_OnKeyDown">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="checkbox"
Content="{Binding Path=Text}"
Uid="{Binding Path=ID}"
FontStyle="Normal"
Foreground="Black"
Checked="CheckBox_OnChecked" />
</DataTemplate>
</ComboBox.ItemTemplate>
Any thoughts would be much appreciated.

How to update an object's inner property from UI to VM? (WPF MVVM)

I have a property in my ViewModel, I'll call it "Project" which contains several nested lists inside of it. None of such lists has an associated property in the view model since I can show everything in xaml by using triggers and bindings.
My xaml shows the Project hierarchy in a treeview and its details in several views (a content control selects the right view depending on which item is selected on the treeview). One of those "details" is a property for the objects contained in one of the nested lists, I'm showing it in a textbox so the user can edit it, the problem I'm having is I don't see that property updated in the Project property in the VM once I make changes to it in the textbox.
I was told I have to create a property in the VM for that specific object's property I'm trying to edit, I just don't know how since the object is deep inside one of the nested lists of my Project object.
Common mistakes are:
Using OneWay binding mode instead of TwoWay
In XAML:
<TextBox Text="{Binding Path=Name, Mode=TwoWay}" />
Not realizing that the update is not propagated to the VM until after the TextBox lost focus.
this is the default for the TextBox:
<TextBox Text="{Binding Path=Name, UpdateSourceTrigger=LostFocus}" />
You could change it to:
<TextBox Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" />

Why is my ComboBox SelectedItem null?

I have a WPF/MVVM (using MVVM-Light) app setup with a ComboBox that is inside a DataTemplate. The XAML of the ComboBox looks like this:
<ComboBox x:Name="cbTeachers"
Grid.Column="1"
Style="{StaticResource ComboBox}"
ItemsSource="{Binding Teachers}"
Grid.Row="3"
DisplayMemberPath="Name"
SelectedValuePath="Id"
IsSynchronizedWithCurrentItem="False"
SelectedItem="{Binding Path=SelectedTeacher}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding TeacherSelectedCommand}"
CommandParameter="{Binding SelectedItem, ElementName=cbTeachers}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
The Teachers property for the ItemsSource is a type called ObservableRangeCollection and is based on the code found here: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx, but it's very similar to a standard ObservableCollection. The SelectedTeacher property is set when another property is set and the code looks very similiar to this:
this.SelectedTeacher = (from t in this.Teachers where t.Id == this.DataItem.Teacher.Id select t).Single();
The problem I am running into, which makes zero sense to me, is SelectedTeacher is getting reset to null once I set it. I can step through the debugger and see SelectedTeacher has a value and when I put a breakpoint on the setter for the property it definitely has the value. But then that property gets hit again with a null value. I checked the call stack and it showed the only preceeding line as being External Code (which makes sense since I only set that property in one place and it only gets hit once, as expected). Expanding the External Code option in the call stack window shows the typical WPF call stack of maybe 40 methods so it's definitely internal to WPF and not something I am doing to make it reset. In fact, when I remove the SelectedItem="{Binding SelectedTeacher}" the setter for that property doesn't get called a second time (thus it retains its value), but of course the ComboBox doesn't show the selected item either. I tried implementing a SelectedIndex option in my viewmodel but that didn't work either. The ComboBox just won't select the item. I can change the selected item in the ComboBox just fine, but the initial setting won't take.
Any ideas? Based on everything I've searched it might be related to me using a DataTemplate, but I have to because that template is part of a parent ContentTemplateSelector implementation.
As a side note, I have multiple properties that bind to controls in this DataTemplate and this is the only one that doesn't work. The others work perfectly. I have also tried the ComboBox with and without the "IsSynchronizedWithCurrentItem" flag and it made no difference.
have you tried to remove to EventTrigger stuff and just to use
SelectedItem="{Binding Path=SelectedTeacher, Mode=TwoWay}"
with Mode=TwoWay?
its not clear to me what you want to achieve with your EventTrigger?

Resources