Анимация WPF: привязка к атрибуту «To» анимации раскадровки

Я пытаюсь создать кнопку, которая ведет себя аналогично кнопке «слайд» на iPhone. У меня есть анимация, которая регулирует положение и ширину кнопки, но я хочу, чтобы эти значения были основаны на тексте, используемом в элементе управления. В настоящее время они жестко закодированы.

Вот мой рабочий XAML, пока:

  0:0:0.2                             

И код позади:

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

Проблема возникает, когда я пытаюсь привязать атрибут «To» в DoubleAnimations к фактической ширине текста, как и в ControlTemplate. Если я привяжу значения к ActualWidth элемента в ControlTemplate, элемент управления появится как пустой флажок (мой базовый class). Тем не менее, я без каких-либо проблем привязываюсь к тем же ActualWidths в ControlTemplate. Похоже, что это CheckBox.Resources, у которого есть проблема с ним.

Например, следующее:

   

Я не знаю, связано ли это с тем, что он пытается привязать к значению, которое не существует до тех пор, пока не будет сделан проход визуализации, или если это что-то еще. У кого-нибудь есть опыт в подобной привязке анимации?

У меня были аналогичные ситуации в ControlTemplate где я хотел привязать атрибут «To» к значению (а не жестко-кодировать его), и наконец нашел решение .

Краткое примечание: если вы копаетесь в Интернете, вы найдете примеры того, как люди могут использовать привязку данных для свойств «От» или «Кому». Однако в этих примерах раскадровки не находятся в стиле или контроле . Если ваш раскадровка находится в стиле или контрольной таблице, вам придется использовать другой подход, например, это решение.

Это решение распространяется на зависающую проблему, поскольку оно просто оживляет двойное значение от 0 до 1. Оно работает с умным использованием свойства Tag и конвертера Multiply. Вы используете многосвязывание для привязки как к желаемому свойству, так и к вашему «масштабу» (тегу), которые умножаются вместе. По сути, идея заключается в том, что значение вашего тега – это то, что вы оживляете, и его значение действует как «масштаб» (от 0 до 1), приведя к желаемому значению атрибута «полный масштаб» после того, как вы активировали тег до 1.

Вы можете увидеть это в действии здесь . Суть его в том, что:

       0.0                         

С помощью этого преобразователя значений:

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

Насколько я знаю, вы не можете привязать анимацию к / из, потому что анимация должна быть заморожена.

См. Эту ссылку для нескольких других способов решения этой проблемы: http://blogs.microsoft.co.il/blogs/tamir/archive/2007/03/19/How-to-bind-to-Animation-To-and- ОТ-properties.aspx

Я реализовал эту точную вещь.

                               

код позади.

 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; ///  /// Interaction logic for SliderControl.xaml ///  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)); ///  /// Initializes a new instance of the  class. ///  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; ///  /// Does basic math operations, eg. value = 60 parameter = "*2 + 1", result = 121 ///  ///  [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; } } } 

пример использования:

  

Мне нравится решение Джейсона Фрэнка. Однако это еще проще и менее подвержено ошибкам, если вы не используете тег, но вместо этого, например, свойство Width для пустого фиктивного элемента Border. Это родное двойное свойство, поэтому нет необходимости в синтаксисе и вы можете назвать Border так же, как и с переменной:

              

Это делает привязки более читабельными.

Анимация всегда 0..1, как указал Джейсон:

      

Затем привяжите все, что хотите, для анимации к ширине фиктивной границы. Таким образом, вы можете даже конвертировать конвертеры друг в друга так:

  

В сочетании с MathConverter вы можете делать практически все в стилях: https://www.codeproject.com/Articles/239251/MathConverter-How-to-Do-Math-in-XAML

Давайте будем гением компьютера.