UWP中的基本动画很好,除了我想创建自己的动画。所以我查看了不同的动画,发现它们都是Timeline
的子类。为了测试,我决定像这样做一个DoubleAnimation类的副本:
public sealed class MyAnimation : Timeline
{
public static DependencyProperty _ByProperty;
public static DependencyProperty _EasingFunctionProperty;
public static DependencyProperty _EnableDependentAnimationProperty;
public static DependencyProperty _FromProperty;
public static DependencyProperty _ToProperty;
public MyAnimation() : base()
{
}
static MyAnimation()
{
_ByProperty = DependencyProperty.Register("By", typeof(double?), typeof(MyAnimation), new PropertyMetadata((double?)null));
_EasingFunctionProperty = DependencyProperty.Register("EasingFunction", typeof(EasingFunctionBase), typeof(MyAnimation), new PropertyMetadata(null));
_EnableDependentAnimationProperty = DependencyProperty.Register("EnableDependentAnimation", typeof(bool), typeof(MyAnimation), new PropertyMetadata(false));
_FromProperty = DependencyProperty.Register("From", typeof(double?), typeof(MyAnimation), new PropertyMetadata((double?)null));
_ToProperty = DependencyProperty.Register("To", typeof(double?), typeof(MyAnimation), new PropertyMetadata((double?)null));
}
public static DependencyProperty ByProperty { get { return _ByProperty; } }
public static DependencyProperty EasingFunctionProperty { get { return _EasingFunctionProperty; } }
public static DependencyProperty EnableDependentAnimationProperty { get { return _EnableDependentAnimationProperty; } }
public static DependencyProperty FromProperty { get { return _FromProperty; } }
public static DependencyProperty ToProperty { get { return _ToProperty; } }
public double? To { get { return (double?)GetValue(_ToProperty); } set { SetValue(_ToProperty, value); } }
public double? From { get { return (double?)GetValue(_FromProperty); } set { SetValue(_FromProperty, value); } }
public bool EnableDependentAnimation { get { return (bool)GetValue(_EnableDependentAnimationProperty); } set { SetValue(_EnableDependentAnimationProperty, value); } }
public EasingFunctionBase EasingFunction { get { return (EasingFunctionBase)GetValue(_EasingFunctionProperty); } set { SetValue(_EasingFunctionProperty, value); } }
public double? By { get { return (double?)GetValue(_ByProperty); } set { SetValue(_ByProperty, value); } }
}
然后我创建了一个要移动的对象:
<Grid Name="Root" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Ellipse Width="200" Height="200" Fill="Green" Name="MyEllipse"/>
</Grid>
然后我在加载所有内容时启动动画:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Storyboard sb = new Storyboard();
MyAnimation da = new MyAnimation();
da.From = 200;
da.To = 500;
da.Duration = new Duration(TimeSpan.FromSeconds(5));
da.EnableDependentAnimation = true;
sb.Children.Add(da);
Storyboard.SetTargetProperty(da, "Width");
Storyboard.SetTarget(da, MyEllipse);
sb.Begin();
}
现在我的问题。当我运行这个时,我得到以下异常:
ERROR: System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component.
at Windows.UI.Xaml.Media.Animation.Storyboard.Begin()
at TestAnimation.MainPage.Button_Click(Object sender, RoutedEventArgs e)
对于出了什么问题,我没有给出任何解释。我猜测在构造函数中需要做更多的事情,但是我无法读取DoubleAnimation
的源代码,只读取元数据,这使得无法知道实际发生了什么。任何人都知道需要做什么才能让它发挥作用?
答案 0 :(得分:0)
请阅读有关该问题的评论,因为我认为它们仍然可以更好地回答该问题
但是,我遇到了这个问题,因为我也得到了
ERROR: System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component
就我而言,它与动画系统无关。这是因为我在缩放算法上犯了一个错误,并且从我的new Size(27688, 8)
返回了所需的override MeasureOverride(...)
。
答案 1 :(得分:0)
根据我的阅读,并且经过多次尝试,UWP无法提供创建自己的动画的方法,因为没有方法可以从时间轴中进行覆盖。
我的场景应该是一个简单的场景-如何使用DoubleAnimation为网格设置动画?听起来微不足道,但a)Grid占用GridLength而不是Double,b)无法将IValueConverter附加到StoryBoard.TargetProperty,并且c)UWP似乎不支持创建自己的自定义动画。
因此,剩下的唯一选择就是利用现有动画并在其上构建一个层。最常见的方法是在页面中使用两个依赖项属性,将其中一个与DoubleAnimation绑定,将另一个与Grid绑定。可行,但它不可扩展,尤其是当您要分离样式时。
这是我的方法概述: -创建将属性“ Converter”附加到任何依赖项对象的全局ConverterService -创建一个作为IValueConverter的通用转换器 -通用值转换器具有一个名为Tin的Input的DependencyProperty和另一个名为Tout类型的Output的DependencyProperty -将DoubleAnimation与转换器的输入绑定,并将输出与网格绑定。
使用方法如下:
<Grid Style="{StaticResource MainGrid}" x:Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=MyMove, Path=Output}" x:Name="MyColumn">
<conv:ConverterService.Converter>
<conv:DoubleToGridLengthConverter x:Name="MyMove" Input="2" GridUnit="Star" />
</conv:ConverterService.Converter>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Portrait">
<VisualState.StateTriggers>
<stateTriggers:WindowAspectRatioTrigger Orientation="Portrait" />
</VisualState.StateTriggers>
<VisualState.Storyboard>
<Storyboard>
<DoubleAnimation
BeginTime="0:0:0"
EnableDependentAnimation="True"
Storyboard.TargetName ="MyMove"
Storyboard.TargetProperty="Input"
From="1" To="15" Duration="0:0:3" FillBehavior="HoldEnd" />
</Storyboard>
</VisualState.Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
当然,请记住在页面顶部导入名称空间。 就我而言:
<base:BasePage
x:Class="MyProject.MainPage"
x:Name="MyPage"
xmlns:base ="using:MyProject.Base"
xmlns:local="using:MyProject"
xmlns:conv="using:MyProject.Converters"
xmlns:stateTriggers="using:MyProject.StateTriggers"
xmlns:ctrl="using:MyProject.UserControls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
这是ConverterService和BaseConverter和DoubleToGridLengthConverter的代码:
/// <summary>A generic converter.</summary>
public interface IConverter: IValueConverter
{
/// <summary>The input value.</summary>
object Input { get; set; }
/// <summary>The output value.</summary>
object Output { get; set; }
}
/// <summary>A service that provides conversion capabilities to dependency objects via an attached property.</summary>
public abstract class ConverterService : DependencyObject
{
/// <summary>The Converter dependency property.</summary>
public static readonly DependencyProperty ConverterProperty;
/// <summary>The Converters dependency property which is a collection of Converter.</summary>
public static readonly DependencyProperty ConvertersProperty;
static ConverterService()
{
ConverterProperty = DependencyProperty.RegisterAttached("Converter",
typeof(IConverter),
typeof(ConverterService),
new PropertyMetadata(null));
ConvertersProperty = DependencyProperty.RegisterAttached("Converters",
typeof(IList<IConverter>),
typeof(ConverterService),
new PropertyMetadata(new List<IConverter>()));
}
/// <summary>Property getter for attached property Converter.</summary>
/// <param name="element">The dependency object to which the attached property applies.</param>
/// <returns>Returns the converter associated with the specified dependency object.</returns>
public static IConverter GetConverter(DependencyObject element)
{
return (IConverter)element.GetValue(ConverterProperty);
}
/// <summary>Property getter for attached property Converter.</summary>
/// <param name="element">The dependency object to which the attached property applies.</param>
/// <param name="value">The converter to associate.</param>
public static void SetConverter(DependencyObject element, IConverter value)
{
element.SetValue(ConverterProperty, value);
}
/// <summary>Property getter for attached property Converters.</summary>
/// <param name="element">The dependency object to which the attached property applies.</param>
/// <returns>Returns the collection of converters associated with the specified dependency object.</returns>
public static IList<IConverter> GetConverters(DependencyObject element)
{
return (IList<IConverter>)element.GetValue(ConverterProperty);
}
/// <summary>Property getter for attached property Converters.</summary>
/// <param name="element">The dependency object to which the attached property applies.</param>
/// <param name="value">The converters to associate.</param>
public static void SetConverters(DependencyObject element, IList<IConverter> value)
{
element.SetValue(ConverterProperty, value);
}
}
/// <summary>A strongly-typed base converter.</summary>
/// <typeparam name="Tin">The input type.</typeparam>
/// <typeparam name="Tout">The output type.</typeparam>
public abstract class BaseConverter<Tin, Tout>: DependencyObject, IConverter
{
/// <summary>The Input dependency property.</summary>
public static readonly DependencyProperty InputProperty;
/// <summary>The Output dependency property.</summary>
public static readonly DependencyProperty OutputProperty;
static BaseConverter()
{
OutputProperty = DependencyProperty.Register("Output",
typeof(Tout),
typeof(BaseConverter<Tin, Tout>),
new PropertyMetadata(GetDefault(typeof(Tout)), OutChanged));
InputProperty = DependencyProperty.Register("Input",
typeof(Tin),
typeof(BaseConverter<Tin, Tout>),
new PropertyMetadata(GetDefault(typeof(Tin)), InChanged));
}
/// <summary>Gets or sets the input value to convert from.</summary>
public Tin Input
{
get { return (Tin)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
/// <summary>Gets or sets the output value to convert to.</summary>
public Tout Output
{
get { return (Tout)GetValue(OutputProperty); }
set { SetValue(OutputProperty, value); }
}
/// <summary>Gets the from type.</summary>
public static Type From
{
get { return typeof(Tin); }
}
/// <summary>Gets the to type.</summary>
public static Type To
{
get { return typeof(Tout); }
}
#region IConverter
object IConverter.Input { get => Input; set => Input = (Tin)value; }
object IConverter.Output { get => Output; set => Output = (Tout)value; }
#endregion
#region IValueConverter
object IValueConverter.Convert(object value, Type targetType, object parameter, string language)
{
return Convert((Tin)value, parameter, language);
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, string language)
{
return ConvertBack((Tout)value, parameter, language);
}
#endregion
/// <summary>Converts an input value into an output value.</summary>
/// <param name="value">The value to convert.</param>
/// <param name="parameter">An optional parameter to pass onto the conversion process (from IValueConverter).</param>
/// <param name="language">An optional language parameter to pass onto the conversion process (from IValueConverter).</param>
/// <returns>Returns the converted value.</returns>
protected abstract Tout Convert(Tin value, object parameter, string language);
/// <summary>Converts back an output value into its original input.</summary>
/// <param name="value">The value to convert.</param>
/// <param name="parameter">An optional parameter to pass onto the conversion process (from IValueConverter).</param>
/// <param name="language">An optional language parameter to pass onto the conversion process (from IValueConverter).</param>
/// <returns>Returns the converted value from output to input.</returns>
protected abstract Tin ConvertBack(Tout value, object parameter, string language);
private static object GetDefault(Type type)
{
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
private static void InChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as BaseConverter<Tin, Tout>;
control.Output = (Tout)((d as IValueConverter).Convert(e.NewValue, typeof(Tout), null, null));
}
private static void OutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as BaseConverter<Tin, Tout>;
//control.Input = (Tin)((d as IValueConverter).ConvertBack(e.NewValue, typeof(Tin), null, null));
}
}
/// <summary>Converts Double to and from GridLength.</summary>
public sealed class DoubleToGridLengthConverter : BaseConverter<double?, GridLength?>
{
/// <summary>The GridUnit dependency property.</summary>
public static readonly DependencyProperty GridUnitProperty;
static DoubleToGridLengthConverter()
{
GridUnitProperty = DependencyProperty.Register("GridUnit",
typeof(GridUnitType),
typeof(DoubleToGridLengthConverter),
new PropertyMetadata(GridUnitType.Auto, UnitChanged));
}
/// <summary>Gets or sets the type of grid unit to be used in the conversions.</summary>
public GridUnitType GridUnit
{
get { return (GridUnitType)GetValue(GridUnitProperty); }
set { SetValue(GridUnitProperty, value); }
}
protected override GridLength? Convert(double? value, object parameter, string language)
{
return value == null || !value.HasValue
? new GridLength()
: new GridLength(value.Value, this.GridUnit);
}
protected override double? ConvertBack(GridLength? value, object parameter, string language)
{
return value == null || !value.HasValue
? default(double?)
: value.Value.Value;
}
private static void UnitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as DoubleToGridLengthConverter;
control.Output = control.Convert(control.Input, null, null);
}
}
如果要使用我的AspectRatio状态触发器,则为:
/// <summary>Aspect ratios.</summary>
public enum AspectRatio
{
/// <summary>Portrait.</summary>
Portrait,
/// <summary>Landscape.</summary>
Landscape
}
/// <summary>A state trigger based on the aspect ratio of the window.</summary>
public class WindowAspectRatioTrigger: StateTriggerBase
{
/// <summary>The target orientation.</summary>
private static readonly DependencyProperty OrientationProperty;
static WindowAspectRatioTrigger()
{
OrientationProperty = DependencyProperty.Register("Orientation",
typeof(AspectRatio),
typeof(WindowAspectRatioTrigger),
new PropertyMetadata(AspectRatio.Landscape, new PropertyChangedCallback(DesiredAspectRatioChanged)));
}
public WindowAspectRatioTrigger()
{
this.OnAspectRatioChanged(this.Orientation);
Window.Current.SizeChanged += Current_SizeChanged;
}
/// <summary>Gets or sets the desired aspect ratio for the trigger.</summary>
public AspectRatio Orientation
{
get { return (AspectRatio)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
private async void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SetActive(IsActive(e.Size, this.Orientation));
});
}
private static void DesiredAspectRatioChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as WindowAspectRatioTrigger;
control.OnAspectRatioChanged((AspectRatio)e.NewValue);
}
private static bool IsActive(Size windowSize, AspectRatio aspectRatio)
{
var currentOrientation = windowSize.Width >= windowSize.Height
? AspectRatio.Landscape
: AspectRatio.Portrait;
return aspectRatio == currentOrientation;
}
private async void OnAspectRatioChanged(AspectRatio aspectRatio)
{
var dimensions = Window.Current.Bounds;
var size = new Size(dimensions.Width, dimensions.Height);
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SetActive(IsActive(size, aspectRatio));
});
}
}