UWP 自定义复选框 - ToggleIcon

UWP Customized Checkbox - ToggleIcon

我正在尝试创建本质上是 Checkbox 控件的自定义版本,但无法使其按我希望的方式工作。这个想法(我称之为 ToggleIcon)本质上是一个图标,当它被点击时会以某种方式发生变化以表示它已被 selected。例如,基本想法是当控件处于未选中状态时显示一个灰色图标,当用户单击该图标时,它将切换到选中状态,由相同的图标显示,但颜色为绿色。

我遵循了 this MSDN 指南,该指南也涉及自定义复选框,但差异导致它无法正常工作。我在控件中添加了两个 DependencyProperties,名为 CheckedImageUncheckedImage,类型均为 ImageSource。这个想法是让用户在 XAML 中添加控件,并为每个控件提供指向 select 所需图标的路径。我也有这些由灰色和绿色复选标记图标组成的默认值。但是,当我对此进行测试时,我不断收到 UriFormatException 提示无法确定格式。

有人可以查看这段代码,看看是否能找到问题所在吗?或者,也将不胜感激关于此类任务的任何指南的链接,我什至不知道如何 google 找到有用的结果。

切换图标代码:

    public sealed partial class ToggleIcon : UserControl
    {
        public ImageSource UncheckedImage
        {
            get { return (ImageSource)GetValue(UncheckedImageProperty); }
            set { SetValue(UncheckedImageProperty, value); }
        }

        public static readonly DependencyProperty UncheckedImageProperty =
            DependencyProperty.Register("UncheckedImage", typeof(ImageSource), typeof(ToggleIcon), new PropertyMetadata(new System.Uri("Assets/default-unchecked.png")));

        public ImageSource CheckedImage
        {
            get { return (ImageSource)GetValue(CheckedImageProperty); }
            set { SetValue(CheckedImageProperty, value); }
        }

        public static readonly DependencyProperty CheckedImageProperty =
            DependencyProperty.Register("CheckedImage", typeof(ImageSource), typeof(ToggleIcon), new PropertyMetadata(new System.Uri("Assets/default-checked.png")));

        public ToggleIcon()
        {
            this.InitializeComponent();
            DataContext = this;
        }
    }

ToggleIcon XAML:

<UserControl
    x:Class="CustomControlScratchpad.ToggleIcon"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CustomControlScratchpad"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="128"
    d:DesignWidth="128">
    <UserControl.Resources>
        <ControlTemplate x:Key="ToggleIconTemplate" TargetType="CheckBox">
            <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="CheckStates">
                        <VisualState x:Name="Checked">
                            <Storyboard>
                                <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="UriSource" Storyboard.TargetName="CheckedState" />
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Unchecked">
                            <Storyboard>
                                <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="UriSource" Storyboard.TargetName="UncheckedState" />
                            </Storyboard>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
                <Grid>
                    <BitmapIcon x:Name="UncheckedState" UriSource="{Binding UncheckedImage }"  />
                    <BitmapIcon x:Name="CheckedState" UriSource="{Binding CheckedImage}" />
                </Grid>
            </Border>
        </ControlTemplate>
    </UserControl.Resources>
    <Grid>
        <CheckBox Template="{StaticResource ToggleIconTemplate}" />
    </Grid>
</UserControl>

至于 MainPage.xaml,我添加的唯一代码是插入 ToggleIcon 控件,即-<local:ToggleIcon />。 MainPage.xaml.cs 没有对 VisualStudio 初始模板进行任何更改。

问题是你做的动画做错了属性。 TargetProperty 应该是 BitmapIcon 的 Opacity,为了正确显示状态,我们需要将 bitmapicon 的默认 Opacity 设置为 0,并通过 VisualState.

切换到不同的状态

并且默认的 UncheckedImage 和 CheckedImage 值应该包含 ms-appx uri shcheme。请参考以下完整代码。

public sealed partial class ToggleIcon : UserControl
{
    public ImageSource UncheckedImage
    {
        get { return (ImageSource)GetValue(UncheckedImageProperty); }
        set { SetValue(UncheckedImageProperty, value); }
    }

    public static readonly DependencyProperty UncheckedImageProperty =
        DependencyProperty.Register("UncheckedImage", typeof(ImageSource), typeof(ToggleIcon), new PropertyMetadata(new System.Uri("ms-appx:///Assets/default-unchecked.png")));

    public ImageSource CheckedImage
    {
        get { return (ImageSource)GetValue(CheckedImageProperty); }
        set { SetValue(CheckedImageProperty, value); }
    }

    public static readonly DependencyProperty CheckedImageProperty =
        DependencyProperty.Register("CheckedImage", typeof(ImageSource), typeof(ToggleIcon), new PropertyMetadata(new System.Uri("ms-appx:///Assets/default-checked.png")));

    public ToggleIcon()
    {
        this.InitializeComponent();
        DataContext = this;
    }
}

Xaml代码

<UserControl.Resources>
    <ControlTemplate x:Key="ToggleIconTemplate" TargetType="CheckBox">
        <Border
            Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
            <Grid>
                <BitmapIcon
                    x:Name="UncheckedState"
                    Opacity="0"
                    UriSource="{Binding UncheckedImage}" />
                <BitmapIcon
                    x:Name="CheckedState"
                    Opacity="0"
                    UriSource="{Binding CheckedImage}" />
            </Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CheckStates">
                    <VisualState x:Name="Checked">
                        <Storyboard>
                            <DoubleAnimation
                                Storyboard.TargetName="CheckedState"
                                Storyboard.TargetProperty="Opacity"
                                To="1"
                                Duration="0" />
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="Unchecked">
                        <Storyboard>
                            <DoubleAnimation
                                Storyboard.TargetName="UncheckedState"
                                Storyboard.TargetProperty="Opacity"
                                To="1"
                                Duration="0" />
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </Border>
    </ControlTemplate>
</UserControl.Resources>
<Grid>
    <CheckBox
        Width="32"
        Height="32"
        Template="{StaticResource ToggleIconTemplate}" />
</Grid>