Question

I am having trouble getting this button (with an image in its content) to correctly change the ImageSource on click. Here is my Xaml and Code Behind.

XAML:

<Window.Resources>
    <Style x:Key="NoChromeButton" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Padding" Value="1"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid x:Name="Chrome" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Foreground" Value="#ADADAD"/>
                            <Setter Property="Opacity" TargetName="Chrome" Value="0.5"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<Grid>
    <Button x:Name="DrawCircleButton" Height="56" Width="157" 
    Margin="10,10,0,0" VerticalAlignment="Top" 
    HorizontalAlignment="Left" Click="DrawCircleButtonClick"
            Style="{DynamicResource NoChromeButton}"
    >
        <Image x:Name="imgButtonState" >
            <Image.Source>
                <BitmapImage UriSource="Resources/off.gif" />
            </Image.Source>
        </Image>
    </Button>
    <ScrollViewer Margin="0,50,0,0">
        <TextBlock Name="textBlock1" TextWrapping="Wrap" FontSize="20" FontWeight="Bold" />
    </ScrollViewer>


</Grid>

Code Behind:

private void DrawCircleButtonClick(object sender, RoutedEventArgs e)
    {
        var t = ButtonState;
        ButtonState = t;
    }

public bool ButtonState
    {
        get
        {
            return (bool)GetValue(ButtonStateProperty);
        }
        set
        {
            var t = new BitmapImage(new Uri("Resource/on.gif", UriKind.Relative));

            DrawCircleButton.Content = !value ? imgButtonState.Source = new BitmapImage(new Uri("on.gif", UriKind.Relative)) : imgButtonState.Source = new BitmapImage(new Uri("off.gif", UriKind.Relative));
            SetValue(ButtonStateProperty, !value);
        }
    }

    public static readonly DependencyProperty ButtonStateProperty = DependencyProperty.Register("ButtonState", typeof(bool), typeof(bool));

So initially, the button is set to 'off'. But as it gets clicked, it toggles between 'on' and 'off'. I'm sure I did something wrong cause what gets displayed is text to the path of the images. Any ideas?

Was it helpful?

Solution

First of all, WPF also has a ToggleButton. Perhaps that might be more appropriate here.

Now your error. It is basically in the following line:

DrawCircleButton.Content = !value ? imgButtonState.Source = new BitmapImage(new Uri("on.gif", UriKind.Relative)) : imgButtonState.Source = new BitmapImage(new Uri("off.gif", UriKind.Relative));

You assign a new BitmapImage to the Content property of the Button. Since the Button's ContentPresenter can't deal with that type, it simply displays the result of ToString(). It would work if you would simply drop the assignment and write the following. The Content isn't changed, simply the source of the Image that is already the Button's Content.

imgButtonState.Source = value
    ? new BitmapImage(new Uri("on.gif", UriKind.Relative))
    : new BitmapImage(new Uri("off.gif", UriKind.Relative));

There is however still a severe problem with the definition of your dependency property ButtonState. If you wanted to define it like this, you would have to derive from Button and write the declaration like below:

public class MyButton : Button
{
    public static readonly DependencyProperty ButtonStateProperty =
        DependencyProperty.Register(
            "ButtonState", typeof(bool), typeof(MyButton),
            new FrameworkPropertyMetadata(ButtonStatePropertyChanged));

    public bool ButtonState
    {
        get { return (bool)GetValue(ButtonStateProperty); }
        set { SetValue(ButtonStateProperty, value); }
    }

    private static void ButtonStatePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        ((MyButton)obj).imgButtonState.Source = (bool)e.NewValue
            ? new BitmapImage(new Uri("on.gif", UriKind.Relative))
            : new BitmapImage(new Uri("off.gif", UriKind.Relative));
    }
}

You should also not do anything else expect GetValue/SetValue in the CLR wrappers of a dependency property. Instead you should use a PropertyChangedCallback as shown above. See the Implementing the Wrapper section in Checklist for Defining a Dependency Property.

If you don't want to derive from Button, you could alternatively define ButtonState as an attached property.

OTHER TIPS

Use Image instead of BitmapImage as the Button's content. Also, use Image.Source = "/Resources/YourFile.png" instead of declaring the bitmapimage in XAML and in code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top