views:

79

answers:

2

I'm currently trying to create a ControlTemplate for the Button class in WPF, replacing the usual visual tree with something that makes the button look similar to the little (X) close icon on Google Chrome's tabs. I decided to use a Path object in XAML to achieve the effect. Using a property trigger, the control responds to a change in the IsMouseOver property by setting the icon's red background.

Here's the XAML from a test app:

<Window x:Class="Widgets.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">

  <Window.Resources>
    <Style x:Key="borderStyle" TargetType="Border">
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Background">
            <Setter.Value>
              <SolidColorBrush Color="#CC0000"/>
            </Setter.Value>
          </Setter>
        </Trigger>
      </Style.Triggers>
    </Style>

    <ControlTemplate x:Key="closeButtonTemplate" TargetType="Button">
      <Border Width="12" Height="12" CornerRadius="6"
              BorderBrush="#AAAAAA" Background="Transparent"
              Style="{StaticResource borderStyle}"
              ToolTip="Close">
        <Viewbox Margin="2.75">
          <Path Data="M 0,0 L 10,10 M 0,10 L 10,0" Stroke="{Binding BorderBrush, RelativeSource={RelativeSource FindAncestor, AncestorType=Border, AncestorLevel=1}}" StrokeThickness="1.8"/>
        </Viewbox>
      </Border>
    </ControlTemplate>
  </Window.Resources>

  <Grid Background="White">
    <Button Template="{StaticResource closeButtonTemplate}"/>
  </Grid>

</Window>

Note that the circular background is always there - it's just transparent when the mouse isn't over it.

The problem with this is that the trigger just isn't working. Nothing changes in the button's appearance. However, if I remove the Background="Transparent" value from the Border object in the ControlTemplate, the trigger does work (albeit only when over the 'X').

I really can't explain this. Setters for any other properties placed in the borderStyle resource work fine, but the Background setter fails as soon as the default background is specified in the ControlTemplate.

Any ideas why it's happening and how I can fix it? I know I could easily replace this code with, for example, a .PNG-based image, but I want to understand why the current implementation isn't working.

Thanks! :)

+3  A: 

Try moving the explict "Background" assignment from inside the Border declaration to the Style itself:

<Style x:Key="borderStyle" TargetType="Border">
    <Setter Property="Background" Value="Transparent" />

    <Style.Triggers> 
        <Trigger Property="IsMouseOver" Value="True"> 
            <Setter Property="Background"> 
            <Setter.Value> 
                <SolidColorBrush Color="#CC0000"/> 
            </Setter.Value> 
        </Setter> 
    </Trigger> 
</Style.Triggers> 

...

<Border Width="12" Height="12" CornerRadius="6" 
        BorderBrush="#AAAAAA" 
        Style="{StaticResource borderStyle}" 
        ToolTip="Close"> 

Styles can't override a property that has been explicitly set. You need to set the value in the style.

Matt Hamilton
Wow, that works - thanks! Wonder how I never came across this before. Do you have a reference (e.g. MSDN article) that describes this limitation of styles?
Mal Ross
Not that I'm aware of, but it comes up here quite often. :)
Matt Hamilton
Found a reference on MSDN: http://msdn.microsoft.com/en-us/library/ms743230.aspx#multiple_sets
Mal Ross
Thank you very much for this, Matt! You just saved my head from my desk.
Nathan Ernst
A: 

I think your problem is that the Border does not 'catch' the mouse events when it is transparant.

To verify - try changing the background to #01FFFFFF instead of Transparent.

Goblin
Nope, that's not it. I specifically used Transparent because it *does* catch mouse events (unlike a null brush).
Mal Ross
Ahh - missed that.
Goblin