我想创建一个与iPhone上的"滑动"按钮类似的按钮。我有一个动画来调整按钮的位置和宽度,但我希望这些值基于控件中使用的文本内容而变化。目前,它们是硬编码的。
以下是我目前正在使用的XAML:
<CheckBox x:Class="Smt.Controls.SlideCheckBox"
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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Smt.Controls"
xmlns:System.Windows="clr-namespace:System.Windows;assembly=PresentationCore"
Name="SliderCheckBox"
mc:Ignorable="d">
<CheckBox.Resources>
<System.Windows:Duration x:Key="AnimationTime">0:0:0.2</System.Windows:Duration>
<Storyboard x:Key="OnChecking">
<DoubleAnimation Storyboard.TargetName="CheckButton"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"
Duration="{StaticResource AnimationTime}"
To="40" />
<DoubleAnimation Storyboard.TargetName="CheckButton"
Storyboard.TargetProperty="(Button.Width)"
Duration="{StaticResource AnimationTime}"
To="41" />
</Storyboard>
<Storyboard x:Key="OnUnchecking">
<DoubleAnimation Storyboard.TargetName="CheckButton"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"
Duration="{StaticResource AnimationTime}"
To="0" />
<DoubleAnimation Storyboard.TargetName="CheckButton"
Storyboard.TargetProperty="(Button.Width)"
Duration="{StaticResource AnimationTime}"
To="40" />
</Storyboard>
<Style x:Key="SlideCheckBoxStyle"
TargetType="{x:Type local:SlideCheckBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SlideCheckBox}">
<Canvas>
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
RecognizesAccessKey="True"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<Canvas>
<!--Background-->
<Rectangle Width="{Binding ElementName=ButtonText, Path=ActualWidth}"
Height="{Binding ElementName=ButtonText, Path=ActualHeight}"
Fill="LightBlue" />
</Canvas>
<Canvas>
<!--Button-->
<Button Width="{Binding ElementName=CheckedText, Path=ActualWidth}"
Height="{Binding ElementName=ButtonText, Path=ActualHeight}"
Name="CheckButton"
Command="{x:Static local:SlideCheckBox.SlideCheckBoxClicked}">
<Button.RenderTransform>
<TransformGroup>
<TranslateTransform />
</TransformGroup>
</Button.RenderTransform>
</Button>
</Canvas>
<Canvas>
<!--Text-->
<StackPanel Name="ButtonText"
Orientation="Horizontal"
IsHitTestVisible="False">
<Grid Name="CheckedText">
<Label Margin="7 0"
Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:SlideCheckBox}}, Path=CheckedText}" />
</Grid>
<Grid Name="UncheckedText"
HorizontalAlignment="Right">
<Label Margin="7 0"
Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:SlideCheckBox}}, Path=UncheckedText}" />
</Grid>
</StackPanel>
</Canvas>
</Canvas>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource OnChecking}" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource OnUnchecking}" />
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</CheckBox.Resources>
<CheckBox.CommandBindings>
<CommandBinding Command="{x:Static local:SlideCheckBox.SlideCheckBoxClicked}"
Executed="OnSlideCheckBoxClicked" />
</CheckBox.CommandBindings>
</CheckBox>
代码背后的实现:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Smt.Controls
{
public partial class SlideCheckBox : CheckBox
{
public SlideCheckBox()
{
InitializeComponent();
Loaded += OnLoaded;
}
public static readonly DependencyProperty CheckedTextProperty = DependencyProperty.Register("CheckedText", typeof(string), typeof(SlideCheckBox), new PropertyMetadata("Checked Text"));
public string CheckedText
{
get { return (string)GetValue(CheckedTextProperty); }
set { SetValue(CheckedTextProperty, value); }
}
public static readonly DependencyProperty UncheckedTextProperty = DependencyProperty.Register("UncheckedText", typeof(string), typeof(SlideCheckBox), new PropertyMetadata("Unchecked Text"));
public string UncheckedText
{
get { return (string)GetValue(UncheckedTextProperty); }
set { SetValue(UncheckedTextProperty, value); }
}
public static readonly RoutedCommand SlideCheckBoxClicked = new RoutedCommand();
void OnLoaded(object sender, RoutedEventArgs e)
{
Style style = TryFindResource("SlideCheckBoxStyle") as Style;
if (!ReferenceEquals(style, null))
{
Style = style;
}
}
void OnSlideCheckBoxClicked(object sender, ExecutedRoutedEventArgs e)
{
IsChecked = !IsChecked;
}
}
}
当我尝试将DoubleAnimations的"To"属性绑定到文本的实际宽度时,问题就出现了,这与我在ControlTemplate中所做的相同。如果我将值绑定到ControlTemplate中元素的ActualWidth,则控件会显示为空白复选框(我的基类)。但是,在ControlTemplate自身中绑定到相同的ActualWidths却没有任何问题,似乎只是CheckBox.Resources对此有问题。
例如,以下内容将导致出现问题:
<DoubleAnimation Storyboard.TargetName="CheckButton"
Storyboard.TargetProperty="(Button.Width)"
Duration="{StaticResource AnimationTime}"
To="{Binding ElementName=CheckedText, Path=ActualWidth}" />
我不知道这是因为它试图绑定到一个值,直到渲染完成之前该值不存在,还是因为其他原因。有没有人对这种动画绑定有经验?