I'm trying to create a customized version of what is essentially a Checkbox control, and am having trouble getting it to work how I'd like it to. The idea (which I'm calling a ToggleIcon) is essentially an icon that changes in some way when it's clicked to signify that it's been selected. For example, the base idea is to display a greyed-out icon when the control is in the Unchecked state, and when a user clicks on the icon it would then switch to a Checked state, shown by the same icon but colored green.
I followed this MSDN guide, which also deals with customizing a Checkbox, but the differences are causing it not to work. I've added two DependencyProperties to the control, named CheckedImage
and UncheckedImage
, both of type ImageSource
. The idea is for the user to add the controls in XAML, and provide a path for each of these to select the desired icons. I also have default values for these consisting of a gray and a green checkmark icon. When I test this though, I keep getting a UriFormatException saying the format could not be determined.
Can someone please look over this code and see if you can find the issue? Alternatively, links to any guides on this type of task would be appreciated too, I can't even figure out what to google to find helpful results.
ToggleIcon Code:
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>
As for the MainPage.xaml, the only code I added was inserting the ToggleIcon control i.e.-<local:ToggleIcon />
. MainPage.xaml.cs has no changes to the VisualStudio initial template.
The problem looks you do animation for wrong property. the TargetProperty
should be BitmapIcon's Opacity, and for displaying the states in right order, we need set the default Opacity of bitmapicon as 0, and switch to different state with VisualState
.
And the default UncheckedImage and CheckedImage value should contains ms-appx uri shcheme. Please refer the following complete code.
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 Code
<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>