WPF: How to prevent CheckBox.Checked event from being fired for ComboBox as a ToggleButton?
-
27-10-2019 - |
Question
I have a lots of controls in a Window including checkboxes and comboboxes. I want to track CheckBox.IsChecked event, so I defined an event handler in Windows level as
<Grid CheckBox.Checked="OnModified" CheckBox.Unchecked="OnModified">
The problem is that the same event is also fired by ComboBox right after mouse clicking an item. I thought my event handler should only capture CheckBox's Checked event, not ToggleButton's. Did I miss anything?
EDIT: As I pointed out below, I thought it would work this way because I read Matthew MacDonald's book "Pro WPF in C# 2010". On page 164, he gave this example code first:
<StackPanel Button.Click="DoSomething" Margin="5">
<Button Name="cmd1">Command 1</Button>
<Button Name="cmd2">Command 2</Button>
<Button Name="cmd3">Command 3</Button>
...
</StackPanel>
Then, he specially noted:
Note The Click event is actually defined in the ButtonBase class and inherited by the Button class. If you attach an event handler to ButtonBase.Click, that event handler will be used when any ButtonBase-derived control is clicked (including the Button, RadioButton, and CheckBox classes). If you attach an event handler to Button.Click, it’s only used for Button objects.
Now, did I misunderstand him, or is his note wrong?
Solution
There is not actually a separate CheckBox.Checked
event. If you look at this page:
and find the Checked
event you will see that it is inherited from ToggleButton
so ToggleButton.Checked
and CheckBox.Checked
are two different names for the same event.
Since you are subscribing to a "wildcard" event, in the event handler you can check the sender or source to see you it is one you are interested in.
Edit:
To address your follow-on question regarding the book quote, I think the quote is at least misleading. Here is a counter-example that shows a CheckBox
reacting to Button
event even though CheckBox
is not derived from Button
:
<CheckBox Button.Click="CheckBox_Click"/>
Of course there is no Button.Click
event, only a ButtonBase.Click
event, but that is the crux of the quote. If the quote were literally true, either this syntax would not be permitted or the event wouldn't fire, neither of which is true.
OTHER TIPS
its because you are setting the event on the grid, so any children contained by the grid that have a Checkbox.Checked
routed event will respond to the event. In your case, it happens that the ComboboxItem
uses the same routed event (and I am sure other controls probably reuse it as well)
the easiest way to handle this would be to add a test to your handler, something like this:
private void OnModified(object sender, EventArgs args)
{
if ( sender is CheckBox )
{
CheckBox ckBox = sender as CheckBox;
// do stuff with ckBox
}
}
This happens because both events get bubbled up the elements tree and both reach your handler (CheckBox
inherits ToggleButton
, hence CheckBox.Checked
and ToggleButton.Checked
are in fact the same events). You cannot prevent this. What you can do instead is to check whether the event was raised by a CheckBox
. It can be done like this:
private void OnModified(object sender, RoutedEventArgs e)
{
// Filter the event by its source
if (e.Source.GetType() != typeof(CheckBox))
return;
// Your handling code
}
I'm the author of "Pro WPF in C# 2010" and I can confirm that the text from the Note box falls under the "what was I smoking?" category. There is no distinction between the Click event based on how you refer to it in your markup.
Matthew