mack369 mack369 - 4 months ago 28
C# Question

ToggleButton changing image depending on state

I would like to use ToggleButton in following way:
There are 5 different images and each of them should be displayed depending on current state:


  1. button disabled

  2. button enabled, unchecked

  3. button enabled, unchecked, pointed by mouse cursor

  4. button enabled, checked

  5. button enabled, checked, pointed by mouse cursor



I've found a simple example with two images here , but how to change the image depending on "checked" property?

The second question: how can I avoid creating different styles for each button in my application? I'm using about 20 different buttons and each of them has different set of icons.

So far I'm using only one icon, below my code. Is it possible to have common code (style and template) and to define the source of images in section where I want to create button (like in section 3 of my code)?

<ControlTemplate x:Key="ToggleButtonTemplate" TargetType="{x:Type ToggleButton}">
<Grid>
<Border x:Name="ContentBorder" CornerRadius="4" BorderBrush="Transparent" BorderThickness="1" Background="{DynamicResource ButtonOff}">
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="ContentBorder" Property="Background" Value="{DynamicResource ButtonOn}"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="ContentBorder" Property="Background" Value="{DynamicResource ButtonOn}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="ContentBorder" Property="Background" Value="{DynamicResource ButtonDisabled}"/>
<Setter Property="Foreground" Value="{DynamicResource BorderDisabled}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

<Style x:Key="ToggleButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Width" Value="64" />
<Setter Property="Height" Value="64" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template" Value="{DynamicResource ToggleButtonTemplate}" />
</Style>

<ToggleButton IsChecked="{Binding Path=IsLectorModeEnabled}" Command="{Binding CmdLector}" Style="{DynamicResource ToggleButtonStyle}">
<Image Source="{DynamicResource LectorImage}" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="None" />
</ToggleButton>

Answer

You can get the functionality you want by creating a UserControl that exposes dependency properties for Command, IsChecked, and one for each stateful image. Your user control will contain a toggle button and image.

You can use MultiDataTriggers to detect your state and swtich the image depending on the state.

Because you exposed the DependencyProperties for the stateful images, they can be set using Databinding wherever you declare your control. The triggers will automatically switch the image source for you, once state changes.

[Edit: Added some code to help explain]

Here is a partial example to get you started:

MyToggleButton.xaml:

<UserControl x:Class="ToggleTest.MyToggleButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ToggleButton
    IsChecked='{Binding RelativeSource={RelativeSource FindAncestor, 
    AncestorType={x:Type ToggleButton} }, 
    Path=IsChecked}'>
    <Image
        x:Name='ButtonImage'>
        <Image.Style>
            <Style
                TargetType='{x:Type Image}'>
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition
                                Binding='{Binding 
                                RelativeSource={RelativeSource FindAncestor, 
                                AncestorType={x:Type ToggleButton} }, 
                                Path=IsChecked}'
                                Value='True' />
                            <Condition
                                Binding='{Binding 
                                RelativeSource={RelativeSource Self}, 
                                Path=IsEnabled}'
                                Value='True' />
                        </MultiDataTrigger.Conditions>
                        <Setter
                            Property='Source'
                            Value='{Binding 
                            RelativeSource={RelativeSource FindAncestor, 
                            AncestorType={x:Type UserControl} }, 
                            Path=EnabledChecked}' />
                    </MultiDataTrigger>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition
                                Binding='{Binding 
                                RelativeSource={RelativeSource FindAncestor, 
                                AncestorType={x:Type ToggleButton} }, 
                                Path=IsChecked}'
                                Value='False' />
                            <Condition
                                Binding='{Binding 
                                RelativeSource={RelativeSource Self}, 
                                Path=IsEnabled}'
                                Value='True' />
                        </MultiDataTrigger.Conditions>
                        <Setter
                            Property='Source'
                            Value='{Binding 
                            RelativeSource={RelativeSource FindAncestor, 
                            AncestorType={x:Type UserControl} }, 
                            Path=EnabledUnchecked}' />
                    </MultiDataTrigger>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition
                                Binding='{Binding 
                                RelativeSource={RelativeSource FindAncestor, 
                                AncestorType={x:Type ToggleButton} }, 
                                Path=IsChecked}'
                                Value='False' />
                            <Condition
                                Binding='{Binding 
                                RelativeSource={RelativeSource Self}, 
                                Path=IsEnabled}'
                                Value='False' />
                        </MultiDataTrigger.Conditions>
                        <Setter
                            Property='Source'
                            Value='{Binding 
                            RelativeSource={RelativeSource FindAncestor, 
                            AncestorType={x:Type UserControl} }, 
                            Path=DisabledUnchecked}' />
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </Image.Style>
    </Image>
</ToggleButton>

And the cs file:

using System;

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;

    namespace ToggleTest

{
    /// <summary>
    /// Interaction logic for ToggleButton.xaml
    /// </summary>
    public partial class MyToggleButton : UserControl
    {
        public MyToggleButton()
        {
            InitializeComponent();
        }


        public static readonly DependencyProperty EnabledUncheckedProperty =
            DependencyProperty.Register(
            "EnabledUnchecked",
            typeof(ImageSource),
            typeof(MyToggleButton),
            new PropertyMetadata(onEnabledUncheckedChangedCallback));

        public ImageSource EnabledUnchecked
        {
            get { return (ImageSource)GetValue(EnabledUncheckedProperty); }
            set { SetValue(EnabledUncheckedProperty, value); }
        }

        static void onEnabledUncheckedChangedCallback(
            DependencyObject dobj,
            DependencyPropertyChangedEventArgs args)
        {
            //do something if needed
        }

        public static readonly DependencyProperty DisabledUncheckedProperty =
            DependencyProperty.Register(
            "DisabledUnchecked",
            typeof(ImageSource),
            typeof(MyToggleButton),
            new PropertyMetadata(onDisabledUncheckedChangedCallback));

        public ImageSource DisabledUnchecked
        {
            get { return (ImageSource)GetValue(DisabledUncheckedProperty); }
            set { SetValue(DisabledUncheckedProperty, value); }
        }

        static void onDisabledUncheckedChangedCallback(
            DependencyObject dobj,
            DependencyPropertyChangedEventArgs args)
        {
            //do something if needed
        }


        public static readonly DependencyProperty EnabledCheckedProperty =
            DependencyProperty.Register(
            "EnabledChecked",
            typeof(ImageSource),
            typeof(MyToggleButton),
            new PropertyMetadata(onEnabledCheckedChangedCallback));

        public ImageSource EnabledChecked
        {
            get { return (ImageSource)GetValue(EnabledCheckedProperty); }
            set { SetValue(EnabledCheckedProperty, value); }
        }

        static void onEnabledCheckedChangedCallback(
            DependencyObject dobj,
            DependencyPropertyChangedEventArgs args)
        {
            //do something if needed
        }


        public static readonly DependencyProperty IsCheckedProperty =
            DependencyProperty.Register(
            "IsChecked",
            typeof(Boolean),
            typeof(MyToggleButton),
            new PropertyMetadata(onCheckedChangedCallback));

        public Boolean IsChecked
        {
            get { return (Boolean)GetValue(IsCheckedProperty); }
            set { if(value != IsChecked) SetValue(IsCheckedProperty, value); }
        }

        static void onCheckedChangedCallback(
            DependencyObject dobj,
            DependencyPropertyChangedEventArgs args)
        {
            //do something, if needed
        }



    }
}

This control could be used like so:

<local:MyToggleButton
            IsChecked='True'
            IsEnabled='False'
            EnabledChecked='<add your image source here>'
            EnabledUnchecked='<add your image source here>'
            DisabledUnchecked='<add your image source here>'/>
Comments