Customizing the toggle state of a toggle button in wpf
Asked Answered
G

4

9

I wanna customize the toggle state of the toggle button in wpf. I want to set a image to the toggle button when it is on and set another image when it is off. TO do so, i thought of using triggers. This is how i ended up doing,

<Window ...>
    <Window.Resources>
        <Image x:Key="OnImage" Source="C:\ON.jpg" />
        <Image x:Key="OffImage" Source="C:\OFF.jpg" />
        <Style x:Key="OnOffToggleImageStyle" TargetType="ToggleButton">
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Content" Value="{StaticResource OnImage}" />
                </Trigger>
                <Trigger Property="IsChecked" Value="False">
                    <Setter Property="Content" Value="{StaticResource OffImage}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <ListBox>
        <ListBox.ItemTemplate>
            <DataTemplate>
                ...
                <ToggleButton IsChecked="{Binding Status}" Width="100" Height="35" Style="{StaticResource OnOffToggleImageStyle}" />
                ...
            </DataTemplate>
        </ListBox.ItemTemplate>
</Window>

The above snippet seems to works fine only for two items in the list box. If more than one item has the binding value, status to be true, it doesn't work (it works for only one such item). Please tell me whether I am proceeding in the correct direction. Also tell me other ways of achieving this.

Gelatinous answered 7/10, 2009 at 16:2 Comment(0)
E
10

The issue here is because you are using Image resources. The Image in your resources is a concrete instance of a control. It can only be in one place at a time. So when you have more than one item in your list...

This should work for you:

<Style x:Key="OnOffToggleImageStyle" TargetType="ToggleButton">
 <Style.Triggers>
   <Trigger Property="IsChecked" Value="True">
     <Setter Property="Content">
       <Setter.Value>
         <Image Source="C:\ON.jpg" />
       </Setter.Value>
     </Setter>
   </Trigger>
   <Trigger Property="IsChecked" Value="False">
     <Setter Property="Content">
       <Setter.Value>
         <Image Source="C:\OFF.jpg" />
       </Setter.Value>
     </Setter>
   </Trigger>
 </Style.Triggers>
</Style>

Note that you can improve the performance of this by using an ImageSource for each image file in your resources, then referencing this in the Image. This effectively means that each image is only loaded once from disk, rather than 2*N times (where N is the number of items in your list.)

Ephraim answered 7/10, 2009 at 16:9 Comment(1)
The above snippet is throwing an exception. Here are the details, Cannot add content of type 'System.Windows.Controls.Image' to an object of type 'System.Object'. Error at object 'System.Windows.Controls.Image' in markup fileGelatinous
C
6

This answer will help you. In there I took a ToggleButton and styled it to look as the ToggleButton in a TreeView (the + / - part to expand collapse nodes). You'll just need to change the paths that draw the - and + signs, to show your images instead.

Here goes personalized to you, just put an image called "on.jpg" and another one called "off.jpg" under your C:\ directory, and it should work by just copy/pasting into your window:

    <Window.Resources>
        <SolidColorBrush x:Key="GlyphBrush" Color="#444" />
        <ControlTemplate x:Key="toggleButtonTemplate" TargetType="ToggleButton">
            <Grid
                Width="15"
                Height="13"
                Background="Transparent">
                <Image x:Name="ExpandImage"
                      Source="C:\off.jpg"
                      HorizontalAlignment="Left" 
                      VerticalAlignment="Center" 
                      Margin="1,1,1,1" />                     
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsChecked"
                     Value="True">
                    <Setter Property="Source"
                      TargetName="ExpandImage"
                      Value="C:\on.jpg"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
        <Style x:Key="toggleButtonStyle" TargetType="ToggleButton">
            <Setter Property="Template" Value="{StaticResource toggleButtonTemplate}" />
        </Style>
    </Window.Resources>
    <Grid>
        <ToggleButton Style="{StaticResource toggleButtonStyle}" />
    </Grid>
Coracorabel answered 7/10, 2009 at 16:17 Comment(4)
What is ExpandImage in the above snippet ? There are no trigger types called ExpandImage. Could you please check it out ?Gelatinous
The name of the image inside your template: <Image x:Name="ExpandImage" ... />. This is so the ControlTemplate can access it and modify its properties: <Setter Property="Source" TargetName="ExpandImage" ... />Coracorabel
Sorry, I didn't read it fully. Thanks. Its working now. Why wasn't it working for me ? Why was it coming only once ??Gelatinous
I'm not really sure right now, when I get 10 mins I'll take a look at your code and let you know what was wrong.Coracorabel
P
3

Here is a ToggleButton with 3 images and a popup:

  1. An image for when IsChecked = false.
  2. An image for when IsChecked = true.
  3. An image for when IsMouseOver = true.

The images are stored in resources as BitmapImage in order to avoid changing Visuals on trigers.

The image files have to be added to resources and then, the files added to the "Resoruces" folder in the project have to be marked as BuildAction = Resource.

It also applies an opacity to the Image control when the ToggleButton IsEnabled = false;

Code:

    <ToggleButton
        x:Name="btnToggleImage"
        Margin="5"
        Width="50"            
        Height="50"
        >
        <ToggleButton.Resources>
            <BitmapImage x:Key="imgNormal" UriSource="/YOURPROJECTNAME;component/Resources/YourUncheckedImage.png"/>
            <BitmapImage x:Key="imgHover" UriSource="/YOURPROJECTNAME;component/Resources/YourHoverImage.png"/>
            <BitmapImage x:Key="imgChecked" UriSource="/YOURPROJECTNAME;component/Resources/YourCheckedImage.png"/>
        </ToggleButton.Resources>

        <ToggleButton.Style>
            <Style TargetType="ToggleButton">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ToggleButton">
                            <Image
                                x:Name="PART_Image"
                                Source="{StaticResource imgNormal}"
                                />
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsChecked" Value="true">
                                    <Setter TargetName="PART_Image" Property="Source" Value="{StaticResource imgChecked}"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="true">
                                    <Setter TargetName="PART_Image" Property="Source" Value="{StaticResource imgHover}"/>
                                </Trigger>
                                 <Trigger Property="IsEnabled" Value="false">
                                    <Setter TargetName="PART_Image" Property="Opacity" Value="0.6"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate> 
                    </Setter.Value>
                </Setter>
            </Style>
        </ToggleButton.Style>
    </ToggleButton>        

    <Popup
        x:Name="popup1"
        PlacementTarget="{Binding ElementName=btnToggleImage}"
        PopupAnimation="Slide"
        IsOpen="{Binding ElementName=btnToggleImage, Path=IsChecked, Mode=TwoWay}"
        StaysOpen="False"
        MinWidth="{Binding ElementName=btnToggleImage, Path=Width}">
        <Grid Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
            <!--<ItemsPresenter/>-->
            <Label Content="Hello Wolrd!"/>
        </Grid>
    </Popup>
Puissant answered 22/3, 2012 at 11:48 Comment(0)
G
1

Like Drew Noakes said, in my snippte there were only two images. So only two items were working properly. I solved this issue with the following snippet.

<ToggleButton
    Grid.Row="0"  Grid.Column="2"  Grid.RowSpan="2"
    VerticalAlignment="Center" HorizontalAlignment="Center"
    IsChecked="{Binding Status}"
    Width="100" Height="35">
  <ToggleButton.Resources>
    <Image x:Key="OnImage" Source="C:\ON.jpg" />
    <Image x:Key="OffImage" Source="C:\OFF.jpg" />
  </ToggleButton.Resources>
  <ToggleButton.Style>
    <Style TargetType="ToggleButton">
      <Style.Triggers>
        <Trigger Property="IsChecked" Value="True">
          <Setter Property="Content" Value="{StaticResource OnImage}">
          </Setter>
        </Trigger>
        <Trigger Property="IsChecked" Value="False">
          <Setter Property="Content" Value="{StaticResource OffImage}">
          </Setter>
        </Trigger>
      </Style.Triggers>
    </Style>
  </ToggleButton.Style>
</ToggleButton>

Simple, I moved the triggers in to the data template. Dunno whether this is the correct answer thou'. Seems to be working

Gelatinous answered 7/10, 2009 at 17:21 Comment(1)
Interesting, I get the Error 'System.Windows.Controls.Image' is not a valid value for 'Setter.Value'; values derived from Visual or ContentElement are not supported.Tbilisi

© 2022 - 2024 — McMap. All rights reserved.