I'm having precisely the same problem and you're right in thinking that MessageBox is screwing things up. To be honest, I've had other issues with MessageBox while working with Windows Forms before switching to WPF. Maybe it's just some century-old bug that became a feature (as often it is with Microsoft)?
In any case, the only solution I can offer you is the one that has worked for me. I was having problems with getting a similar situation to work with a ListBox - if there were changes to data in the form, when selection of the ListBox changed (either by clicking on new item or using keys "Up" or "Down"), I offered user a choice in the MessageBox whether to save, discard or cancel.
Naturally using the direct approach of handling ListBox's MouseDown or PreviewMouseDown events didn't work well with a MessageBox. Here's what worked.
I have a data template to display items in my ListBox (I'm almost expecting you to have the same):
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=NAME}" KeyDown="checkForChanges" MouseDown="checkForChanges"/>
</DataTemplate>
</ListBox.ItemTemplate>
Note how I've moved the KeyDown and MouseDown event handlers to the TextBlock control instead. I kept the same code-behind:
// The KeyDown handler
private void checkForChanges(object sender, KeyEventArgs e) {
e.Handled = checkForChanges();
}
// Method that checks if there are changes to be saved or discard or cancel
private bool checkForChanges() {
if (Data.HasChanges()) {
MessageBoxResult answer = MessageBox.Show("There are unsaved changes. Would you like to save changes now?", "WARNING", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
if (answer == MessageBoxResult.Yes) {
Data.AcceptDataChanges();
} else if (answer == MessageBoxResult.Cancel) {
return true;
}
return false;
}
return false;
}
// The MouseDown handler
private void checkForChanges(object sender, MouseButtonEventArgs e) {
e.Handled = checkForChanges();
}
As a side note, it's odd how Binding always marks my DataRows as Modified when the selected item in the ListBox, which has ItemsSource bound to a DataTable, changes (I don't know if you're using DataTables/Sets). To battle that, I discard any unhandled changes once the selection has already been changed (because I handle anything that's necessary in the event MouseDown that occurs prior to that):
<ListBox IsSynchronizedWithCurrentItem="True" [...] SelectionChanged="clearChanges"> ... </ListBox>
And the code-behind for the handler:
private void clearChanges(object sender, SelectionChangedEventArgs e) {
Data.cancelChanges();
}