在WPF中自定义切换按钮的切换状态

9

我希望自定义wpf中切换按钮的状态。当开启时,我想为切换按钮设置一张图片,关闭时则设置另一张图片。为此,我考虑使用触发器。以下是我的做法:

<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>

上面的代码片段似乎只适用于列表框中的两个项目。如果有多个项目具有绑定值,并且状态为true,则它将无法正常工作(仅适用于一个此类项目)。请告诉我是否在正确的方向上进行。还请告诉我实现此目标的其他方法。

4个回答

10

这里的问题是因为您正在使用图片资源。您的资源中的图像是控件的具体实例。它一次只能在一个位置上。因此,当您的列表中有多个项时...

以下内容应该适用于您:

<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>

请注意,您可以通过为资源中的每个图像文件使用ImageSource,然后在Image中引用它来提高性能。这有效地意味着每个图像仅从磁盘加载一次,而不是2 * N次(其中N是列表中的项目数)。


3
以上代码片段抛出了一个异常。以下是详细信息,无法将类型为“System.Windows.Controls.Image”的内容添加到类型为“System.Object”的对象中。位于标记文件中的对象“System.Windows.Controls.Image”处发生错误。 - sudarsanyes

6
这个答案可以帮助您。在那里,我使用了一个ToggleButton并将其样式设置为TreeView中的ToggleButton(+ / -部分以展开折叠节点)。您只需要更改绘制-和+符号的路径,以显示您自己的图像即可。
以下是针对您的个性化建议:只需在C:\目录下放置名为“on.jpg”和“off.jpg”的图像,然后将其复制/粘贴到您的窗口中即可。
    <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>

以上片段中的ExpandImage是什么?没有叫做ExpandImage的触发器类型,请你检查一下。 - sudarsanyes
模板中图像的名称:<Image x:Name="ExpandImage" ... />。这样,控件模板就可以访问它并修改其属性:<Setter Property="Source" TargetName="ExpandImage" ... />。 - Carlo
抱歉,我没有完全阅读它。谢谢。现在它可以工作了。为什么对我不起作用?为什么它只出现一次? - sudarsanyes
我现在不太确定,等我有10分钟的时间,我会看一下你的代码并告诉你哪里出了问题。 - Carlo

3
以下是一个具有3个图像和弹出窗口的ToggleButton:
  1. 当IsChecked = false时显示的图像。
  2. 当IsChecked = true时显示的图像。
  3. 当IsMouseOver = true时显示的图像。
这些图像作为BitmapImage存储在资源中,以避免在触发器上更改可视元素。
图像文件必须添加到资源中,然后将添加到项目中的“资源”文件夹标记为BuildAction = Resource。
当ToggleButton IsEnabled = false时,它还会对Image控件应用不透明度。
代码:
    <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>

1
就像Drew Noakes所说,在我的代码片段中只有两个图片。因此只有两个项目可以正常工作。我用以下代码片段解决了这个问题。
<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>

很简单,我将触发器移动到了数据模板中。虽然不确定这是否是正确的答案,但似乎正在工作。


有趣,我得到了“System.Windows.Controls.Image”不是“Setter.Value”的有效值的错误;不支持从Visual或ContentElement派生的值。 - Andy Dent
感谢您发布这个答案。 - Tony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接