XAML без WPF - анимации

Я пытаюсь использовать XAML полностью вне WPF, в особенности в приложении XNA. До сих пор я справился (довольно легко, я удивлен признать) загрузить некоторые данные в моем приложении XNA из файла XAML. Проблемы запускаются, когда я решил, что хотел анимировать одно из свойств моего класса... Ничего не происходит :(

Вот основной класс, который я загружаю из файла 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);

Я умираю любопытства :)

8
задан Giuseppe Maggiore 5 March 2010 в 14:37
поделиться

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>

"code- за "классом 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); } }
}

В функции Initialize класса XNA Game мы десериализуем наш xaml-файл и запускаем анимацию:

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

Функция Update принимает в качестве входных данных GameTime, который предлагает поле TotalGameTime, в котором хранится TimeSpan количества времени, прошедшего с момента запуска приложения: это именно то, что нужно для раскадровки:

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

В методе рисования мы можем просто нарисовать текст, используя свойство 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);
}
4
ответ дан 5 December 2019 в 12:57
поделиться

Вне обычного цикла WPF-приложения, я сомневаюсь, что есть способ управлять анимацией. Возможно, есть какой-то класс, который можно подтолкнуть или подтолкнуть, чтобы управлять анимацией, но он, скорее всего, запечатан.

Скорее всего, вам придется создавать собственный механизм выполнения анимации, работающий в другом потоке, и следить за тем, чтобы обновления происходили в потоке UI, что означает либо найти способ повторно использовать диспетчер, либо воссоздать что-то подобное.

Эта статья MSDN может предоставить некоторую полезную информацию в этом начинании

Это интересный проект... Мне будет любопытно услышать, если у вас получится!

1
ответ дан 5 December 2019 в 12:57
поделиться

Вау, это просто потрясающе! К сожалению, это, скорее всего, сводится к некоторому вызову типа "update", который выполняется в каком-то внутреннем API. И если вы не вызовете его, анимация не будет анимироваться... примерно так же, как если в игре XNA не будет вызван метод Update.

Я бы очень хотел получить больше информации о том, как вы это делаете и какой уровень успеха вы нашли. Вы должны написать пост в блоге/статью где-нибудь :-)

1
ответ дан 5 December 2019 в 12:57
поделиться

Хотя XAML независим от WPF, визуальные элементы не являются таковыми. В частности, анимация и компоновка являются частью WPF и зависят от присутствия WPF plumbing - через объект Application, PresentationSource, такой как HwndSource, XBAP PresentationHost.exe и т. д.

Таким образом, вы можете прочитать XAML и получить объектный граф объекта Test с дочерним объектом Storyboard, но этот объект Test не подключен к механизмам анимации или компоновки, пока он не помещен в контекст WPF. Все, что дает вам XAML, - это тупой граф объектов в памяти: именно WPF, а не XAML, делает объекты "живыми".

Поэтому, как говорит Бен, вам, вероятно, придется "подталкивать или подталкивать" анимацию самостоятельно. Я не знаю никакой документации о том, как это сделать, но, порывшись в Reflector, похоже, что ключевым API является Storyboard.SeekAlignedToLastTick, о котором в документации сказано:

Значения немедленно обновляются, чтобы отражают изменения, вызванные SeekAlignedToLastTick, даже если экран не отражает эти изменения пока экран не обновится.

Обратите внимание на второе предложение. Обычно WPF обрабатывает обновление экрана при изменении значений визуальных объектов. Если вы не используете WPF, то вам придется считывать изменившиеся значения и перерисовывать экран соответствующим образом: у вас нет менеджера компоновки WPF, чтобы сделать это за вас.

Наконец, обратите внимание, что я не проверял, будет ли работать SeekAlignedToLastTick в окружении без загруженного WPF plumbing. Звучит похоже, что должна, поскольку ей все равно, WPF или пользовательский код управляет часами, но я не могу ничего обещать... хотя, признаюсь, мне стало любопытно!


UPDATE: Я попробовал, и, похоже, это действительно работает. Вот демонстрация размещения анимации в Windows Forms (в данном случае используется обычный таймер Windows Forms, но в XNA, я думаю, фреймворк предоставит вам игровой таймер - я не пробовал, потому что не знаю XNA). Предположим, что у вас есть ванильная Windows Form с таймером (timer1) и меткой (label1), и что проект ссылается на сборки 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;
}

Обратите внимание, что мне пришлось установить целью раскадровки объект XAML, который я только что загрузил. Это не происходит автоматически. Я попробовал сделать это с помощью Storyboard.TargetName в XAML, но, похоже, это не сработало - возможно, вам повезет больше.

Последние строки - это просто настройка для обратного вызова таймера:

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 и убедиться, что он действительно анимирован. В реальности я бы использовал это для обновления положения или ориентации любого визуального элемента XNA, который представляет объект XAML.

7
ответ дан 5 December 2019 в 12:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: