【问题标题】:Style WPF radio button as toggle button with correct IsEnabled behaviour将 WPF 单选按钮设置为具有正确 IsEnabled 行为的切换按钮
【发布时间】:2017-02-07 11:48:51
【问题描述】:

我需要将一些分组的单选按钮设置为切换按钮。为此,我将以下样式应用于单选按钮:

Style="{StaticResource {x:Type ToggleButton}}"

这给了我想要的风格,但我注意到一个烦人的副作用。我需要能够在控件被禁用时更改选定的按钮。这与普通单选按钮一样工作。但是,使用切换按钮样式的按钮,它不再显示其中一个按钮被选中。

在下面的演示中,如果您反复单击“启用切换”按钮,您可以看到重新启用时选定的按钮仍然突出显示。但是,如果您在它们被禁用然后重新启用时更改所选按钮(单击“启用切换”、“更改值”、“启用切换”),则两个按钮都不会突出显示。

我想要达到的目标:

  1. 尽可能接近 ToggleButton 的样式。
  2. 禁用选中按钮,保持蓝色背景但具有不透明度。
  3. 当按钮重新启用时,无论值是否发生变化,都具有标准的 ToggleButton 样式。

XAML:

<Window x:Class="ToggleButtonDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ToggleButtonDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="200"
        Name="demoWindow"
        DataContext="{Binding ElementName=demoWindow}">
    <StackPanel>
        <GroupBox Header="Radio" IsEnabled="{Binding Enable}">
            <StackPanel Orientation="Horizontal">
                <RadioButton Name="radio1" Content="One" GroupName="RadioGroup" IsChecked="True"/>
                <RadioButton Name="radio2" Content="Two" GroupName="RadioGroup"/>
            </StackPanel>
        </GroupBox>
        <GroupBox Header="Toggle" IsEnabled="{Binding Enable}">
            <StackPanel Orientation="Horizontal">
                <RadioButton Name="toggle1" Content="One" GroupName="ToggleGroup" Style="{StaticResource {x:Type ToggleButton}}" IsChecked="True"/>
                <RadioButton Name="toggle2" Content="Two" GroupName="ToggleGroup" Style="{StaticResource {x:Type ToggleButton}}"/>
            </StackPanel>
        </GroupBox>
        <Button Name="toggle" Content="Toggle enabled" Click="toggle_Click"/>
        <Button Name="changeValue" Content="Change value" Click="changeValue_Click"/>
    </StackPanel>
</Window>

后面的代码:

using System.ComponentModel;
using System.Windows;

namespace ToggleButtonDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {

        private bool mEnable = true;

        public bool Enable
        {
            get
            {
                return mEnable;
            }
            set
            {
                mEnable = value;
                OnPropertyChanged(nameof(Enable));
            }
        }

        public MainWindow()
        {
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void toggle_Click(object sender, RoutedEventArgs e)
        {
            Enable = !Enable;
        }

        private void changeValue_Click(object sender, RoutedEventArgs e)
        {
            if (radio1.IsChecked == true)
            {
                radio2.IsChecked = true;
            }
            else if (radio2.IsChecked == true)
            {
                radio1.IsChecked = true;
            }

            if (toggle1.IsChecked == true)
            {
                toggle2.IsChecked = true;
            }
            else if (toggle2.IsChecked == true)
            {
                toggle1.IsChecked = true;
            }
        }
    }
}

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    我从下面引用的链接中得到了这个答案...我以前从未见过这样做过,但它使用的 XAML 比从 Blend 中获取完整的样式定义要好得多(除非您需要自定义样式):

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/e0ce86f0-8fa3-4aa9-9617-4157326ee077/make-radiobutton-appear-as-togglebutton?forum=wpf

    <RadioButton Style="{StaticResource {x:Type ToggleButton}}">Yes</RadioButton>
    
    <RadioButton Style="{StaticResource {x:Type ToggleButton}}">no</RadioButton>
    

    您还可以为 RadioButton 定义 Style 并将其基于 ToggleButton,这样您就不必为每个 RadioButton 指定 Style。

    <Style BasedOn="{StaticResource {x:Type ToggleButton}}" TargetType="RadioButton"/>
    

    【讨论】:

      【解决方案2】:

      这就是禁用的ToggleButton 的样子。如果你想改变它的外观,你应该定义自定义ControlTemplate。请参考以下示例:

      <Window x:Class="ToggleButtonDemo.MainWindow"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
          xmlns:local="clr-namespace:ToggleButtonDemo"
          mc:Ignorable="d"
          Title="MainWindow" Height="200" Width="200"
          Name="demoWindow"
          DataContext="{Binding ElementName=demoWindow}">
      <Window.Resources>
          <Style x:Key="FocusVisual">
              <Setter Property="Control.Template">
                  <Setter.Value>
                      <ControlTemplate>
                          <Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                      </ControlTemplate>
                  </Setter.Value>
              </Setter>
          </Style>
          <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
          <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
          <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
          <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
          <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
          <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
          <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
          <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
          <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
          <Style TargetType="{x:Type ToggleButton}">
              <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
              <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
              <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
              <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
              <Setter Property="BorderThickness" Value="1"/>
              <Setter Property="HorizontalContentAlignment" Value="Center"/>
              <Setter Property="VerticalContentAlignment" Value="Center"/>
              <Setter Property="Padding" Value="1"/>
              <Setter Property="Template">
                  <Setter.Value>
                      <ControlTemplate TargetType="{x:Type ToggleButton}">
                          <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                              <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                          </Border>
                          <ControlTemplate.Triggers>
                              <Trigger Property="Button.IsDefaulted" Value="true">
                                  <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                              </Trigger>
                              <Trigger Property="IsMouseOver" Value="true">
                                  <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
                                  <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
                              </Trigger>
                              <Trigger Property="IsPressed" Value="true">
                                  <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
                                  <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
                              </Trigger>
                              <Trigger Property="IsChecked" Value="True">
                                  <Setter Property="Background" TargetName="border" Value="#FFBCDDEE"/>
                                  <Setter Property="BorderBrush" TargetName="border" Value="#FF245A83"/>
                              </Trigger>
                              <Trigger Property="IsEnabled" Value="false">
                                  <Setter Property="Opacity" TargetName="border" Value="0.7"/>
                              </Trigger>
                          </ControlTemplate.Triggers>
                      </ControlTemplate>
                  </Setter.Value>
              </Setter>
          </Style>
      </Window.Resources>
      <StackPanel>
          <GroupBox Header="Radio" IsEnabled="{Binding Enable}">
              <StackPanel Orientation="Horizontal">
                  <RadioButton Name="radio1" Content="One" GroupName="RadioGroup" IsChecked="True"/>
                  <RadioButton Name="radio2" Content="Two" GroupName="RadioGroup"/>
              </StackPanel>
          </GroupBox>
          <GroupBox Header="Toggle" IsEnabled="{Binding Enable}">
              <StackPanel Orientation="Horizontal">
                  <RadioButton Name="toggle1" Content="One" GroupName="ToggleGroup" Style="{StaticResource {x:Type ToggleButton}}" IsChecked="True"/>
                  <RadioButton Name="toggle2" Content="Two" GroupName="ToggleGroup" Style="{StaticResource {x:Type ToggleButton}}"/>
              </StackPanel>
          </GroupBox>
          <Button Name="toggle" Content="Toggle enabled" Click="toggle_Click"/>
          <Button Name="changeValue" Content="Change value" Click="changeValue_Click"/>
      </StackPanel>
      </Window>
      

      【讨论】:

      • 这很棒。它给了我我正在寻找的行为。是否有可能在不必重新定义这么多风格的情况下实现这一目标?我真的很想保留更多标准的 ToggleButton 样式。
      • 不行,你必须重新定义整个模板。您不能只覆盖模板的一部分。但这就是 Windows 10 上默认模板的外观,稍作修改。您可以通过在 VS 或 Blend 中的设计模式下右键单击 ToggleButton 并选择 Edit Template->Edit a Copy 将默认值复制到 XAML 标记中,然后根据您的要求对其进行修改。
      • 谢谢,这让我得到了我需要的东西。
      猜你喜欢
      • 1970-01-01
      • 2014-08-08
      • 1970-01-01
      • 1970-01-01
      • 2012-01-08
      • 2022-01-21
      • 1970-01-01
      • 1970-01-01
      • 2021-08-07
      相关资源
      最近更新 更多