WPF 让一组 Button 实现 RadioButton 的当前样式效果

WPF 让一组 Button 实现 RadioButton 的当前样式效果

WPF 让一组 Button 实现 RadioButton 的当前样式效果

—— 魏刘宏 2020年6月19日

概述:本文通过 WPF 的数据触发器(DataTrigger)和多重绑定(MultiBinding),在一组普通按钮(Button)上实现了像单选按钮(RadioButton)那样的,同一时间只有一个按钮具有当前样式(本文演示的是背景颜色)的效果。

需求起因:公司项目中有个 WPF 项目,有个界面下方有一块显示当前信息的区域,并且有几个按钮,意图是点击之后可以切换为另一区域的信息。由于到目前为止,业务中都还只有一个区域,所以之前同事就没写这切换的逻辑。经过本人的不懈努力,切换逻辑写好了,就差切换后按钮样式的转移了,如下图。

%title插图%num

 

这种需求本来最好是直接使用 RadioButton,然后更改显示模板的。由于内部还有其它逻辑,改起来也没有那么轻松,加上之前触发器用得比较少,多重绑定也没怎么用过,正好可以借此机会练练手,所以就还是基于普通按钮来实现这个需求。

 

首先给出几个按钮的代码:

<Button Command="{Binding CommandEntranceSwitch}" CommandParameter="1" Tag="{Binding LeftEntranceSelected.Tag}" Style="{DynamicResource RadioStyleButton}"/>
<Button Command="{Binding CommandEntranceSwitch}" CommandParameter="2" Tag="{Binding LeftEntranceSelected.Tag}" Style="{DynamicResource RadioStyleButton}"/>
<Button Command="{Binding CommandEntranceSwitch}" CommandParameter="3" Tag="{Binding LeftEntranceSelected.Tag}" Style="{StaticResource BorderStyleButton}"/>

 

Command 绑定了按钮的命令,用于切换当前需要显示的数据(即后面的 LeftEntranceSelected ),命令的代码与本文关系不大,就不展示了;

CommandParameter 是命令的参数,也就是作为区分按钮的编号;

Tag 绑定了当前选中信息的 Tag 属性,用于与按钮编号进行比较,三个按钮的 Tag 值是一样的;

Style 就是本次添加了触发器的样式,可以看到第三个按钮还是原来的样式,这是我故意给出的错误示范,因为之前忘了元素上的样式优先级更高,以为外面的 Style 指定了 TargetType 就行,这里也给大家提个醒。还有一个坑就是,要使用 DynamicResource,不能使用 StaticResource,不然没有反应。

 

然后就是本次新增的样式(样式里有数据触发器,触发器里有多重绑定):

<Grid.Resources>
    <Style TargetType="Button" BasedOn="{StaticResource BorderStyleButton}" x:Key="RadioStyleButton">
        <Style.Triggers>
            <DataTrigger Value="true">
                <DataTrigger.Binding>
                    <MultiBinding Converter="{StaticResource EqualConverter}">
                        <Binding RelativeSource="{RelativeSource Self}" Path="Tag"></Binding>
                        <Binding RelativeSource="{RelativeSource Self}" Path="CommandParameter"></Binding>
                    </MultiBinding>
                </DataTrigger.Binding>
                <Setter Property="Background" Value="#4BEF9E"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Grid.Resources>

 

可以看到,此样式目标为 Button,基于原样式 BorderStyleButton,取名为 RadioStyleButton。DataTriggers 的 Value 设为 true,表明 Binding 中计算的值为 true 时,设置 Setter 中指定的样式(此处为背景色)。那么关键就是 Binding 中如何计算了。

此处使用了多重绑定(MultiBinding),绑定了每个应用了该样式的 Button 元素上的 Tag 和 CommandParameter,因为每个 Button 自己的 CommandParameter 是确定的,而 Tag 随着选中项变化,当两者相等,说明自己被选中了,随即被设置上当前样式。

那么怎么判断两者是否相等呢?答案就是使用 MultiBinding 的转换器(Converter)EqualConverter:

<converters:EqualConverter x:Key="EqualConverter"></converters:EqualConverter>

 

转换器代码为:

/// <summary>
/// [dlgcy]相等比较器(判断给定的数据的字符串形式是否都相等)
/// </summary>
public class EqualConverter:IMultiValueConverter
{

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values == null || values.Length == 0)
            throw new ArgumentNullException(nameof(values), "values can not be null or empty");

        return values.All(x => x+"" == values[0]+"");
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return null;
    }
}

 

最终效果为:

%title插图%num

 

%title插图%num

 

希望大家多多讨论,不吝赐教。

 

2024年3月12日 更新:

实现了一个图片按钮样式(上图下字),并结合《使用通用附加属性来减少 WPF 元素自定义样式的多余代码》及本文,给它添加了这个突出显示当前项的功能:

%title插图%num

 

使用方法(使用 WpfXamlPropProxy.Id 和 WpfXamlPropProxy.CurrentSelectedId 来代替 Tag 和 CommandParameter,这样意义更明确):

%title插图%num

 

源码地址:https://gitee.com/dlgcy/DLGCY_WPFPractice/tree/Blog20240312

 

 

原创文章,转载请注明: 转载自 独立观察员(dlgcy.com)

本文链接地址: [WPF 让一组 Button 实现 RadioButton 的当前样式效果](https://dlgcy.com/wpf-button-as-radiobutton/)

关注微信公众号 独立观察员博客(DLGCY_BLOG) 第一时间获取最新文章

%title插图%num

发表评论