3

I'm trying to create a <Button/> with a png image for the background AND a background color.

Note: The png image source may not yet be known, so I can't just put them in the template.

I've got a Style that looks like this:

<Style x:Key="MyButton" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border CornerRadius="0">
                    <Image Margin="0" Source="/MyApp;component/Images/my-image.png" Stretch="None" />
                    <Border.Style>
                        <Style TargetType="{x:Type Border}">
                            <Setter Property="Background" Value="LimeGreen" />
                        </Style>
                    </Border.Style>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

which gives me a button with a png image on top of a solid LimeGreen background.

My MainWindow.xaml looks somewhat like this:

<Button Style="{StaticResource MyButton}" Click="btnMine_Click" /* some other props */ />

and the code behind it is like this:

private void btnMine_Click(object sender, RoutedEventArgs e)
{
    // TODO: change the image src on this button
    var myButton = sender as Button;

    // This won't work:
    // myButton.Border.Image.Source = getNewImageString();
}

How do I change the image source from /MyApp2;component/Images/my-image.png to something else?

8
  • p.s. manually editing a .xaml file is not programmatic, there seem to be several answers here on SO that seem to think so. :-/ Commented Apr 4, 2013 at 19:14
  • do you want it exactly like that (your click handler)? And can you change the XAML at all? Making it 'all programmatic' only makes sense if you're unable to 'touch' that Style. Commented Apr 4, 2013 at 19:25
  • Sure, I can edit the Style, but there are just too many buttons for that to be realistic. All the buttons, however, share the same background color(s). Commented Apr 4, 2013 at 19:30
  • And no, not exactly like that, its just an example. What I want to do is to take a button Object and change its background image after certain events (click, mouseenter, window resize, etc). Make sense? Commented Apr 4, 2013 at 19:32
  • add click trigger event to button and change background in template <ControlTemplate.Triggers> <Trigger Property="Button.IsPressed" Value="True"> <Setter Property="Image.Source" Value=",,,"></Setter> </Trigger> </ControlTemplate.Triggers> Commented Apr 4, 2013 at 19:32

2 Answers 2

2

Sigh, in Windows Forms it was literally one line. In WPF it looks like it will be several hundred.

That's because it was the ONLY thing you could place in a button in winforms. WPF is a true UI framework, not some random semi-deprecated dinosaur that only allows to do the default stuff (which by the way looks horrible).

Option 1: Place the Image as Button's Content:

If you just want to place an Image in the button, and nothing else, why not just do that?

        <Style TargetType="Button">
            <Setter Property="Background" Value="LimeGreen"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}">
                            <ContentPresenter ContentSource="Content"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

Then:

     <Button>
        <!-- Background not set, defaults to what's set in the Style -->
        <Image Source="./ChessPieces/BlackKnight.png"/>
    </Button>
    <Button Background="Red">
        <Image Source="./ChessPieces/BlackBishop.png"/>
    </Button>
    <Button Background="Blue">
        <Image Source="./ChessPieces/BlackPawn.png"/>
    </Button>

    <!-- WPF's idea of "Dynamic" is not the same as win(hack)forms' -->
    <Button Background="White">
        <Image Source="{Binding SomeStringPropertyDefinedInAViewModel}"/>
    </Button>

Result:

enter image description here

Option 2 (not very elegant): Use the Tag Property:

If, in addition to the Image, you want to put something else inside the Button, you can resort to a somewhat hacky approach of putting the ImageSource in the Button's Tag property.

        <Style TargetType="Button">
            <Setter Property="Background" Value="LimeGreen"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}">
                            <StackPanel Orientation="Horizontal">
                                <Image Source="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"
                                       Height="50" Width="50"/>
                                <ContentPresenter ContentSource="Content"/>
                            </StackPanel>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

Then:

    <Button Tag="./ChessPieces/BlackKnight.png"
            Background="Blue"
            Foreground="White"
            Content="Some Text"/>

    <Button Tag="./ChessPieces/BlackBishop.png"
            Background="LightGray">
        <CheckBox Content="A CheckBox!" VerticalAlignment="Center"/>
    </Button>

Result:

enter image description here

Option 3: Use an Attached Property

Same as option 2, but using a property declared elsewhere, for example:

 <Button ButtonImage.Source="./ChessPieces/BlackBishop.png"
         <!-- etc -->

Option 4: Create a Custom Control:

Create a class (.cs file with no XAML) derived from Button and add a DependencyProperty to hold the image, then set the template to that and use that value.

Option 5: MVVM

Create a ButtonViewModel, or actually use a DelegateCommand declared in the ViewModel to bind the Button's properties.

Not an Option: Traverse the Visual Tree and change the Image.Source in code.

That's not something you will want to do. It's not a good approach at all.

I could go on forever, but I have to go to sleep. If you want me to elaborate on any of these approaches just let me know.

Sign up to request clarification or add additional context in comments.

2 Comments

I went with something similar to "Option 1: Place the Image as Button's Content". Thanks!
I didn't use a button though; I couldn't figure out how to style a button's hover and pressed states.
0

A few ways spring to mind:

  1. Access the style at runtime and traverse it to find the image
  2. Give the image a name and reference that at runtime and just change the source

The best solution I believe is to create a usercontrol and have the image source as a dependency property see: http://www.codeproject.com/Articles/224230/Exploring-the-use-of-Dependency-Properties-in-User

That way you'll have a ready-to-use "custom" control that supports what you're after and allows you to just reference it directly and use bindings, that way you avoid the messy solutions from using a style.


After discussion and expansion on requirements, please see:

How can I access ResourceDictionary in wpf from C# code?

10 Comments

Access the style at runtime and traverse it to find the image that is essentially what I want to do, but I can't figure out how.
@DavidMurdoch where is the style defined? That will completely influence how you access it at runtime, traversal would be trickier than simply creating a usercontrol.
It's in a file named "Templates.xaml" (a ResourceDictionary), which is referenced in my App.xaml.
I may have misunderstood you, I don't think I want to access the Style itself, because it will be applied to many buttons. I just want to override the style's "properties" (or whatever they'd be called in xaml) on specific buttons.
@DavidMurdoch this isn't possible afaik, the only way of having button-specific overrides would be by using a UserControl.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.