我正在尝试创建一个与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}" />
我不知道这是否是因为它试图绑定到一个不存在的值,直到渲染通道完成,或者它是否是其他东西。任何人都有这种动画绑定的经验吗?
答案 0 :(得分:52)
我在ControlTemplate
中遇到类似情况,我想将“To”属性绑定到一个值(而不是硬编码),最后我找到了一个解决方案< / strong>即可。
快速注意事项:如果您在网上挖掘,您会发现examples人可以使用数据绑定“From”或“To”属性。但是,在这些示例中,故事板是not in a Style or ControlTemplate。如果您的Storyboard位于Style或ControlTemplate中,则必须使用其他方法,例如此解决方案。
这个解决方案解决了freezable问题,因为它只是简单地设置了从0到1的double值。它可以巧妙地使用Tag属性和Multiply转换器。您使用多绑定绑定到所需的属性和“缩放”(标记),它们相乘。基本上这个想法是你的标签值是你的动画,它的值就像一个“比例”(从0到1),一旦你将标签设置为1,你的所需属性值就会达到“满刻度”。
您可以在行动here中看到这一点。关键在于:
<local:MultiplyConverter x:Key="multiplyConverter" />
<ControlTemplate x:Key="RevealExpanderTemp" TargetType="{x:Type Expander}">
<!-- (other stuff here...) -->
<ScrollViewer x:Name="ExpanderContentScrollView">
<!-- ** BEGIN IMPORTANT PART #1 ... -->
<ScrollViewer.Tag>
<sys:Double>0.0</sys:Double>
</ScrollViewer.Tag>
<ScrollViewer.Height>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</ScrollViewer.Height>
<!-- ...end important part #1. -->
<ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>
</ScrollViewer>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- ** BEGIN IMPORTANT PART #2 (make TargetProperty 'Tag') ... -->
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="1"
Duration="0:0:0.4"/>
<!-- ...end important part #2 -->
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
使用此值转换器:
public class MultiplyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double result = 1.0;
for (int i = 0; i < values.Length; i++)
{
if (values[i] is double)
result *= (double)values[i];
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new Exception("Not implemented");
}
}
答案 1 :(得分:8)
据我所知,你无法将动画绑定到/从中,因为动画必须是freezable。
答案 2 :(得分:1)
有关此问题的其他几种解决方法,请参阅此链接:http://blogs.microsoft.co.il/blogs/tamir/archive/2007/03/19/How-to-bind-to-Animation-To-and-FROM-properties.aspx
答案 3 :(得分:1)
我实现了这一点。
<UserControl x:Class="YOURNAMESPACE.UserControls.SliderControl"
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:YOURNAMESPACE.UserControls"
xmlns:converter="clr-namespace:YOURNAMESPACE.ValueConverters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
SizeChanged="UserControl_SizeChanged">
<UserControl.Resources>
<converter:MathConverter x:Key="mathConverter" />
<LinearGradientBrush x:Key="CheckedBlue" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#e4f5fc" Offset="0" />
<GradientStop Color="#e4f5fc" Offset="0.1" />
<GradientStop Color="#e4f5fc" Offset="0.1" />
<GradientStop Color="#9fd8ef" Offset="0.5" />
<GradientStop Color="#9fd8ef" Offset="0.5" />
<GradientStop Color="#bfe8f9" Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="CheckedOrange" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFCA6A13" Offset="0" />
<GradientStop Color="#FFF67D0C" Offset="0.1" />
<GradientStop Color="#FFFE7F0C" Offset="0.1" />
<GradientStop Color="#FFFA8E12" Offset="0.5" />
<GradientStop Color="#FFFF981D" Offset="0.5" />
<GradientStop Color="#FFFCBC5A" Offset="1" />
</LinearGradientBrush>
<SolidColorBrush x:Key="CheckedOrangeBorder" Color="#FF8E4A1B" />
<SolidColorBrush x:Key="CheckedBlueBorder" Color="#FF143874" />
<Style x:Key="CheckBoxSlider" TargetType="{x:Type CheckBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}" >
<DockPanel x:Name="dockPanel"
Width="{TemplateBinding ActualWidth}"
Height="{TemplateBinding Height}" >
<DockPanel.Resources>
<Storyboard x:Key="ShowRightStoryboard">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
<SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="ShowLeftStoryboard" >
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="slider"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"
>
<SplineDoubleKeyFrame x:Name="RightHalfKeyFrame" KeyTime="00:00:00.1000000" Value="300" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</DockPanel.Resources>
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
ContentTemplate="{TemplateBinding ContentTemplate}"
RecognizesAccessKey="True"
VerticalAlignment="Center" />
<Grid>
<Border x:Name="BackgroundBorder" BorderBrush="#FF939393" BorderThickness="1" CornerRadius="3"
Width="{TemplateBinding ActualWidth}"
Height="{TemplateBinding Height}" >
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFB5B5B5" Offset="0" />
<GradientStop Color="#FFDEDEDE" Offset="0.1" />
<GradientStop Color="#FFEEEEEE" Offset="0.5" />
<GradientStop Color="#FFFAFAFA" Offset="0.5" />
<GradientStop Color="#FFFEFEFE" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock x:Name="LeftTextBlock" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:SliderControl}}, Path=LeftText, Mode=TwoWay}" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock x:Name="RightTextBlock" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:SliderControl}}, Path=RightText, Mode=TwoWay}" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
<Border x:Name="slider"
BorderBrush="#FF939393"
HorizontalAlignment="Left"
Width="{TemplateBinding ActualWidth, Converter={StaticResource mathConverter}, ConverterParameter=/2}"
Height="{TemplateBinding Height}"
BorderThickness="1"
CornerRadius="3"
RenderTransformOrigin="0.5,0.5" Margin="0"
>
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1" />
<SkewTransform AngleX="0" AngleY="0" />
<RotateTransform Angle="0" />
<TranslateTransform X="{TemplateBinding ActualWidth, Converter={StaticResource mathConverter}, ConverterParameter=/2}" Y="0" />
</TransformGroup>
</Border.RenderTransform>
<Border.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF0F0F0" Offset="0" />
<GradientStop Color="#FFCDCDCD" Offset="0.1" />
<GradientStop Color="#FFFBFBFB" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<DockPanel Background="Transparent" LastChildFill="False">
<Viewbox x:Name="SlideRight" Stretch="Uniform" Width="28" Height="28" DockPanel.Dock="Right" Margin="0,0,50,0" >
<Path Stretch="Fill" Fill="{DynamicResource TextBrush}">
<Path.Data>
<PathGeometry Figures="m 27.773437 48.874779 -8.818359 9.902343 -4.833984 0 8.847656 -9.902343 -8.847656 -10.019532 4.833984 0 z m -11.396484 0 -8.7597655 9.902343 -4.9804687 0 9.0234372 -9.902343 -9.0234372 -10.019532 4.9804687 0 z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
<Viewbox x:Name="SlideLeft" Stretch="Uniform" Width="28" Height="28" DockPanel.Dock="Left" Margin="50,0,0,0" >
<Path Stretch="Fill" Fill="{DynamicResource TextBrush}">
<Path.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleX="-1"/>
</TransformGroup>
</Path.LayoutTransform>
<Path.Data>
<PathGeometry Figures="m 27.773437 48.874779 -8.818359 9.902343 -4.833984 0 8.847656 -9.902343 -8.847656 -10.019532 4.833984 0 z m -11.396484 0 -8.7597655 9.902343 -4.9804687 0 9.0234372 -9.902343 -9.0234372 -10.019532 4.9804687 0 z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</DockPanel>
</Border>
</Grid>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="BackgroundBorder" Property="Background" Value="{StaticResource CheckedOrange}" />
<Setter TargetName="BackgroundBorder" Property="BorderBrush" Value="{StaticResource CheckedOrangeBorder}" />
<Setter TargetName="SlideRight" Property="Visibility" Value="Collapsed" />
<Setter TargetName="SlideLeft" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="BackgroundBorder" Property="Background" Value="{StaticResource CheckedBlue}" />
<Setter TargetName="BackgroundBorder" Property="BorderBrush" Value="{StaticResource CheckedBlueBorder}" />
<Setter TargetName="SlideRight" Property="Visibility" Value="Visible" />
<Setter TargetName="SlideLeft" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox x:Name="checkBox"
Style="{StaticResource CheckBoxSlider}"
HorizontalAlignment="Stretch"
DockPanel.Dock="Top"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:SliderControl}}, Path=Height, Mode=TwoWay}"
IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:SliderControl}}, Path=IsLeftVisible, Mode=TwoWay}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked"
/>
</Grid>
</UserControl>
代码背后的代码。
namespace YOURNAMESPACE.UserControls
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
/// <summary>
/// Interaction logic for SliderControl.xaml
/// </summary>
public partial class SliderControl : UserControl
{
public static readonly DependencyProperty IsLeftVisibleProperty =
DependencyProperty.RegisterAttached(
"IsLeftVisible",
typeof(bool),
typeof(SliderControl),
new UIPropertyMetadata(true, IsLeftVisibleChanged));
public static readonly DependencyProperty LeftTextProperty =
DependencyProperty.RegisterAttached(
"LeftText",
typeof(string),
typeof(SliderControl),
new UIPropertyMetadata(null, LeftTextChanged));
public static readonly DependencyProperty RightTextProperty =
DependencyProperty.RegisterAttached(
"RightText",
typeof(string),
typeof(SliderControl),
new UIPropertyMetadata(null, RightTextChanged));
/// <summary>
/// Initializes a new instance of the <see cref="SliderControl"/> class.
/// </summary>
public SliderControl()
{
this.InitializeComponent();
}
public string LeftText { get; set; }
public string RightText { get; set; }
[AttachedPropertyBrowsableForType(typeof(SliderControl))]
public static bool GetIsLeftVisible(SliderControl sliderControl)
{
return (bool)sliderControl.GetValue(IsLeftVisibleProperty);
}
[AttachedPropertyBrowsableForType(typeof(SliderControl))]
public static string GetLeftText(SliderControl sliderControl)
{
return (string)sliderControl.GetValue(LeftTextProperty);
}
public static void SetIsLeftVisible(SliderControl sliderControl, bool value)
{
sliderControl.SetValue(IsLeftVisibleProperty, value);
}
public static void IsLeftVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SliderControl slider = d as SliderControl;
if ((bool)e.NewValue == true)
{
slider.RunAnimation("ShowLeftStoryboard");
}
else
{
slider.RunAnimation("ShowRightStoryboard");
}
}
[AttachedPropertyBrowsableForType(typeof(SliderControl))]
public static string GetRightText(SliderControl sliderControl)
{
return (string)sliderControl.GetValue(RightTextProperty);
}
public static void SetLeftText(SliderControl sliderControl, string value)
{
sliderControl.SetValue(LeftTextProperty, value);
}
public static void SetRightText(SliderControl sliderControl, string value)
{
sliderControl.SetValue(RightTextProperty, value);
}
private static void LeftTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SliderControl slider = d as SliderControl;
slider.LeftText = e.NewValue as string;
}
private static void RightTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SliderControl slider = d as SliderControl;
slider.RightText = e.NewValue as string;
}
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
this.checkBox.Width = e.NewSize.Width;
CheckBox cb = this.checkBox;
var controlTemplate = cb.Template;
DockPanel dockPanel = controlTemplate.FindName("dockPanel", cb) as DockPanel;
Storyboard story = dockPanel.Resources["ShowLeftStoryboard"] as Storyboard;
DoubleAnimationUsingKeyFrames dk = story.Children[0] as DoubleAnimationUsingKeyFrames;
SplineDoubleKeyFrame sk = dk.KeyFrames[0] as SplineDoubleKeyFrame;
// must manipulate this in code behind, binding does not work,
// also it cannot be inside of a control template
// because storyboards in control templates become frozen and cannot be modified
sk.Value = cb.Width / 2;
if (cb.IsChecked == true)
{
story.Begin(cb, cb.Template);
}
}
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
this.RunAnimation("ShowLeftStoryboard");
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
{
this.RunAnimation("ShowRightStoryboard");
}
private void RunAnimation(string storyboard)
{
CheckBox cb = this.checkBox;
var controlTemplate = cb.Template;
DockPanel dockPanel = controlTemplate.FindName("dockPanel", cb) as DockPanel;
if (dockPanel != null)
{
Storyboard story = dockPanel.Resources[storyboard] as Storyboard;
DoubleAnimationUsingKeyFrames dk = story.Children[0] as DoubleAnimationUsingKeyFrames;
SplineDoubleKeyFrame sk = dk.KeyFrames[0] as SplineDoubleKeyFrame;
story.Begin(cb, cb.Template);
}
}
}
}
的IValueConverter
namespace YOURNAMESPACE.ValueConverters
{
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
/// <summary>
/// Does basic math operations, eg. value = 60 parameter = "*2 + 1", result = 121
/// </summary>
/// <seealso cref="System.Windows.Data.IValueConverter" />
[ValueConversion(typeof(double), typeof(double))]
public class MathConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double d = (double)value;
string mathExpression = d.ToString() + parameter;
if (mathExpression.Contains("^"))
{
throw new Exception("Doesn't handle powers or square roots");
}
DataTable table = new DataTable();
table.Columns.Add("expression", typeof(string), mathExpression);
DataRow row = table.NewRow();
table.Rows.Add(row);
double ret = double.Parse((string)row["expression"]);
if (ret <= 0)
{
return 1d;
}
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// not implemented
return null;
}
}
}
示例用法:
<chart:SliderControl x:Name="sliderControl"
LeftText="{Binding Path=LeftTextProperty}"
RightText="{Binding Path=RightTextProperty}"
Grid.Row="0"
IsLeftVisible="{Binding Path=IsLeftVisible, Mode=TwoWay}"
Height="35" />
答案 4 :(得分:1)
我喜欢@Jason Frank的解决方案。但是,如果您不使用标签,它会更容易且不易出错,而是例如空虚拟边框元素的宽度属性。它是一个原生的双重属性,所以不需要<sys:Double>
语法,你可以像使用这样的变量一样命名边框:
<!-- THIS IS JUST THE SLIDING AMIMATION MATH -->
<!-- animated Border.Width From 0 to 1 -->
<Border x:Name="Var_Animation_0to1" Width="0"/>
<!-- animated Border.Width From 0 to (TotalWidth-SliderWidth) -->
<Border x:Name="Var_Slide_Length">
<Border.Width>
<MultiBinding Converter="{mvvm:MathConverter}" ConverterParameter="a * (b-c)">
<Binding ElementName="Var_Animation_0to1" Path="Width"/>
<Binding ElementName="BackBorder" Path="ActualWidth"/>
<Binding ElementName="Slider" Path="ActualWidth"/>
</MultiBinding>
</Border.Width>
</Border>
这使得绑定更具可读性。
动画总是0..1,正如杰森指出的那样:
<BeginStoryboard Name="checkedSB">
<Storyboard Storyboard.TargetProperty="Width" Storyboard.TargetName="Var_Animation_0to1">
<DoubleAnimation To="1" Duration="00:00:00.2"/>
</Storyboard>
</BeginStoryboard>
然后将您想要动画的任何内容绑定到虚拟边框的宽度。这样你甚至可以将转换器链接到彼此:
<Border x:Name="Slider" HorizontalAlignment="Left"
Margin="{Binding ElementName=Var_Slide_Length, Path=Width, Converter={StaticResource DoubleToThickness}, ConverterParameter=x 0 0 0}"/>
结合MathConverter,你几乎可以做任何风格的事情: https://www.codeproject.com/Articles/239251/MathConverter-How-to-Do-Math-in-XAML