I recently responded to a thread on MSDN forums where a person was trying to get something to happen when the IsFocused property of a ComboBox was set to true in WPF.
Because of some of the work I had previously done on retemplating my own ComboBox, I quickly recognized that this was an issue with the way the control is templated. Largely, what is going on is that the ComboBox has a complex visual tree and when the ComboBox is in edit mode the control that has focus is actually a textbox inside the control template.
Thus, the ComboBox in WPF will never have its IsFocused property set to true.
I realized, though, that this has an effect on other things, such as trying to template a control through styles and triggers without replacing the entire visual tree.
Take the following style for a WPF Button:
<Style TargetType="Button" x:Key="ButtonStyle">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
If you put that in the Windows.Resources section of your window and set up a button to use that style, you’ll see that the button immediately reflects the DropShadowEffect. Change the above IsEnabled to IsFocused and run the app. The DropShadowEffect is only rendered if the button has focus.
Now, try the same thing with a style for the ComboBox:
<Style TargetType="ComboBox" x:Key="ComboBoxStyle">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
This, again, works off the hop, even with IsFocused. But if you set IsEditable to true, things change.
As you can see from the screenies to the right, we don’t get our drop shadow rendered even when the control has focus when it’s in edit mode.
I think the only solution is to work around this through code-behind in the class. This means that you’ll either need to plop some code into each form where you want to bind to the IsFocused property of the ComboBox, or you’ll need to create your own control library (not a bad idea if you’re trying to do something like this, I suppose).
So, I tried to go down that route. My first attempt, though close, isn’t what we are looking for. Indeed, it looks kinda cool and some might find it funky, but it doesn’t put the shadow where we’d like – namely, on the ComboBox itself.
Here’s the code:
this.comboBox1.Loaded += delegate
{
TextBox tb = (TextBox)comboBox1.Template.FindName("PART_EditableTextBox", comboBox1);
if (tb != null)
{
Style tbStyle = new Style(typeof(TextBox), tb.Style);
Trigger effectTrigger = new Trigger();
effectTrigger.Property = TextBox.IsFocusedProperty;
effectTrigger.Value = true;
Setter dsSetter = new Setter(TextBox.EffectProperty, new DropShadowEffect());
effectTrigger.Setters.Add(dsSetter);
tbStyle.Triggers.Add(effectTrigger);
tb.Style = tbStyle;
}
};
Perhaps a simpler approach would be just storing a couple of style references in the window (or the control, if you’re rewriting) as such:
Style _cboDropShadowStyle;
Style _cboCleanStyle;
We build up our styles in the ComboBox.Loaded delegate:
this.comboBox1.Loaded += delegate
{
// store the clean style
_cboCleanStyle = new Style(typeof(ComboBox), comboBox1.Style);
// build and store the dropshadowed style
_cboDropShadowStyle = new Style(typeof(ComboBox), comboBox1.Style);
Trigger effectTrigger = new Trigger();
effectTrigger.Property = ComboBox.IsEnabledProperty;
effectTrigger.Value = true;
Setter dsSetter = new Setter(ComboBox.EffectProperty, new DropShadowEffect());
effectTrigger.Setters.Add(dsSetter);
_cboDropShadowStyle.Triggers.Add(effectTrigger);
};
Then, finally, we write our event handlers (be sure to mark the XAML up accordingly):
private void comboBox1_GotFocus(object sender, RoutedEventArgs e)
{
comboBox1.Style = _cboDropShadowStyle;
}
private void comboBox1_LostFocus(object sender, RoutedEventArgs e)
{
comboBox1.Style = _cboCleanStyle;
}
I will continue to ponder a cleaner way, but for now this should be an acceptable approach. You could easily build this into a custom control. Or, you could extend the TextBox sample above into the custom control and expose a DependencyProperty called IsSeriouslyFocusedForRealYo that you set when the focus on that PART_EditableTextBox changes.