Reputation: 4568
My ComboBox
:
<ComboBox
SourceUpdated="MyComboBox_SourceUpdated"
ItemsSource="{Binding ...}"
SelectedValue="{Binding Path=SelectedValueMember, NotifyOnSourceUpdated=True}"
SelectedValuePath="..."
DisplayMemberPath="..."
/>
And my SourceUpdated
handler:
private void MyComboBox_SourceUpdated(object sender, DataTransferEventArgs args) {
if (/* user answers no to an 'are you sure?' prompt */) {
// TODO: revert ComboBox back to original value
}
}
I'm having difficult reverting the ComboBox back to its original value (in the "TODO" comment of my code above).
The first and most obvious thing I tried was to simply change back the value of the SelectedValueMember of the DataContext, assuming the binding would update the ComboBox:
MyDataContext.SelectedValueMember = original_value;
When I do this, in the debugger I can see that it is indeed updating the SelectedValueMember value, but the ComboBox does not change back - it holds onto the new value.
Any ideas?
AngelWPF's answer below works and is probably the neatest and clearest way.
I did however, find a non-obvious solution:
private void MyComboBox_SourceUpdated(object sender, DataTransferEventArgs args) {
if (/* user answers no to an 'are you sure?' prompt */) {
Dispatcher.BeginInvoke(new Action(() => {
MyDataContext.SelectedValueMember = original_value;
}));
}
}
Just by putting the operation on a second UI thread by means of BeginInvoke
, it works! I could be wrong, but my hunch is that to avoid binding update loops, the actions directly in Source/Target Updated handlers are not reacted to by bindings.
Upvotes: 1
Views: 1800
Reputation: 6989
Adding on to AngelWPF's answer using UpdateSourceTrigger="Explicit":
A slightly cleaner way to do this would be to use the BindingExpression.UpdateSource() and BindingExpression.UpdateTarget() methods. UpdateSource() moves the value from the UI control to the ViewModel and UpdateTarget() moves data in the opposite direction. Using these methods saves you from having to do this line of code:
((MyObject) bndExp.DataItem).MyID = (int)((ComboBox) sender).SelectedValue;
Contrary to AngelWPF's experience, in my situation I did have to revert the ComboBox back to the original value when the user cancelled even though I had the Binding.UpdateSourceTrigger set to Explicit. Maybe this is because of the Telerik RadComboBox or maybe the previous comment was incorrect, I cannot say at this time. But either way the following, easier to read, code works.
private void OfficeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = (RadComboBox) sender;
var binding = comboBox.GetBindingExpression(RadComboBox.SelectedValueProperty);
if(MessageBox.Show("Are you sure?", "Are you sure?", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
binding.UpdateSource();
}
else
{
binding.UpdateTarget();
}
}
Add your own null checks as desired.
Upvotes: 3
Reputation: 19885
For explicit conditional update of a source in a binding, use the updatesource trigger as Explicit and handle the selection changed event of combobox to perform the sync with the source based on the message box result.
<StackPanel DataContext="{StaticResource MyObject}">
<ComboBox ItemsSource="{Binding MyData}"
DisplayMemberPath="Name"
SelectedValuePath="ID"
SelectedValue="{Binding Path=MyID,
Mode=TwoWay,
UpdateSourceTrigger=Explicit}"
SelectionChanged="ComboBox_SelectionChanged">
</ComboBox>
<TextBlock Text="{Binding Path=MyID}"/>
</StackPanel>
So the ComboBox
is bound to a collection of items (called MyData
) having Name
and ID
as properties. The selected value of the ComboBox
is bound to another property called MyID
. Now notcie that UpdateSourceTrigger=Explicit
in the SelectedValue
binding.
The way I sync the MyID
when the selection changes, with the selected value of the combobox ONLY if user selects Yes
on a message box as below...
private void ComboBox_SelectionChanged
(object sender, SelectionChangedEventArgs e)
{
var bndExp
= BindingOperations.GetBindingExpression(
(ComboBox) sender, Selector.SelectedValueProperty);
if (bndExp != null && bndExp.ParentBinding != null)
{
if (MessageBox.Show(
"Are you sure?",
"Sure?",
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
((MyObject) bndExp.DataItem).MyID
= (int)((ComboBox) sender).SelectedValue;
}
}
}
Thsi way there is no need to revert. The source updates explicitly and you have all the control over it.
Upvotes: 2