31

I am trying to create a button which has 3 images: a Normal image, a Pressed image and a Disabled image (I will use these for creating up/down arrow buttons).

I believe the correct approach would be to derive from Button and use a Template and set triggers to change the image. I have 3 dependency properties, one for each image.

The images would be .png and have transparent backgrounds (as they are not rectangular).

I am looking for something like CBitmapButton in MFC.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72

5 Answers5

71

You won't need dependency properties because you are inheriting from Button. You already have the IsPressed and IsEnabled properties. In fact, this is all you need:

<Button x:Class="TestWpfApplication.ThreeStateImageButton"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Button.Template>
      <ControlTemplate TargetType="{x:Type Button}">
         <Grid>
            <Image Name="Normal" Source="Resources/Normal.png"/>
            <Image Name="Pressed" Source="Resources/Pressed.png" Visibility="Hidden"/>
            <Image Name="Disabled" Source="Resources/Disabled.png" Visibility="Hidden"/>
         </Grid>
         <ControlTemplate.Triggers>
            <Trigger Property="IsPressed" Value="True">
               <Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
               <Setter TargetName="Pressed" Property="Visibility" Value="Visible"/>
            </Trigger>
            <Trigger Property="IsEnabled" Value="False">
               <Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
               <Setter TargetName="Disabled" Property="Visibility" Value="Visible"/>
            </Trigger>
         </ControlTemplate.Triggers>
      </ControlTemplate>
   </Button.Template>
</Button>

With your UserControl code-behind file:

public partial class ThreeStateImageButton : Button
{
   public ThreeStateImageButton()
   {
      InitializeComponent();
   }
}
Charlie
  • 15,069
  • 3
  • 64
  • 70
  • 9
    Exactly. In fact, inheriting from Button is superfluous. You can do everything with this template and a style to apply it. The rule here should be if you need to change behavior, inherit and make a control. If you need to change "Look", then use a style. – Anderson Imes Aug 11 '09 at 19:38
  • Thank you for your response. This is what I was looking for. –  Aug 12 '09 at 15:27
  • THANKS. This is the only Image Button solution that worked for me. I've even added a "Hover" trigger, Major Kudos! – dortzur Mar 27 '11 at 10:45
  • when i am trying to add button template it shows error as the attachable property not found for button – Ash Jun 28 '11 at 04:02
  • Nice solution. If anybody wondered about reusability, it's worth noticing that the image paths don't need to be hardcoded in style. If one ever needs to change something inside the style or template, for example change the button text or icon, one can use attached properties as suggested in this answer: http://stackoverflow.com/a/650620/724944 – surfen Mar 27 '12 at 22:30
  • 3
    How can I pass the image src as a parameter so I can use this solution for different buttons, thanks – Guy Sep 11 '12 at 11:23
1

I have provided an alternative to this solution, its not quite as light weight but it offers much greater re-usability.

WPF TriState Image Button

Community
  • 1
  • 1
PJUK
  • 1,758
  • 1
  • 16
  • 21
1
 public static readonly DependencyProperty DefaultImageSourceProperty = DependencyProperty.Register("DefaultImageSource", typeof(ImageSource), typeof(PressedImageButton), new PropertyMetadata(null, new PropertyChangedCallback(DefaultImageSourceChangedCallback)));
    public static readonly DependencyProperty PressedImageSourceProperty = DependencyProperty.Register("PressedImageSource", typeof(ImageSource), typeof(PressedImageButton), new PropertyMetadata(null, new PropertyChangedCallback(PressedImageSourceChangedCallback)));
    public static readonly DependencyProperty ImageStretchProperty = DependencyProperty.Register("ImageStretch", typeof(Stretch), typeof(PressedImageButton), new PropertyMetadata(Stretch.None, new PropertyChangedCallback(ImageStretchChangedCallback)));

   <ControlTemplate>
        <Grid>
            <Image Name="imgDefault" Source="{Binding Path=DefaultImageSource,ElementName=uc}" Stretch="{Binding Path=ImageStretch,ElementName=uc}"></Image>
            <ContentPresenter Content="{TemplateBinding Property=ContentControl.Content}" />
        </Grid>
        <ControlTemplate.Triggers>
            <Trigger Property="Button.IsPressed" Value="True">
                <Setter Property="Image.Source" TargetName="imgDefault" Value="{Binding Path=PressedImageSource,ElementName=uc}"></Setter>
                <Setter Property="UIElement.Effect">
                    <Setter.Value>
                        <DropShadowEffect BlurRadius="10" Color="Black" Direction="0" Opacity="0.6" RenderingBias="Performance" ShadowDepth="0" />
                    </Setter.Value>
                </Setter>
            </Trigger>
            <Trigger Property="Button.IsMouseOver" Value="True">
                <Setter Property="UIElement.Effect">
                    <Setter.Value>
                        <DropShadowEffect BlurRadius="10" Color="White" Direction="0" Opacity="0.6" RenderingBias="Performance" ShadowDepth="0" />
                    </Setter.Value>
                </Setter>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
tning
  • 11
  • 1
-1

Simply try this code

    <Button Name="btn_Refresh" Grid.Column="0" Height="25" Width="25" HorizontalAlignment="Right" VerticalAlignment="Center" Background="White" Margin="0,0,10,0" Click="btn_Refresh_Click">
                        <Button.Content>
                            <Grid>
                                <Path Width="15" Height="15" Fill="Blue" Stretch="Fill" Data="F1 M 38,20.5833C 42.9908,20.5833 47.4912,22.6825 50.6667,26.046L 50.6667,17.4167L 55.4166,22.1667L 55.4167,34.8333L 42.75,34.8333L 38,30.0833L 46.8512,30.0833C 44.6768,27.6539 41.517,26.125 38,26.125C 31.9785,26.125 27.0037,30.6068 26.2296,36.4167L 20.6543,36.4167C 21.4543,27.5397 28.9148,20.5833 38,20.5833 Z M 38,49.875C 44.0215,49.875 48.9963,45.3932 49.7703,39.5833L 55.3457,39.5833C 54.5457,48.4603 47.0852,55.4167 38,55.4167C 33.0092,55.4167 28.5088,53.3175 25.3333,49.954L 25.3333,58.5833L 20.5833,53.8333L 20.5833,41.1667L 33.25,41.1667L 38,45.9167L 29.1487,45.9167C 31.3231,48.3461 34.483,49.875 38,49.875 Z " Margin="-8.3,-2,-5.701,0"/>
                            </Grid>
                        </Button.Content>
                    </Button>
Joee
  • 1,834
  • 18
  • 19
-3

The simple way in WPF:

  1. Create a button image with Photoshop or other programs.

  2. Place it on your window and use the MouseEnter and MouseLeave events.

    private void img1_MouseEnter(object sender, MouseEventArgs e)
    {
        Cursor = Cursors.Hand;
    }
    
    private void img1_MouseLeave(object sender, MouseEventArgs e)
    {
        Cursor = Cursors.Arrow;
    }
    
ChrisF
  • 134,786
  • 31
  • 255
  • 325
Ilan
  • 13
  • 1
  • 2
    The question clearly states that they need an image button with a template. A photoshop button does not constitute a template of any kind, an besides you don't need to handle the MouseEnter and MouseLeave events to change the cursor, you have the Cursor property for that. One more thing, don't asume that the normal cursor is an arrow, it might be something else, you can set the Cursor to null to return to normal. – Hannish Apr 06 '13 at 15:59