Sunday, February 20, 2011

IsMouseOver Trigger not working in WPF

In this post I will explain the problem I faced when I tried to handle IsMouseOver property of a Button in WPF and the solution or right way to do. I was working on a Window which had a Button and I wanted to override the default color schemes related to Button. For example, if you put your mouse over a Button in WPF, by default Button's background color will change light blue. I wanted to override this light blue color with some color of my own choice and similarly I wanted to change background color of Button when button goes in to pressed state.

First piece of XAML I wrote to achieve the change in background color on mouse over was this.

   1:   <Button Width="100" Height="50" Content="Click Me!">
   2:        <Button.Style>
   3:             <Style TargetType="{x:Type Button}">
   4:                 <Style.Triggers>
   5:                      <Trigger Property="IsMouseOver" Value="True">
   6:                           <Setter Property="Background" Value="LightGreen"/>
   7:                      </Trigger>
   8:                 </Style.Triggers>
   9:            </Style>
  10:       </Button.Style>
  11:  </Button>
I ran my application and on putting mouse over the Button I saw same old light blue color in Button's background instead of light green which I specified in XAML. When I looked closely I found out that on putting mouse over Button, for just a moment, Button's background color was changing to green and then immediately it changed to light blue. So what was I missing or doing wrong?

On digging deeper in to this issue I found out that Button in WPF has a default control template. Button was changing it's background color to light blue according to that default control template. When I applied my own trigger on Button, saying that change background color to light green when mouse comes over, as a result two triggers existed for the Button at same time. First one was the WPF's default(which changes background color to light blue) and second was the one which I defined in XAML. As WPF supports multiple triggers on same property, both triggers were working perfectly fine with one another. I think my custom trigger had precedence over the default one thats why WPF was applying my custom trigger first, because of which I was able to see light green background for a moment. After that WPF applied the second trigger, the WPF's default one, which changed the background color of Button to light blue.

The correct way to override the Button's default behavior is by overriding default control template. This can be done by following XAML below.

   1:  <Button Width="100" Height="50" Content="Click Me!">
   2:       <Button.Template>
   3:            <ControlTemplate TargetType="{x:Type Button}">
   4:                 <Border x:Name="bdr_main" CornerRadius="20" Margin="4" BorderThickness="1" BorderBrush="Black" Background="LightGray">
   5:                      <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Margin="8,6,8,6" ContentSource="Content" />
   6:                 </Border>
   7:                 <ControlTemplate.Triggers>
   8:                      <Trigger Property="IsMouseOver" Value="True">
   9:                           <Setter TargetName="bdr_main" Property="Background" Value="LightGreen"/>
  10:                      </Trigger>
  11:                      <Trigger Property="IsPressed" Value="True">
  12:                           <Setter TargetName="bdr_main" Property="Background" Value="Red"/>
  13:                      </Trigger>
  14:                 </ControlTemplate.Triggers>
  15:            </ControlTemplate>
  16:       </Button.Template>
  17:  </Button>
As you can see in above XAML, instead of creating triggers on top of default control template, I am now defining the triggers inside overridden control template. Once you define your own control template, you gain full control of a control's default behavior and layout. In XAML above I have changed the default rectangular shape of WPF's Button to a Button which is more round or circular from the corners.
You can download a ready to run sample based on this post from here.

8 comments:

  1. This was extremely helpful. Thank you so much!

    ReplyDelete
  2. Hello Haris,
    Can you tell me how i can change color of row on mouseover.




    Name
    Last Name


    something like that..Suppose i want to change the color of row on mouseover how i do that.
    Please help me!!!

    ReplyDelete
  3. You can use style + triggers to achieve this.. Apply this style on your DataGridRow

    <Style TargetType="{x:Type DataGridRow}">
    <Style.Triggers>
    <Trigger Property="IsMouseOver"
    Value="True">
    <Setter Property="Background"
    Value="Red" />
    </Trigger>
    </Style.Triggers>
    </Style>

    ReplyDelete
  4. Hello Haris,
    Can you tell me how to assign the mouse over color dynamically.In my code button background color will change in the run time, so i need apply the same color for mouse enter as well. thnks.

    ReplyDelete
  5. This article is excellent. Wonderful job.

    ReplyDelete
  6. great! this is exactly what I was searching for hours... thanks a lot man

    ReplyDelete