如何从XAML访问元素资源中的故事板?

考虑以下代码:

           ...            

上面的代码没有问题。 现在,我想将MyStory关键帧值绑定到此用户控件的DP(名为SpecialColor ),如下所示:

      

这会出错:

无法冻结此Storyboard时间轴树以跨线程使用。

使用后面的代码可以做到这一点。 但是我怎么能只在XAML中做呢?


代码隐藏辅助解决方案:

步骤1:MyStory故事板放入brdBase资源中。

           ...           

错误: 找不到名为“MyStory”的资源。 资源名称区分大小写。

步骤2:消除IsMouseOver属性上的IsMouseOver并从后面的代码开始MyStory

              

C#Code-Behind:

 private void brdBase_MouseEnter(object sender, MouseEventArgs e) { Border grdRoot = (Border)this.Template.FindName("brdBase", this); Storyboard story = grdRoot.Resources["MyStory"] as Storyboard; story.Begin(this, this.Template); } 

步骤3:解决方案已经完成,但第一次不起作用。 幸运的是,这个问题有一个解决方法。 将ControlTemplate放入Style足够了。

(我需要其他Trigger类型而不是EventTrigger并且必须使用ControlTemplate包装UserControl元素。)


更新:

关于使用ObjectDataProvider的想法失败了。

  1. ObjectDataProvider资源不能用于提供故事板! 错误报告是:
    • XamlParseException:设置属性’System.Windows.Media.Animation.BeginStoryboard.Storyboard’抛出exception。
    • InnerException: ‘System.Windows.Data.ObjectDataProvider’不是属性’Storyboard’的有效值。
  2. AssociatedControl DP始终为空。

这是代码:

       MyStory            ...           

StoryboardFinder类:

 public class StoryboardFinder : DependencyObject { #region ________________________________________ AssociatedControl public Control AssociatedControl { get { return (Control)GetValue(AssociatedControlProperty); } set { SetValue(AssociatedControlProperty, value); } } public static readonly DependencyProperty AssociatedControlProperty = DependencyProperty.Register("AssociatedControl", typeof(Control), typeof(StoryboardFinder), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None)); #endregion public Storyboard Finder(string resourceName) { // // Associated control is always null :( // return new Storyboard(); } } 

好吧,你不能真正绑定到“To”和From,因为故事板必须被冻结,以便有效地使用跨线程。

解决方案1)最简单的解决方案,无需黑客攻击(涉及代码隐藏):在事件处理程序中添加MouseOver事件处理程序,找到必要的动画,直接设置“To”属性,这样就不会使用绑定,可以完成“冻结” 。 这样你就不会硬编码:)。

解决方案2)有一个很酷的黑客只支持XAML(一点转换魔术),但我不建议。 尽管如此,它很酷:) WPF动画:绑定到故事板动画的“To”属性请参阅Jason的回答。

你可以尝试更多的东西:

解决方案3)不要使用依赖属性,而是实现INotifyProperthChanged。 这样你仍然可以BIND“To”。 请注意,我认为这应该在理论上有效,但我没有尝试过。

解决方案4)将Mode = OneTime应用于绑定。 也许它有效?

解决方案5)编写您自己的附加行为,该行为将评估正确线程上的依赖项属性并设置“To”属性。 我认为这将是一个很好的解决方案。

这里也是很好的重复: WPF动画“无法冻结此Storyboard时间轴树以跨线程使用”

如果这段代码是真的怎么办?

      

如果是这样,我可以在IsMouseOver属性上有一个Trigger …

我很高兴地说这是一个有效的代码:)我只能在标签中使用EventTrigger 。 这是限制。 所以我开始考虑这个想法:如果我有一个可以在FrameworkElement.Triggers范围内工作的自定义触发器怎么办? 这是代码:

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows; using System.Windows.Interactivity; using System.Windows.Media.Animation; namespace TriggerTest { ///  /// InteractiveTrigger is a trigger that can be used as the System.Windows.Trigger but in the System.Windows.Interactivity. ///  /// Note: There is neither `EnterActions` nor `ExitActions` in this class. The `CommonActions` can be used instead of `EnterActions`. /// Also, the `Actions` property which is of type System.Windows.Interactivity.TriggerAction can be used. ///  ///   ///  /// There is only one kind of triggers (ie EventTrigger) in the System.Windows.Interactivity. So you can use the following triggers in this namespace: /// 1- InteractiveTrigger : Trigger /// 2- InteractiveMultiTrigger : MultiTrigger /// 3- InteractiveDataTrigger : DataTrigger /// 4- InteractiveMultiDataTrigger : MultiDataTrigger ///  ///  public class InteractiveTrigger : TriggerBase { #region ___________________________________________________________________________________ Properties #region ________________________________________ Value ///  /// [Wrapper property for ValueProperty] ///  /// Gets or sets the value to be compared with the property value of the element. The comparison is a reference equality check. ///  ///  public object Value { get { return (object)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(InteractiveTrigger), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, OnValuePropertyChanged)); private static void OnValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { InteractiveTrigger instance = sender as InteractiveTrigger; if (instance != null) { if (instance.CanFire) instance.Fire(); } } #endregion ///  /// Gets or sets the name of the object with the property that causes the associated setters to be applied. ///  public string SourceName { get; set; } ///  /// Gets or sets the property that returns the value that is compared with this trigger.Value property. The comparison is a reference equality check. ///  public DependencyProperty Property { get; set; } ///  /// Gets or sets a collection of System.Windows.Setter objects, which describe the property values to apply when the trigger object becomes active. ///  public List Setters { get; set; } ///  /// Gets or sets the collection of System.Windows.TriggerAction objects to apply when this trigger object becomes active. ///  public List CommonActions { get; set; } ///  /// Gets a value indicating whether this trigger can be active to apply setters and actions. ///  private bool CanFire { get { if (this.AssociatedObject == null) { return false; } else { object associatedValue; if (string.IsNullOrEmpty(SourceName)) associatedValue = this.AssociatedObject.GetValue(Property); else associatedValue = (this.AssociatedObject.FindName(SourceName) as DependencyObject).GetValue(Property); TypeConverter typeConverter = TypeDescriptor.GetConverter(Property.PropertyType); object realValue = typeConverter.ConvertFromString(Value.ToString()); return associatedValue.Equals(realValue); } } } #endregion #region ___________________________________________________________________________________ Methods ///  /// Fires (activates) current trigger by setting setter values and invoking all actions. ///  private void Fire() { // // Setting setters values to their associated properties.. // foreach (Setter setter in Setters) { if (string.IsNullOrEmpty(setter.TargetName)) this.AssociatedObject.SetValue(setter.Property, setter.Value); else (this.AssociatedObject.FindName(setter.TargetName) as DependencyObject).SetValue(setter.Property, setter.Value); } // // Firing actions.. // foreach (System.Windows.TriggerAction action in CommonActions) { Type actionType = action.GetType(); if (actionType == typeof(BeginStoryboard)) { (action as BeginStoryboard).Storyboard.Begin(); } else throw new NotImplementedException(); } this.InvokeActions(null); } #endregion #region ___________________________________________________________________________________ Events public InteractiveTrigger() { Setters = new List(); CommonActions = new List(); } protected override void OnAttached() { base.OnAttached(); if (Property != null) { object propertyAssociatedObject; if (string.IsNullOrEmpty(SourceName)) propertyAssociatedObject = this.AssociatedObject; else propertyAssociatedObject = this.AssociatedObject.FindName(SourceName); // // Adding a property changed listener to the property associated-object.. // DependencyPropertyDescriptor dpDescriptor = DependencyPropertyDescriptor.FromProperty(Property, propertyAssociatedObject.GetType()); dpDescriptor.AddValueChanged(propertyAssociatedObject, PropertyListener_ValueChanged); } } protected override void OnDetaching() { base.OnDetaching(); if (Property != null) { object propertyAssociatedObject; if (string.IsNullOrEmpty(SourceName)) propertyAssociatedObject = this.AssociatedObject; else propertyAssociatedObject = this.AssociatedObject.FindName(SourceName); // // Removing previously added property changed listener from the associated-object.. // DependencyPropertyDescriptor dpDescriptor = DependencyPropertyDescriptor.FromProperty(Property, propertyAssociatedObject.GetType()); dpDescriptor.RemoveValueChanged(propertyAssociatedObject, PropertyListener_ValueChanged); } } private void PropertyListener_ValueChanged(object sender, EventArgs e) { if (CanFire) Fire(); } #endregion } } 

我还创建了其他触发器类型(即InteractiveMultiTriggerInteractiveDataTriggerInteractiveMultiDataTrigger )以及一些可以使条件和多条件EventTriggers成为可能的动作。 如果专业人士确认这个解决方案,我会发布所有内容。

感谢您的关注!