Pergunta

Existe uma maneira para definir um lugar de animação em XAML (ex. Como um recurso) uma vez e depois reutilizá-lo várias vezes? Eu tenho um monte de escovas independentes em todo DataTemplates differnt que, independentemente precisa para começar o mesmo tipo de animação baseado em uma DataTrigger. Agora, já que parece que uma animação tem de definir uma Storyboard.TargetName e Storyboard.TargetProperty. Este bonito muito frustra a finalidade de reutilização. Gostaria de alguma forma como a Declarar "usar esta forma de animação o recurso, mas aplicá-la a um outro elemento desta vez".

Para mim, este parece ser um pedido bastante básico, importante e essencial e estou surpreso que não é que a frente para acomplish. Estou faltando alguma coisa aqui?

A mesma coisa se aplica para os gatilhos. Suponha que eu tenho um monte de elementos visuais differnt que todos representam o mesmo tipo de animações cor estado usando. Por exemplo. fade to verde quando fade "ativo" para "vermelho" quando "erro" etc. A única diferença entre o visual é a sua forma / árvore visual do comportamento de animação desejado é o mesmo, todos eles têm um lugar elemento em sua árvore visual que tem uma propriedade do tipo de cor. Eu acho que não é difícil imaginar como tedioso é redefinir as mesmas animações e DataTrigger conjuntos de uma e outra vez. Todo desenvolvedor odeia isso. Eu procuro desesperadamente por uma solução mais fácil que não requer nenhuma (ou pelo menos muito pouco) por trás c # código.

O que eu vim acima com até agora é a seguinte:

Defina as animações em um recurso lik este (repetir isso para todos os estados básicos que existem, como ativar, ativo, inativo, erro):

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

O uso-lo em storyboard nos gatilhos (Repita este zilhões de vezes para cada estado X cada stateviusal differnt, sempre vêm com um novo nome para o storyboard):

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

Você pode facilmente imaginar o quanto inchaço XAML Eu tenho que repetidamente copiar e colar para todos aqueles DataTriggers zilhão.

Seria legal para definir todos os Isso desencadeia uma vez e aplicá-la a diferentes visuais do estado. Como é algo como isto resolvido em WPF? Qualquer dica?

Foi útil?

Solução 2

Não parece haver nenhuma boa solução apenas de XAML para este problema geral. Acabei escrevendo minhas próprias propriedades anexadas que definem todos os comportamentos de animação para um determinado elemento. Algo parecido com isto:

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

O resto (criando a animação etc.) é feito no codebehind.

Outras dicas

Você poderia tentar algo como isto?

  • Envolver todos os seus modelos de controle atuais com um elemento raiz invisível, por exemplo, uma borda ou um StackPanel, cuja caixa delimitadora vai cobrir todo o controle.
  • Criar um modelo de controle de estilo ou para essa caixa invisível que contém todos os seus gatilhos e animações.
  • Faça com que as animações animar uma propriedade arbitrária cores na caixa invisível.
  • Nas árvores visuais para todos os seus controles diferentes, vincular todas as propriedades que você deseja animar a propriedade Color no elemento raiz invisível.

Eu percebo esta questão é um pouco morto no momento desta postagem, mas eu fiz encontrar uma solução que requer muito pouco código por trás.

Você pode fazer um UserControl com propriedades personalizadas (Scroll down para cerca de Figura 8) que contém o retângulo, bem como as animações e gatilhos estaduais. Este controle de usuário poderia definir uma propriedade pública, como o status que provocaria a mudança de cor quando alterados.

A única code-behind necessário é criar suas variáveis ??no código.

O controle de usuário pode lhes ser reutilizado inúmeras vezes sem reescrever os storyboards XAML ou gatilhos de dados.

O mais " XAML caminho" para atingir esse objetivo que eu posso pensar é criar MarkupExtension dedicado que puxaria a animação a partir dos recursos dicionário e definir as propriedades necessárias - Presumo aqueles estão limitados a um subconjunto de Storyboard.Target, Storyboard.TargetName e Storyboard.TargetProperty. Embora exija algum código-behind, é o esforço de uma só vez, além disso, MarkupExtensions são projetados para ser usado com XAML . Aqui está a versão mais simples:

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

O uso é muito simples:

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

Ser tão simples como pode ser esta solução tem suas limitações - ele não suporta nem Binding nem DynamicResource extensões para propriedades acima mencionadas. Este, porém, é possível, mas exige algum esforço extra. apoio Binding deve ser bastante simples - uma questão de uso adequado de XamlSetMarkupExtensionAttribute (Mais algum código padrão). apoio DynamicResource seria um pouco mais complicado, e além de usar XamlSetMarkupExtensionAttribute exigiria envolvendo o IServiceProvider para retornar adequada IProvideValueTarget implementação, mas ainda é possível.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top