背景提要
我在封装样式时发现的问题,然而没有找到一个完整的例子来解决问题,或者说少。
问题出现
当给一个控件添加动画效果——当鼠标放置在控件上的时候,它的宽度会产生变化
我们可以使用EventTrigger,其中Width会在0.1秒内变化到110
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.100"
Storyboard.TargetProperty="Width" To="110" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.100"
Storyboard.TargetProperty="Width" To="100" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
但问题在于,我们在封装样式后,To的值是不可以被任意设置的。
于是将To绑定到一个变量中。
<DoubleAnimation Duration="0:0:0.100" Storyboard.TargetProperty="Width"
To="{TemplateBinding Width_increase}" />
就出现了开头的问题:
在查阅其他文献后,出现这个问题的原因是,计算机需要预知变化的值来渲染动画,这个过程叫作“冻结”,绑定后,To的值是难以预测的。
问题解决
大致意思是
1)To的值必须设置为常数
2)使用多个控件无用的属性(Tag)来表示变化
3)Width的值多绑定在各个Tag值上,经过转换器计算最终的值
下面开始举实际例子
newbutton.xaml:
<Style x:Key="NewbuttonStyle" TargetType="example:Newbutton">
<Style.Resources>
<converter:MultiplyConverter x:Key="MultiplyConverter" />
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="example:Newbutton">
<Canvas Width="{Binding Width, ElementName=ButtonBorder}"
HorizontalAlignment="Center"
Height="{Binding Height}"
x:Name="Tag_0to1">
<Canvas.Tag>
<sys:Double>0</sys:Double>
</Canvas.Tag>
<Border x:Name="ButtonBorder"
Background="White"
Height="{TemplateBinding Height}"
Tag="{TemplateBinding ButtonIncreaseWidth}"
BorderThickness="1"
BorderBrush="Black">
<Border.Width>
<MultiBinding Converter="{StaticResource MultiplyConverter}">
<Binding ElementName="Tag_0to1" Path="Tag"/>
<Binding ElementName="ButtonBorder" Path="Tag"/>
<Binding ElementName="ori_width" Path="Tag"/>
</MultiBinding>
</Border.Width>
<StackPanel x:Name="ori_width" Orientation="Horizontal" HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="{TemplateBinding Padding}" Tag="{TemplateBinding Width}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center">
</ContentPresenter>
</StackPanel>
</Border>
</Canvas>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.100" Storyboard.TargetName="Tag_0to1" Storyboard.TargetProperty="Tag" To="1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.100" Storyboard.TargetName="Tag_0to1" Storyboard.TargetProperty="Tag" To="0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="example:Newbutton" BasedOn="{StaticResource NewbuttonStyle}"></Style>
MultiplyConverter.cs:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace xxx.xx.x
{
public class MultiplyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (double)values[0] /*会从0直接变成1*/ * (double)values[1] /*放大的宽度*/ + (double)values[2] /*控件原本的宽度*/ ;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
其中控件的Width,ButtonIncreaseWidth的数据类型是double,Canvas.Tag需要
<sys:Double>0</sys:Double>
来表示为double类型
效果:
总结
Tag0to1.Tag会在Duration内逐渐由0变化为1
ButtonIncreaseWidth是控件即将变化的宽度
ori_Width是控件原本的宽度
控件最终宽度=Tag0to1.Tag * ButtonIncreaseWidth + ori_Width (Tag0to1.Tag 0->1)
ButtonIncreaseWidth也可以设置为负数,这样的话,当触发时,控件宽度减小。