我试图在WPF之外完全使用XAML,特别是在XNA应用程序中。到目前为止,我已经管理(很容易,我很惊讶地承认)从XAML文件加载我的XNA应用程序中的一些数据。当我决定要为我班级的一个属性制作动画时,问题就开始了......没有任何反应:(
这是我从XAML文件加载的主要类:
[ContentPropertyAttribute("Animation")]
public class Test : FrameworkContentElement
{
public string Text { get; set; }
public Vector2 Position { get; set; }
public Color Color { get; set; }
public Storyboard Animation { get; set; }
public static DependencyProperty RotationProperty =
DependencyProperty.Register("Rotation", typeof(double), typeof(Test), new PropertyMetadata(0.0));
public double Rotation { get { return (double)GetValue(RotationProperty); } set { SetValue(RotationProperty, value); } }
}
这是XAML文件:
<l:Test xmlns:l="clr-namespace:XAMLAndXNA;assembly=XAMLAndXNA"
xmlns:a1="clr-namespace:System.Windows.Media.Animation;assembly=PresentationFramework"
xmlns:a2="clr-namespace:System.Windows.Media.Animation;assembly=PresentationCore"
Text="Testo" Position="55,60" Color="0,255,255,255">
<a1:Storyboard>
<a2:DoubleAnimation a1:Storyboard.TargetProperty="Rotation"
From="0"
To="360"
Duration="00:00:10.0"/>
</a1:Storyboard>
</l:Test>
这是加载和动画启动(尝试):
Test test = XamlReader.Load(new XmlTextReader("SpriteBatchStuff.xaml")) as Test;
test.Animation.Begin(test);
我死于好奇心:)
答案 0 :(得分:6)
虽然XAML独立于WPF,但视觉元素却不是。特别是,动画和布局是 WPF的一部分,并依赖于WPF管道 - 通过Application对象,一个PresentationSource,如HwndSource,XBAP PresentationHost.exe等。
因此,您可以读取XAML并获取具有子Storyboard对象的Test对象的对象图,但该Test对象在放置在WPF上下文中之前不会连接到动画或布局引擎。 XAML给你的所有东西都是一个愚蠢的内存对象图:它是WPF,而不是XAML,使对象“活着”。
正如Ben所说,你可能最终需要自己“推动或刺激”动画。我不知道有关如何执行此操作的任何文档,但是通过在Reflector中进行讨论,看起来关键API是Storyboard.SeekAlignedToLastTick,其中文档说:
值会立即更新为 反映由于的变化 SeekAlignedToLastTick,即使是 屏幕不反映这些变化 直到屏幕更新。
注意第二个条款。通常,WPF在视觉对象值改变时处理屏幕的更新。如果您没有使用WPF,那么您可以自行阅读更改后的值并重新绘制屏幕:您没有WPF布局管理器来为您处理。
最后,请注意我没有测试SeekAlignedToLastTick是否可以在没有加载WPF管道的环境中工作。它应该是声音,因为它不关心它是WPF还是驱动时钟的用户代码,但我不能做出任何承诺......虽然我承认你已经拥有了我好奇!
更新:我快速给了它,它似乎确实有效。这是一个在Windows窗体中托管动画的演示(在这种情况下使用普通的'Windows窗体计时器,但在XNA中我想框架将为您提供一个游戏计时器 - 没试过,因为我不知道XNA)。假设您有一个带有计时器(timer1)和标签(label1)的vanilla窗体,并且该项目引用了WPF程序集。
首先,我的班级简化版:
[ContentProperty("Animation")]
public class Fie : DependencyObject
{
public double Test
{
get { return (double)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test", typeof(double), typeof(Fie),
new FrameworkPropertyMetadata(0.0));
public Storyboard Animation { get; set; }
}
现在,WPF代码从XAML加载其中一个婴儿并开始动画:
private Fie _f;
private DateTime _startTime;
public Form1()
{
InitializeComponent();
string xaml =
@"<local:Fie xmlns:local=""clr-namespace:AnimationsOutsideWpf;assembly=AnimationsOutsideWpf""
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty=""Test""
From=""0""
To=""360""
Duration=""00:00:10.0""/>
</Storyboard>
</local:Fie>";
_f = (Fie)XamlReader.Load(XmlReader.Create(new StringReader(xaml)));
Storyboard.SetTarget(_f.Animation, _f);
_f.Animation.Begin();
_startTime = DateTime.Now;
timer1.Enabled = true;
}
请注意,我必须将storyboard的目标设置为我刚刚加载的XAML对象。这不会自动发生。我尝试在XAML中使用Storyboard.TargetName执行此操作,但这似乎不起作用 - 您可能会有更多运气。
最后一行只是为定时器回调设置:
private void timer1_Tick(object sender, EventArgs e)
{
TimeSpan sinceStart = DateTime.Now - _startTime;
_f.Animation.SeekAlignedToLastTick(sinceStart);
label1.Text = _f.Test.ToString();
}
我已经存储了动画的开始时间,并使用它来计算动画的距离。 WinForms定时器有点粗糙,但这足以证明概念;毫无疑问,XNA会有更好的东西。然后我调用Storyboard.SeekAlignedToLastTick,它会更新动画值。没有任何东西会自动显示,因为我的XAML对象没有连接显示,但我可以检查它的Test属性并验证它确实是动画。实际上,我会用它来更新XAML对象所代表的任何XNA视觉元素的位置或方向。
答案 1 :(得分:4)
仅供参考,我现在将记录如何使用XNA进行此操作。感谢itowlson提供缺少的链接:否则我不得不创建一个带有隐形窗口的空应用程序......
我们在XAML中定义具有动画的类(注意xmlns指令):
<l:Test
xmlns:l="clr-namespace:XAMLAndXNA;assembly=XAMLAndXNA"
xmlns:a1="clr-namespace:System.Windows.Media.Animation;assembly=PresentationFramework"
xmlns:a2="clr-namespace:System.Windows.Media.Animation;assembly=PresentationCore"
Text="Testo" Position="55,60" Color="0,255,255,255">
<a1:Storyboard>
<a2:DoubleAnimation a1:Storyboard.TargetProperty="Rotation"
From="0"
To="6.28"
Duration="00:00:2.0"
RepeatBehavior="Forever"/>
</a1:Storyboard>
</l:Test>
“代码隐藏”类测试如下:
[ContentPropertyAttribute("Animation")]
public class Test : DependencyObject
{
public string Text { get; set; }
public Vector2 Position { get; set; }
public Color Color { get; set; }
public Storyboard Animation { get; set; }
public static DependencyProperty RotationProperty =
DependencyProperty.Register("Rotation", typeof(double), typeof(Test), new PropertyMetadata(0.0));
public double Rotation { get { return (double)GetValue(RotationProperty); } set { SetValue(RotationProperty, value); } }
}
在XNA Game类的Initialize函数中,我们反序列化我们的xaml文件并启动动画:
test = XamlReader.Load(new XmlTextReader("SpriteBatchStuff.xaml")) as Test;
Storyboard.SetTarget(test.Animation, test);
test.Animation.Begin();
Update函数将GameTime作为输入,它提供TotalGameTime字段,用于存储自应用程序启动以来经过的时间量的TimeSpan:这正是Storyboard需要勾选的内容:
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
test.Animation.SeekAlignedToLastTick(gameTime.TotalGameTime);
base.Update(gameTime);
}
在draw方法中,我们可以使用Rotation属性绘制一些文本,现在可以正确设置动画:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.DrawString(Content.Load<SpriteFont>("font"), test.Text, test.Position, test.Color, (float)test.Rotation, Vector2.Zero, 1.0f, SpriteEffects.None, 0.0f);
spriteBatch.End();
base.Draw(gameTime);
}
答案 2 :(得分:0)
在WPF应用程序的正常循环之外,我怀疑是否有任何方法可以驱动动画。可能会有一些课程可以推动或刺激它们,但它很可能是密封的。
您可能最终会在另一个线程上构建自己的动画执行引擎,并确保在您的UI线程上进行更新,这意味着要么找到重用Dispatcher的方法,要么重新创建类似的东西。
This MSDN article may provide some useful information in this endeavor
这是一个有趣的项目......我很想知道你是否成功了!
答案 3 :(得分:0)
我非常希望了解您如何做到这一点以及您找到的成功程度。你应该写一篇博客文章/文章: - )