2010-02-02 12 views
24

Sto provando a creare un pulsante che si comporta in modo simile al pulsante "diapositiva" sull'iPhone. Ho un'animazione che regola la posizione e la larghezza del pulsante, ma voglio che questi valori siano basati sul testo utilizzato nel controllo. Attualmente sono hardcoded.Animazione WPF: associazione all'attributo "A" dell'animazione storyboard

Ecco la mia XAML di lavoro, finora:

<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> 

E il codice dietro:

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; 
     } 
    } 
} 

Il problema viene quando provo a legare la "A" attributo nelle DoubleAnimations al reale larghezza del testo, lo stesso che sto facendo in ControlTemplate. Se lego i valori a una larghezza effettiva di un elemento nel ControlTemplate, il controllo viene visualizzato come casella di controllo vuota (la mia classe base). Tuttavia, sono vincolante per gli stessi ActualWidths nello stesso ControlTemplate senza problemi. Sembra essere il CheckBox.Resources che ha un problema con esso.

Per esempio, il seguente sarà romperlo:

 <DoubleAnimation Storyboard.TargetName="CheckButton" 
         Storyboard.TargetProperty="(Button.Width)" 
         Duration="{StaticResource AnimationTime}" 
         To="{Binding ElementName=CheckedText, Path=ActualWidth}" /> 

Io non so se questo è perché si sta cercando di legarsi a un valore che non esiste fino a quando un passaggio di rendering è fatto, o se è qualcos'altro Qualcuno ha qualche esperienza con questo tipo di associazione di animazione?

risposta

8

Per quanto ne so, non è possibile associare l'animazione a/da perché l'animazione deve essere libera.

43

ho avuto situazioni simili in ControlTemplate s dove ho voluto di impegnare la "A" attribuire ad un valore (piuttosto che hard-codifica), e alla fine ho trovato una soluzione .

Nota rapida: se si scava sul Web, è possibile trovare examples di persone che possono utilizzare l'associazione dati per le proprietà "Da" o "A". Tuttavia, in quegli esempi gli storyboard sono not in a Style or ControlTemplate. Se lo Storyboard si trova in uno Style o in un ControlTemplate, dovrai utilizzare un approccio diverso, come questa soluzione.

Questa soluzione risolve il problema del congelamento perché anima semplicemente un doppio valore da 0 a 1. Funziona con un uso intelligente della proprietà Tag e di un convertitore di moltiplicazione. Si utilizza una multibinding per associare sia una proprietà desiderata che la "scala" (il Tag), che vengono moltiplicati insieme. Fondamentalmente l'idea è che il valore del Tag sia ciò che si anima e il suo valore agisce come una "scala" (da 0 a 1) portando il valore dell'attributo desiderato a "fondo scala" dopo aver animato il Tag su 1.

È possibile vedere questo in azione here. Il punto cruciale di esso è questo:

<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> 

Con questo convertitore valore:

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"); 
    } 
} 
+0

come faccio a fare questo, ma si legano ad un'altezza a mio avviso modello? – claudekennilol

+2

Puoi spiegarmi perché questo è diverso dal semplice legame? –

+0

@Jason Frank Ho trovato un buon modo per sconfiggere il problema del congelamento e dare un'occhiata alla mia soluzione – 0x4f3759df

1

ho implementato questa cosa esatta.

<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> 
codice

codice.

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; 
      } 
     } 
} 

esempio di utilizzo:

<chart:SliderControl x:Name="sliderControl" 
                 LeftText="{Binding Path=LeftTextProperty}" 
                 RightText="{Binding Path=RightTextProperty}" 
                 Grid.Row="0" 
                 IsLeftVisible="{Binding Path=IsLeftVisible, Mode=TwoWay}" 
                 Height="35" /> 
0

mi piace la soluzione di @ Jason Frank. Tuttavia, è ancora più semplice e meno soggetto a errori se non si utilizza il tag, ma invece ad es. la proprietà Width di un elemento Border dummy vuoto. Si tratta di una doppia proprietà nativo, senza necessità di ed è possibile assegnare un nome alla frontiera, proprio come si farebbe con una variabile in questo modo:

<!-- 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> 

che rende gli attacchi molto più leggibile.

L'animazione è sempre 0..1, come Jason ha sottolineato:

<BeginStoryboard Name="checkedSB"> 
    <Storyboard Storyboard.TargetProperty="Width" Storyboard.TargetName="Var_Animation_0to1"> 
     <DoubleAnimation To="1" Duration="00:00:00.2"/> 
    </Storyboard> 
</BeginStoryboard> 

quindi associare quello che vuoi animare la larghezza del bordo manichino. Questo modo è possibile anche convertitori catena tra loro in questo modo:

<Border x:Name="Slider" HorizontalAlignment="Left" 
     Margin="{Binding ElementName=Var_Slide_Length, Path=Width, Converter={StaticResource DoubleToThickness}, ConverterParameter=x 0 0 0}"/> 

In combinazione con la MathConverter si può fare quasi tutto in stili: https://www.codeproject.com/Articles/239251/MathConverter-How-to-Do-Math-in-XAML