Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XAML Without WPF - Animations

I am trying to use XAML completely outside WPF, in particular inside an XNA application. So far, I have managed (quite easily, I am surprised to admit) to load some data inside my XNA application from a XAML file. The problems start when I decided that I wanted to animate one of the properties of my class...Nothing happens :(

Here is the main class I load from the XAML file:

[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); } }
}

Here is the XAML file:

<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>

And here is the loading and animation launching (attempt):

Test test = XamlReader.Load(new XmlTextReader("SpriteBatchStuff.xaml")) as Test;
test.Animation.Begin(test);

I am dying of curiosity :)

like image 461
Giuseppe Maggiore Avatar asked Mar 05 '10 14:03

Giuseppe Maggiore


People also ask

Can WPF application be built without using XAML?

But WPF without XAML is possible. You don't even need Visual Studio installed for this, just the . NET CLI and the Developer Command Prompt. Drop these two files in a folder and run msbuild and then you can run the file in the Bin directory.

Can you animate without storyboard?

Animations can be applied without using the StoryBoard. BeginAnimation() method can be used to apply animations instead of StoryBoard. This method can be used when simple animations are applied to a property of a control.

Does WPF support animation?

WPF provides builtin support for animations. The WPF animation system makes it very easy to animate controls and other graphical objects.

Why XAML is used in WPF?

The goal of XAML is to enable visual designers to create user interface elements directly. WPF aims to make it possible to control all visual aspects of the user interface from mark-up.


1 Answers

Although XAML is independent of WPF, the visual elements aren't. In particular, animation and layout are part of WPF, and depends on the WPF plumbing being present -- through an Application object, a PresentationSource such as a HwndSource, the XBAP PresentationHost.exe, etc.

So you can read in your XAML and get an object graph of a Test object with a child Storyboard object, but that Test object isn't hooked up to the animation or layout engines until it's placed in a WPF context. All that the XAML gets you is a dumb in-memory object graph: it's WPF, not XAML, that makes the objects "live."

So as Ben says, you'll probably end up needing to "push or prod" the animation yourself. I'm not aware of any documentation on how to do this, but from poking around in Reflector, it looks like the key API is Storyboard.SeekAlignedToLastTick, of which the docs say:

Values are immediately updated to reflect the changes due to SeekAlignedToLastTick, even though the screen does not reflect these changes until the screen updates.

Notice that second clause. Normally, WPF handles the updating of the screen as visual object values change. If you're not using WPF, then it's up to you to read the changed values out and redraw the screen accordingly: you don't have the WPF layout manager to handle it for you.

Finally, please note I haven't tested whether SeekAlignedToLastTick will work in an environment without the WPF plumbing loaded. It sounds like it should, because it doesn't care whether it's WPF or user code which is driving the clocks, but I can't make any promises... though I admit you've got me curious!


UPDATE: I've given this a quick go, and it does seem to work. Here's a demo of hosting an animation within Windows Forms (in this case using a plain ol' Windows Forms timer, but in XNA I guess the framework will provide a game timer for you -- didn't try that because I don't know XNA). Assume you have a vanilla Windows Form with a timer (timer1) and a label (label1), and that the project references the WPF assemblies.

First, my simplified version of your class:

[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; }
}

Now, the WPF code to load one of these babies from XAML and begin the animation:

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;
}

Note that I had to set the storyboard's target to be the XAML object I'd just loaded. This doesn't happen automatically. I tried doing this with Storyboard.TargetName in the XAML, but that didn't seem to work -- you may have more luck.

The final lines are just setup for the timer callback:

private void timer1_Tick(object sender, EventArgs e)
{
  TimeSpan sinceStart = DateTime.Now - _startTime;
  _f.Animation.SeekAlignedToLastTick(sinceStart);

  label1.Text = _f.Test.ToString();
}

I've stored the start time of the animation, and used that to calculate how far into the animation we are. WinForms timers are a bit crude, but this suffices for proof of concept; no doubt XNA will have something better. Then I call Storyboard.SeekAlignedToLastTick, which updates the animated values. Nothing displays automatically because my XAML object isn't hooked up for display, but I can check its Test property and verify that it is indeed animating. In reality, I'd use this to update the position or orientation of whatever XNA visual element the XAML object represented.

like image 84
itowlson Avatar answered Sep 17 '22 17:09

itowlson