определить анимации и триггеры как ресурс многократного использования? - PullRequest
13 голосов
/ 31 октября 2009

Есть ли способ определить анимацию где-нибудь в xaml (например, как ресурс) один раз, а затем повторно использовать ее несколько раз? У меня есть много независимых кистей для разных шаблонов данных, которые независимо друг от друга должны запускать анимацию одного и того же типа, основанную на датгергере. Теперь, поскольку кажется, что анимация должна определять Storyboard.TargetName и Storyboard.TargetProperty. Это в значительной степени побеждает цель повторного использования. Я хотел бы как-то объявить «использовать эту анимацию из ресурса, но на этот раз применить ее к другому элементу».

Мне кажется, это довольно простой, важный и важный запрос, и я удивлен, что его не так уж и легко выполнить. Я что-то здесь упускаю?

То же самое относится и к триггерам. Предположим, у меня есть много разных визуальных элементов, которые все представляют один и тот же тип состояния, используя цветную анимацию. Например. исчезают до зеленого, когда «активный», переходят в «красный», когда «ошибка» и т. д. Единственное различие между визуалами состоит в том, что их дерево форм / визуалов имеет одинаковое желаемое поведение анимации, все они имеют элемент где-то в своем дереве визуалов, который имеет свойство типа color. Я думаю, нетрудно представить, насколько утомительно пересматривать одни и те же анимации и наборы данных снова и снова. Каждый разработчик ненавидит это. Я отчаянно ищу более простое решение, которое не требует (или, по крайней мере, очень мало) кода C # позади.

То, что я до сих пор придумал, это:

Определите анимации в ресурсе, например: (повторите это для всех основных состояний, таких как активация, активация, неактивность, ошибка):

<ColorAnimationUsingKeyFrames x:Key="deactivatingColorAnimation" 
                    Storyboard.TargetProperty="Material.(MaterialGroup.Children)[0].Brush.(SolidColorBrush.Color)"                    
                    FillBehavior="HoldEnd" RepeatBehavior="Forever" AutoReverse="True">
      <ColorAnimationUsingKeyFrames.KeyFrames>
        <LinearColorKeyFrame KeyTime="00:00:00" Value="Gray"/>
        <LinearColorKeyFrame KeyTime="00:00:0.25" Value="Gray"/>
        <LinearColorKeyFrame KeyTime="00:00:0.5" Value="Gray" />
        <LinearColorKeyFrame KeyTime="00:00:0.75" Value="Gray" />
     </ColorAnimationUsingKeyFrames.KeyFrames>
</ColorAnimationUsingKeyFrames>

Используйте его в раскадровке в триггерах (повторяйте это миллионы раз для каждого состояния X каждого отдельного состояния, всегда придумывайте новое имя для раскадровки):

<DataTrigger Binding="{Binding SubstrateHolder.State}" Value="Deactivating">
        <DataTrigger.EnterActions>
            <BeginStoryboard x:Name="someStateVisualDeactivatingStoryboard">
                <Storyboard Storyboard.TargetName="someStateVisual">
                    <StaticResource ResourceKey="deactivatingColorAnimation" />
                </Storyboard>
            </BeginStoryboard>
        </DataTrigger.EnterActions>
        <DataTrigger.ExitActions>
            <RemoveStoryboard BeginStoryboardName="someStateVisualDeactivatingStoryboard" />
        </DataTrigger.ExitActions>
</DataTrigger>

Вы можете легко представить, сколько раздуваемого XAML мне приходится многократно копировать и вставлять для всех этих миллиардов DataTriggers.

Было бы здорово определить все эти триггеры один раз и применить их к различным визуальным состояниям. Как что-то подобное решается в WPF? Любой совет?

Ответы [ 4 ]

3 голосов
/ 07 ноября 2009

Не могли бы вы попробовать что-нибудь подобное?

  • Оберните все ваши текущие шаблоны управления невидимым корневым элементом, например, Border или StackPanel, чья ограничительная рамка будет покрывать весь элемент управления.
  • Создайте шаблон стиля или элемента управления для этого невидимого поля, которое содержит все ваши триггеры и анимацию.
  • Пусть анимация анимирует произвольное свойство Color на невидимом поле.
  • В визуальных деревьях для всех ваших различных элементов управления свяжите любые свойства, которые вы хотите анимировать, со свойством Color невидимого корневого элемента.
1 голос
/ 10 ноября 2009

Кажется, не существует какого-либо хорошего решения только для XAML для этой общей проблемы. В итоге я написал свои собственные прикрепленные свойства, которые определяют все анимационные поведения для данного элемента. Примерно так:

<DataTemplate>
   <!-- ...  -->
   <Rectangle Fill="Gray">
     <v:AnimationHelper.Animations>
        <v:StandardColorStateAnimation TargetColorProperty="(Rectangle.Fill).(SolidColorBrush.Color)" TargetStateProperty={Binding State} />
     </v:AnimationHelper.Animations>
   </Rectangle>
<DataTemplate>

Все остальное (создание анимации и т. Д.) Выполняется в следующем коде.

0 голосов
/ 27 января 2018

Самый " XAML способ" для достижения этой цели, о котором я могу подумать, - это создать выделенный MarkupExtension, который будет извлекать анимацию из словаря ресурсов и устанавливать необходимые свойства - я предполагаю, что они ограничены подмножество Storyboard.Target, Storyboard.TargetName и Storyboard.TargetProperty. Хотя это требует некоторого кода, это одноразовое усилие, более того, MarkupExtension s предназначены для использования с XAML . Вот самая простая версия:

[MarkupExtensionReturnType(typeof(Timeline))]
public class AnimationResourceExtension : StaticResourceExtension
{
    //This property is for convienience so that we
    //don't have to remember to set x:Shared="False"
    //on all animation resources, but have an option
    //to avoid redundant cloning if it is
    public bool IsShared { get; set; } = true;

    public DependencyObject Target { get; set; }

    public string TargetName { get; set; }

    public PropertyPath TargetProperty { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (base.ProvideValue(serviceProvider) is Timeline animation)
        {
            //If the animation is shared we shall clone it
            //Checking if it is frozen is optional and we can
            //either clone it or throw an exception
            //(or simply proceed knowing one will be thrown anyway)
            if (IsShared || animation.IsFrozen)
                animation = animation.Clone();
            Storyboard.SetTarget(animation, Target);
            Storyboard.SetTargetName(animation, TargetName);
            Storyboard.SetTargetProperty(animation, TargetProperty);
            return animation;
        }
        else
            throw new XamlException("The referenced resource is not an animation");
    }
}

Использование очень просто:

<FrameworkElement.Resources>
    <DoubleAnimation x:Key="MyAnimation" From="0" To="1" Duration="0:0:1" />
</FrameworkElement.Resources>
(...)
<Storyboard>
    <utils:AnimationResource ResourceKey="MyAnimation" TargetName="SomeElement" TargetProperty="Opacity" />
</Storyboard>

Будучи настолько простым, насколько это возможно, это решение имеет свои ограничения - оно не поддерживает ни Binding, ни DynamicResource расширений для вышеупомянутых свойств. Это, однако, достижимо, но требует дополнительных усилий. Поддержка Binding должна быть довольно простой - вопрос правильного использования XamlSetMarkupExtensionAttribute (плюс некоторый шаблонный код). Поддержка DynamicResource будет немного сложнее, и в дополнение к использованию XamlSetMarkupExtensionAttribute потребуется обернуть IServiceProvider, чтобы вернуть адекватную реализацию IProvideValueTarget, но все еще возможно.

0 голосов
/ 18 декабря 2009

Я понимаю, что эта проблема немного устарела на момент публикации, но я нашел решение, которое требует очень мало кода.

Вы можете создать UserControl с пользовательскими свойствами (прокрутить вниз до рисунка 8), который содержит ваш прямоугольник, а также анимацию и триггеры состояния. Этот пользовательский элемент управления будет определять открытое свойство, такое как состояние, которое будет вызывать изменение цвета при изменении.

Единственный требуемый код должен создать ваши переменные в коде.

Пользовательский элемент управления может многократно использоваться без переписывания раскадровок XAML или триггеров данных.

...