如何从XAML元素的资源中访问一个故事板? [英] How to access a storyboard within an element resources from XAML?
问题描述
考虑这个code:
<用户控件X:类=MyApp.MyControl
...
XMLNS:地方=CLR的命名空间:MyApp的
的DataContext ={绑定的RelativeSource = {的RelativeSource模式=自}}> < UserControl.Template>
<&控件模板GT;
< ControlTemplate.Resources>
<情节提要X:键=MyStory>
< ColorAnimationUsingKeyFrames Storyboard.TargetProperty =。(Border.BorderBrush)(SolidColorBrush.Color)Storyboard.TargetName =brdBase>
< SplineColorKeyFrame KeyTime =0:0:1VALUE =红/>
< / ColorAnimationUsingKeyFrames>
< /故事板>
< /ControlTemplate.Resources> < BORDER X:NAME =brdBase了borderThickness =1BorderBrush =青色背景=黑>
...
< /边框> < ControlTemplate.Triggers>
<触发SOURCENAME =brdBase属性=IsMouseOverVALUE =真>
< Trigger.EnterActions>
< BeginStoryboard故事板={StaticResource的MyStory}/>
< /Trigger.EnterActions>
< /触发>
< /ControlTemplate.Triggers>
< /控件模板>
< /UserControl.Template>
< /用户控件>
以上code工作没有问题。现在,我想 MyStory
的绑定关键帧的值给该用户控制等构成的DP(名为 SpecialColor
)所以:
<情节提要X:键=MyStory>
< ColorAnimationUsingKeyFrames Storyboard.TargetProperty =。(Border.BorderBrush)(SolidColorBrush.Color)Storyboard.TargetName =brdBase>
< SplineColorKeyFrame KeyTime =0:0:1VALUE ={绑定的RelativeSource = {的RelativeSource模式= FindAncestor,AncestorType = {X:类型本地:MyControl}},路径= SpecialColor}/>
< / ColorAnimationUsingKeyFrames>
< /故事板>
这使得一个错误:
的无法冻结这个故事板时间表树用于跨线程。的
这是可能的背后做到这一点使用code。但是,我怎么能做到这一点XAML中的?
code-背后辅助解决方案:
►的步骤1:的把 MyStory
故事板到 brdBase
资源
< UserControl.Template>
<&控件模板GT;
< BORDER X:NAME =brdBase了borderThickness =1BorderBrush =青色背景=黑>
< Border.Resources>
<情节提要X:键=MyStory>
< ColorAnimationUsingKeyFrames Storyboard.TargetProperty =。(Border.BorderBrush)(SolidColorBrush.Color)Storyboard.TargetName =brdBase>
< SplineColorKeyFrame KeyTime =0:0:1VALUE ={绑定的RelativeSource = {的RelativeSource模式= FindAncestor,AncestorType = {X:类型本地:MyControl}},路径= SpecialColor}/>
< / ColorAnimationUsingKeyFrames>
< /故事板>
< /Border.Resources>
...
< /边框> < ControlTemplate.Triggers>
<触发SOURCENAME =brdBase属性=IsMouseOverVALUE =真>
< Trigger.EnterActions>
< BeginStoryboard故事板={StaticResource的MyStory}/>
< /Trigger.EnterActions>
< /触发>
< /ControlTemplate.Triggers>
< /控件模板>
< /UserControl.Template>
错误: 找不到资源名为MyStory。资源名称是区分大小写的。的
►的第二步:的消除触发
在 IsMouseOver
属性并开始 MyStory
从code的后面。
< UserControl.Template>
<&控件模板GT;
< BORDER X:NAME =brdBase了borderThickness =1BorderBrush =青色背景=黑的MouseEnter =brdBase_MouseEnter>
< Border.Resources>
<情节提要X:键=MyStory>
< ColorAnimationUsingKeyFrames Storyboard.TargetProperty =。(Border.BorderBrush)(SolidColorBrush.Color)Storyboard.TargetName =brdBase>
< SplineColorKeyFrame KeyTime =0:0:1VALUE ={绑定的RelativeSource = {的RelativeSource模式= FindAncestor,AncestorType = {X:类型本地:MyControl}},路径= SpecialColor}/>
< / ColorAnimationUsingKeyFrames>
< /故事板>
< /Border.Resources>
< /边框>
< /控件模板>
< /UserControl.Template>
C#code-背后:
私人无效brdBase_MouseEnter(对象发件人,MouseEventArgs E)
{
边境grdRoot =(境)this.Template.FindName(brdBase,这一点);
故事板故事= grdRoot.Resources [MyStory]作为故事板; story.Begin(这一点,this.Template);
}
►<青霉>步骤3:的溶液已经完成,但它不会在第一次工作。幸运的是,这个问题的解决方法。这足以把控件模板
在风格
。
(我需要其他触发
类型比的EventTrigger
,必须包裹用户控件
与元素控件模板
)
更新:
有关使用的想法的ObjectDataProvider
失败。
- 的的ObjectDataProvider 的资源不能被用于提供故事板!错误报告是:
XamlParseException:设置属性System.Windows.Media.Animation.BeginStoryboard.Storyboard引发了异常。
的InnerException:的'System.Windows.Data.ObjectDataProvider'不是财产故事板的有效值。
- 的 AssociatedControl 的DP总是空。
下面是code:
&LT; UserControl.Template&GT;
&LT;&控件模板GT;
&LT; ControlTemplate.Resources&GT;
&lt;局部:StoryboardFinder X:键=StoryboardFinder1AssociatedControl ={绑定的ElementName = brdBase}/&GT;
&LT; ObjectDataProvider的X:键=dataProvider中对象实例={StaticResource的StoryboardFinder1}方法名=搜索&GT;
&LT; ObjectDataProvider.MethodParameters&GT;
&LT; SYS:字符串&GT; MyStory&LT; / SYS:字符串&GT;
&LT; /ObjectDataProvider.MethodParameters>
&LT; / ObjectDataProvider的&GT;
&LT; /ControlTemplate.Resources> &LT; BORDER X:NAME =brdBase了borderThickness =1BorderBrush =青色背景=黑&GT;
&LT; Border.Resources&GT;
&LT;情节提要X:键=MyStory&GT;
&LT; ColorAnimationUsingKeyFrames Storyboard.TargetProperty =。(Border.BorderBrush)(SolidColorBrush.Color)Storyboard.TargetName =brdBase&GT;
&LT; SplineColorKeyFrame KeyTime =0:0:1VALUE ={绑定的RelativeSource = {的RelativeSource模式= FindAncestor,AncestorType = {X:类型本地:MyControl}},路径= SpecialColor}/&GT;
&LT; / ColorAnimationUsingKeyFrames&GT;
&LT; /故事板&GT;
&LT; /Border.Resources>
...
&LT; /边框&GT; &LT; ControlTemplate.Triggers&GT;
&LT;触发SOURCENAME =brdBase属性=IsMouseOverVALUE =真&GT;
&LT; Trigger.EnterActions&GT;
&LT; BeginStoryboard故事板={StaticResource的dataProvider中}/&GT;
&LT; /Trigger.EnterActions>
&LT; /触发&GT;
&LT; /ControlTemplate.Triggers>
&LT; /控件模板&GT;
&LT; /UserControl.Template>
该StoryboardFinder类:
公共类StoryboardFinder:DependencyObject的
{
#区域________________________________________ AssociatedControl 公共控制AssociatedControl
{
{返回(控制)的GetValue(AssociatedControlProperty); }
集合{的SetValue(AssociatedControlProperty,值); }
} 公共静态只读的DependencyProperty AssociatedControlProperty =
DependencyProperty.Register(AssociatedControl
typeof运算(控制),
typeof运算(StoryboardFinder)
新FrameworkPropertyMetadata(NULL,FrameworkPropertyMetadataOptions.None)); #endregion 公共故事板取景器(字符串资源名称)
{
//
//相关的控制总是空:(
//
返回新故事板();
}
}
如果这是什么code是真的?
&LT;用户控件X:类=MyApp.MyControl
...
的xmlns:I =CLR的命名空间:System.Windows.Interactivity;装配= System.Windows.Interactivity
的xmlns:L =CLR的命名空间:MyApp的
的DataContext ={绑定的RelativeSource = {的RelativeSource模式=自}}&GT; &LT; UserControl.Resources&GT;
&LT;风格的TargetType ={X:I型:MyControl}&GT;
&LT; setter属性=模板&GT;
&LT; Setter.Value&GT;
&LT;的ControlTemplate的TargetType ={X:I型:MyControl}&GT;
&LT; BORDER X:NAME =brdBase了borderThickness =1BorderBrush =青色背景=黑&GT;
&LT; Border.Resources&GT;
&LT;情节提要X:键=MyStory&GT;
&LT; ColorAnimationUsingKeyFrames Storyboard.TargetProperty =。(Border.BorderBrush)(SolidColorBrush.Color)Storyboard.TargetName =brdBase&GT;
&LT; SplineColorKeyFrame KeyTime =0:0:1VALUE ={绑定的RelativeSource = {的RelativeSource模式= FindAncestor,AncestorType = {X:I型:MyControl}},路径= SpecialColor}/&GT;
&LT; / ColorAnimationUsingKeyFrames&GT;
&LT; /故事板&GT;
&LT; /Border.Resources> &LT; I:Interaction.Triggers&GT;
&LT; L:InteractiveTrigger属性=IsMouseOverVALUE =真&GT;
&LT; L:InteractiveTrigger.CommonActions&GT;
&LT; BeginStoryboard故事板={StaticResource的MyStory}/&GT;
&LT; / L:InteractiveTrigger.CommonActions&GT;
&LT; / L:InteractiveTrigger&GT;
&LT; /我:Interaction.Triggers&GT;
&LT; /边框&GT;
&LT; /控件模板&GT;
&LT; /Setter.Value>
&LT; /二传手&GT;
&LT; /样式和GT;
&LT; /UserControl.Resources>
&LT; /用户控件&GT;
如果是这样,我可以让 IsMouseOver
属性触发器...
我很高兴地说这是一个工作code :)我只能在&LT使用
标记。这是限制。于是我开始思考这样的想法:如果我能有什么自定义触发可在的EventTrigger
; Border.Triggers&GT; FrameworkElement.Triggers
范围工作?这里是code:
使用系统;
使用System.Collections.Generic;
使用System.ComponentModel;
使用System.Windows;
使用System.Windows.Interactivity;
用System.Windows.Media.Animation;命名空间TriggerTest
{
///&LT;总结&gt;
/// InteractiveTrigger是可以用作System.Windows.Trigger但在System.Windows.Interactivity触发。
///&LT;&款GT;
///注意:既不是`EnterActions`也不`ExitActions`在这个类。在`CommonActions`可以用来代替`EnterActions`。
///此外,可以使用的`Actions`属性是类型System.Windows.Interactivity.TriggerAction的。
///&LT; /对&GT;
///&LT;&款GT; &LT; /对&GT;
///&LT;&款GT;
///只有一种在System.Windows.Interactivity触发器(即的EventTrigger)的。所以,你可以使用此命名空间中的以下触发器:
///&LT;&款GT 1- InteractiveTrigger:触发&LT; /对&GT;
///&所述;对将2- InteractiveMultiTrigger:MultiTrigger&下; /段&GT;
///&所述;段&将3- InteractiveDataTrigger:DataTrigger&下; /段&GT;
///&所述;对将4- InteractiveMultiDataTrigger:MultiDataTrigger&下; /段&GT;
///&LT; /对&GT;
///&LT; /总结&gt;
公共类InteractiveTrigger:TriggerBase&LT;&FrameworkElement的GT;
{
#区域___________________________________________________________________________________属性 #区域________________________________________值 ///&LT;总结&gt;
/// [包装属性ValueProperty]
///&LT;&款GT;
///获取或设置为与元件的特性值进行比较的值。比较结果为基准相等性检查。
///&LT; /对&GT;
///&LT; /总结&gt;
公共对象值
{
{返回(对象)的GetValue(ValueProperty); }
集合{的SetValue(ValueProperty,值); }
} 公共静态只读的DependencyProperty ValueProperty =
DependencyProperty.Register(值,
typeof运算(对象),
typeof运算(InteractiveTrigger)
新FrameworkPropertyMetadata(NULL,FrameworkPropertyMetadataOptions.None,OnValuePropertyChanged)); 私有静态无效OnValuePropertyChanged(DependencyObject的发件人,DependencyPropertyChangedEventArgs E)
{
InteractiveTrigger例如=发件人为InteractiveTrigger; 如果(比如!= NULL)
{
如果(instance.CanFire)
instance.Fire();
}
} #endregion
///&LT;总结&gt;
///获取或设置与导致要施加的关联的制定者的属性的对象的名称。
///&LT; /总结&gt;
公共字符串SOURCENAME
{
得到;
组;
} ///&LT;总结&gt;
///获取或设置返回与此trigger.Value属性比较值的属性。比较结果为基准相等性检查。
///&LT; /总结&gt;
公共属性的DependencyProperty
{
得到;
组;
} ///&LT;总结&gt;
///获取或设置System.Windows.Setter对象,描述当在触发对象成为激活的属性值以应用的集合。
///&LT; /总结&gt;
公开名单&LT;二传手&GT;塞特斯
{
得到;
组;
} ///&LT;总结&gt;
///获取或设置System.Windows.TriggerAction的集合对象时,该触发器对象被激活申请。
///&LT; /总结&gt;
公开名单&LT; System.Windows.TriggerAction&GT; CommonActions
{
得到;
组;
} ///&LT;总结&gt;
///获取指示该触发器是否可以主动申请制定者和行动的价值。
///&LT; /总结&gt;
私人布尔CanFire
{
得到
{
如果(this.AssociatedObject == NULL)
{
返回false;
}
其他
{
反对associatedValue; 如果(string.IsNullOrEmpty(SOURCENAME))
associatedValue = this.AssociatedObject.GetValue(物业);
其他
associatedValue =(this.AssociatedObject.FindName(SOURCENAME)为DependencyObject的).GetValue(物业); 类型转换器的TypeConverter = TypeDescriptor.GetConverter(Property.PropertyType);
对象realValue = typeConverter.ConvertFromString(Value.ToString()); 返回associatedValue.Equals(realValue);
}
}
} #endregion
#区域___________________________________________________________________________________方法 ///&LT;总结&gt;
///触发(激活)当前触发设置二传手值和调用的所有操作。
///&LT; /总结&gt;
私人无效火()
{
//
//设置setter方法值及其相关属性..
//
的foreach(在二传手二传手二传手)
{
如果(string.IsNullOrEmpty(setter.TargetName))
this.AssociatedObject.SetValue(setter.Property,setter.Value);
其他
(this.AssociatedObject.FindName(setter.TargetName)为DependencyObject的).SetValue(setter.Property,setter.Value);
} //
//射击动作..
//
的foreach(在CommonActions System.Windows.TriggerAction动作)
{
键入操作类型= action.GetType(); 如果(操作类型== typeof运算(BeginStoryboard))
{
(动作如BeginStoryboard).Storyboard.Begin();
}
其他
抛出新NotImplementedException();
} this.InvokeActions(NULL);
} #endregion
#区域___________________________________________________________________________________活动 公共InteractiveTrigger()
{
塞特斯=新的List&LT;二传手&GT;();
CommonActions =新的List&LT; System.Windows.TriggerAction&GT;();
} 保护覆盖无效OnAttached()
{
base.OnAttached(); 如果(属性!= NULL)
{
反对propertyAssociatedObject; 如果(string.IsNullOrEmpty(SOURCENAME))
propertyAssociatedObject = this.AssociatedObject;
其他
propertyAssociatedObject = this.AssociatedObject.FindName(SOURCENAME); //
//添加属性改变监听器属性相关联的对象..
//
DependencyPropertyDescriptor dpDescriptor = DependencyPropertyDescriptor.FromProperty(房产,propertyAssociatedObject.GetType());
dpDescriptor.AddValueChanged(propertyAssociatedObject,PropertyListener_ValueChanged);
}
} 保护覆盖无效OnDetaching()
{
base.OnDetaching(); 如果(属性!= NULL)
{
反对propertyAssociatedObject; 如果(string.IsNullOrEmpty(SOURCENAME))
propertyAssociatedObject = this.AssociatedObject;
其他
propertyAssociatedObject = this.AssociatedObject.FindName(SOURCENAME); //
//删除previously添加的属性从相关对象改变监听器..
//
DependencyPropertyDescriptor dpDescriptor = DependencyPropertyDescriptor.FromProperty(房产,propertyAssociatedObject.GetType());
dpDescriptor.RemoveValueChanged(propertyAssociatedObject,PropertyListener_ValueChanged);
}
} 私人无效PropertyListener_ValueChanged(对象发件人,EventArgs的发送)
{
如果(CanFire)
火();
} #endregion
}
}
我还创建了其他触发类型(即 InteractiveMultiTrigger
, InteractiveDataTrigger
, InteractiveMultiDataTrigger
),以及一些更多的操作,这使得有可能有一个条件和多条件EventTriggers。我会公布所有这些,如果你专业的人证实了这一解决方案。
感谢您的关注!
Consider this code:
<UserControl x:Class="MyApp.MyControl"
...
xmlns:local="clr-namespace:MyApp"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<UserControl.Template>
<ControlTemplate>
<ControlTemplate.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="Red"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Cyan" Background="Black">
...
</Border>
<ControlTemplate.Triggers>
<Trigger SourceName="brdBase" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MyStory}"/>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
</UserControl>
The above code works with no problem. Now, I wanna bind key-frame value of MyStory
to a DP (named SpecialColor
) of this user-control like so:
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
which makes an error:
Cannot freeze this Storyboard timeline tree for use across threads.
It's possible to do this using code behind. But how can I do it in XAML only?
Code-Behind Aided Solution:
►Step 1: Putting the MyStory
storyboard into the brdBase
resources.
<UserControl.Template>
<ControlTemplate>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Cyan" Background="Black">
<Border.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
...
</Border>
<ControlTemplate.Triggers>
<Trigger SourceName="brdBase" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MyStory}"/>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
Error: Cannot find resource named 'MyStory'. Resource names are case sensitive.
►Step 2: Eliminating Trigger
on IsMouseOver
property and begin the MyStory
from code behind.
<UserControl.Template>
<ControlTemplate>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Cyan" Background="Black" MouseEnter="brdBase_MouseEnter">
<Border.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
</Border>
</ControlTemplate>
</UserControl.Template>
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);
}
►Step 3: The solution is already done, but it doesn't work at the first time. Fortunately, there is a workaround for this issue. It's enough to put the ControlTemplate
in a Style
.
(I need other Trigger
types than EventTrigger
and must wrap the UserControl
elements with the ControlTemplate
.)
Update:
The idea about using ObjectDataProvider
failed.
- An ObjectDataProvider resource cannot be used to provide a storyboard!!! The error report is:
- XamlParseException: Set property 'System.Windows.Media.Animation.BeginStoryboard.Storyboard' threw an exception.
- InnerException: 'System.Windows.Data.ObjectDataProvider' is not a valid value for property 'Storyboard'.
- The AssociatedControl DP is always null.
Here is the code:
<UserControl.Template>
<ControlTemplate>
<ControlTemplate.Resources>
<local:StoryboardFinder x:Key="StoryboardFinder1" AssociatedControl="{Binding ElementName=brdBase}"/>
<ObjectDataProvider x:Key="dataProvider" ObjectInstance="{StaticResource StoryboardFinder1}" MethodName="Finder">
<ObjectDataProvider.MethodParameters>
<sys:String>MyStory</sys:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ControlTemplate.Resources>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Cyan" Background="Black">
<Border.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
...
</Border>
<ControlTemplate.Triggers>
<Trigger SourceName="brdBase" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource dataProvider}"/>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
The StoryboardFinder class:
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();
}
}
What if this code was true?
<UserControl x:Class="MyApp.MyControl"
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:l="clr-namespace:MyApp"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<UserControl.Resources>
<Style TargetType="{x:Type l:MyControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type l:MyControl}">
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Cyan" Background="Black">
<Border.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type l:MyControl}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
<i:Interaction.Triggers>
<l:InteractiveTrigger Property="IsMouseOver" Value="True">
<l:InteractiveTrigger.CommonActions>
<BeginStoryboard Storyboard="{StaticResource MyStory}"/>
</l:InteractiveTrigger.CommonActions>
</l:InteractiveTrigger>
</i:Interaction.Triggers>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
</UserControl>
If so, I could have a Trigger on IsMouseOver
property...
I'm glad to say it's a working code :) I could only use EventTrigger
in <Border.Triggers>
tag. It was the limitation. So I started thinking about this idea: What if I could have a custom trigger which can work in FrameworkElement.Triggers
scope? Here is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Media.Animation;
namespace TriggerTest
{
/// <summary>
/// InteractiveTrigger is a trigger that can be used as the System.Windows.Trigger but in the System.Windows.Interactivity.
/// <para>
/// 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.
/// </para>
/// <para> </para>
/// <para>
/// There is only one kind of triggers (i.e. EventTrigger) in the System.Windows.Interactivity. So you can use the following triggers in this namespace:
/// <para>1- InteractiveTrigger : Trigger</para>
/// <para>2- InteractiveMultiTrigger : MultiTrigger</para>
/// <para>3- InteractiveDataTrigger : DataTrigger</para>
/// <para>4- InteractiveMultiDataTrigger : MultiDataTrigger</para>
/// </para>
/// </summary>
public class InteractiveTrigger : TriggerBase<FrameworkElement>
{
#region ___________________________________________________________________________________ Properties
#region ________________________________________ Value
/// <summary>
/// [Wrapper property for ValueProperty]
/// <para>
/// Gets or sets the value to be compared with the property value of the element. The comparison is a reference equality check.
/// </para>
/// </summary>
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
/// <summary>
/// Gets or sets the name of the object with the property that causes the associated setters to be applied.
/// </summary>
public string SourceName
{
get;
set;
}
/// <summary>
/// Gets or sets the property that returns the value that is compared with this trigger.Value property. The comparison is a reference equality check.
/// </summary>
public DependencyProperty Property
{
get;
set;
}
/// <summary>
/// Gets or sets a collection of System.Windows.Setter objects, which describe the property values to apply when the trigger object becomes active.
/// </summary>
public List<Setter> Setters
{
get;
set;
}
/// <summary>
/// Gets or sets the collection of System.Windows.TriggerAction objects to apply when this trigger object becomes active.
/// </summary>
public List<System.Windows.TriggerAction> CommonActions
{
get;
set;
}
/// <summary>
/// Gets a value indicating whether this trigger can be active to apply setters and actions.
/// </summary>
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
/// <summary>
/// Fires (activates) current trigger by setting setter values and invoking all actions.
/// </summary>
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<Setter>();
CommonActions = new List<System.Windows.TriggerAction>();
}
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
}
}
I've also created other trigger types (i.e. InteractiveMultiTrigger
, InteractiveDataTrigger
, InteractiveMultiDataTrigger
) as well as some more actions which makes it possible to have a conditional and multi-conditional EventTriggers. I'll publish them all if you professional guys confirm this solution.
Thanks for your attention!
这篇关于如何从XAML元素的资源中访问一个故事板?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!