ToggleButton 根据状态改变图片

16

我想以以下方式使用ToggleButton: 有5张不同的图片,每张图片都应根据当前状态显示:

  1. 按钮禁用
  2. 按钮启用,未选中
  3. 按钮启用,未选中,鼠标指针指向
  4. 按钮启用,已选中
  5. 按钮启用,已选中,鼠标指针指向

我在这里找到了一个简单的示例,其中包含两个图像,但是如何根据“checked”属性更改图像呢?

第二个问题:如何避免为应用程序中的每个按钮创建不同的样式? 我正在使用约20个不同的按钮,每个按钮都有不同的图标集。

到目前为止,我只使用了一个图标,下面是我的代码。是否可以有通用代码(样式和模板)并在我要创建按钮的部分中定义图像来源(例如在我的代码的第3部分中)?

<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>
7个回答

45

这个解决方案是一个简单的方案:

 <ToggleButton IsChecked="{Binding IsCheckedState}">
            <Image Width="24" Height="24"  >
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsCheckedState}" Value="true">
                                <Setter Property="Source" Value="Images/checked.ico"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding IsCheckedState}" Value="false">
                                <Setter Property="Source" Value="Images/unchecked.ico"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </ToggleButton>

2
简而言之,“IsCheckedState”是 VM 中的属性,与 WPF 无关。即,如果您有一个要绑定的 VM 属性“IsNewUser”,则可以将上面示例中的“IsCheckedState”替换为“IsNewUser”。 - JsAndDotNet

12

通过创建一个公开依赖属性的UserControl来实现所需的功能,其中包括Command、IsChecked和每个有状态图片的属性。你的用户控件将包含一个切换按钮和图像。

您可以使用MultiDataTriggers检测状态并根据状态切换图像。

由于您公开了有状态图像的DependencyProperties,它们可以在声明控件的任何位置使用数据绑定进行设置。一旦状态改变,触发器会自动为您切换图像源。

[编辑:添加一些代码以帮助说明]

这里是一个部分示例,可以让您开始:

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>

还有 cs 文件:

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
        }



    }
}

此控件可如下使用:

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

6

Ed Gonzalez先生,感谢您的好榜样。

唯一的问题是绑定到MyToggleButton.IsChecked依赖属性不起作用(平台:Windows 7,.NET 4.0,VS2010)。因此,我对您的示例进行了一些更改。

xaml:

<ToggleButton x:Class="MyApp.ToggleButtonEx"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         Checked="ToggleButton_CheckedChanged"
         Unchecked="ToggleButton_CheckedChanged"
         IsEnabledChanged="ToggleButton_IsEnabledChanged"
         Loaded="ToggleButton_Loaded">
    <Image x:Name='ButtonImage'/>
</ToggleButton>

cs:

public partial class ToggleButtonEx : ToggleButton
{
    public ToggleButtonEx()
    {
        InitializeComponent();            
    }

    public static readonly DependencyProperty EnabledUncheckedProperty =
        DependencyProperty.Register(
        "EnabledUnchecked",
        typeof(ImageSource),
        typeof(ToggleButtonEx),
        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(ToggleButtonEx),
        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(ToggleButtonEx),
        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
    }

    private void ToggleButton_CheckedChanged(object sender, RoutedEventArgs e)
    {
        ChangeImage();
    }

    private void ToggleButton_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        ChangeImage();
    }

    private void ToggleButton_Loaded(object sender, RoutedEventArgs e)
    {
        ChangeImage();
    }

    private void ChangeImage()
    {
        if (IsEnabled)
        {
            if(IsChecked == true)
                ButtonImage.Source = EnabledChecked;
            else
                ButtonImage.Source = EnabledUnchecked;
        }
        else
        {
            ButtonImage.Source = DisabledUnchecked;
        }
    }
}

使用模式保持不变:

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

这对我非常有效(而且我学到了一两件事!)。干得好! - Flea

1
我对我的RibbonToggleButton做了同样的事情,但我认为这更容易。我将样式触发器放在按钮内部,而不是使用额外的图像元素。
 <RibbonToggleButton Label="{x:Static p:Resources.Main_Connect}" Command="{Binding ConnectRemoteCommand}" CommandParameter="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}}">
                        <RibbonToggleButton.Style>
                            <Style TargetType="{x:Type RibbonToggleButton}">
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}}" Value="true">
                                        <Setter Property="LargeImageSource" Value="../../Resources/Images/GPS-On.png"/>
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}}" Value="false">
                                        <Setter Property="LargeImageSource" Value="../../Resources/Images/GPS-Off.png"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </RibbonToggleButton.Style>
                    </RibbonToggleButton>

1

只是因为我不想搞砸我的切换按钮模板才使用bool到可见性转换器来实现与上面相同的行为。而且它非常简单。现在我使用XAML向量图像,所以我目前正在使用内容控件,但是你可以轻松地将其替换为图像。这使我可以保持我的切换按钮样式纯净,这样我就可以将它们默认设置为整个应用程序,而无需为每个按钮编写自定义样式。

<UserControl.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
    <converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" />
</UserControl.Resources>

<ToggleButton>
    <Grid>
        <ContentControl Visibility="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={x:Type ToggleButton}}, Converter={StaticResource BoolToVisConverter}}" Content="{StaticResource Play}" />
        <ContentControl Visibility="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={x:Type ToggleButton}}, Converter={StaticResource InverseBooleanToVisibilityConverter}}" Content="{StaticResource Pause}" />
    </Grid>
</ToggleButton>

<ToggleButton>
    <Grid>
        <Image Visibility="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={x:Type ToggleButton}}, Converter={StaticResource BoolToVisConverter}}" Source="play.png" />
        <Image Visibility="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={x:Type ToggleButton}}, Converter={StaticResource InverseBooleanToVisibilityConverter}}" Source="pause.png" />
    </Grid>
</ToggleButton>

0
另一种方法是在ToggleButton本身上使用样式。将图像添加为内容,并通过对IsChecked属性的触发器来更改图像。
    <ToggleButton Name="ExpandButton" Grid.Row="1" Grid.Column="0" IsChecked="{Binding IsCheckedState}">
      <ToggleButton.Template>
        <ControlTemplate TargetType="ToggleButton">
         <Grid>
            <Image Name="LogoImage" Stretch="Uniform" Source="checked.png"/>
          </Grid> 

          <ControlTemplate.Triggers>
            <Trigger Property="IsChecked" Value="true">
              <Setter TargetName="LogoImage" Property="Source" Value="checked.png"/> 
            </Trigger>
            <Trigger Property="IsChecked" Value="false">
              <Setter TargetName="LogoImage" Property="Source" Value="unchecked.png"/>
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </ToggleButton.Template>  
    </ToggleButton>

-1
先生,感谢您提供的好例子。
唯一的问题是绑定到 MyToggleButton.IsChecked 依赖属性不能正常工作(平台:Windows 7、.NET 4.0、VS2010)。因此,我对您的示例进行了一些更改。
只需在 IsChecked DependencyProperty 上移除 static,将您的 ChangeImage() 添加到 IsChecked() 中,Sir Ed Gonzalez 的示例就可以正常工作了 ^^
public readonly DependencyProperty IsCheckedProperty =
            DependencyProperty.Register(
            "IsChecked" ...

public Boolean IsChecked
... if (value != IsChecked) SetValue(IsCheckedProperty, value); ChangeImage();

3
不行,这些 get/set 方法没有被框架调用。 - Louis Kottmann

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