Question

J'essaie de déclencher une animation de progression chaque fois que le modèle ViewModel / Presentation est occupé. J'ai une propriété IsBusy et le ViewModel est défini en tant que DataContext de UserControl. Quel est le meilleur moyen de déclencher un " progrèsAnimation " story board lorsque la propriété IsBusy est vraie? Blend ne laisse que med ajouter des déclencheurs d'événements au niveau de UserControl, et je ne peux créer que des déclencheurs de propriétés dans mes modèles de données.

Le " progressAnimation " est défini comme une ressource dans le contrôle utilisateur.

J'ai essayé d'ajouter les DataTriggers en tant que style sur UserControl, mais lorsque j'essaie de démarrer StoryBoard, l'erreur suivante apparaît:

'System.Windows.Style' value cannot be assigned to property 'Style' 
of object'Colorful.Control.SearchPanel'. A Storyboard tree in a Style 
cannot specify a TargetName. Remove TargetName 'progressWheel'.

ProgressWheel est le nom de l'objet que j'essaie d'animer. Supprimer le nom de la cible n'est donc PAS ce que je veux.

J'espérais résoudre ce problème en XAML à l'aide de techniques de liaison de données, au lieu de devoir exposer des événements et de démarrer / arrêter l'animation via le code.

Était-ce utile?

La solution

Ce que vous voulez est possible en déclarant l'animation sur le progressWheel lui-même: Le XAML:

<UserControl x:Class="TriggerSpike.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<UserControl.Resources>
    <DoubleAnimation x:Key="SearchAnimation" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:4"/>
    <DoubleAnimation x:Key="StopSearchAnimation" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:4"/>
</UserControl.Resources>
<StackPanel>
    <TextBlock Name="progressWheel" TextAlignment="Center" Opacity="0">
        <TextBlock.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsBusy}" Value="True">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <StaticResource ResourceKey="SearchAnimation"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                        <DataTrigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                   <StaticResource ResourceKey="StopSearchAnimation"/> 
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.ExitActions>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
        Searching
    </TextBlock>
    <Label Content="Here your search query"/>
    <TextBox Text="{Binding SearchClause}"/>
    <Button Click="Button_Click">Search!</Button>
    <TextBlock Text="{Binding Result}"/>
</StackPanel>

Code derrière:

    using System.Windows;
using System.Windows.Controls;

namespace TriggerSpike
{
    public partial class UserControl1 : UserControl
    {
        private MyViewModel myModel;

        public UserControl1()
        {
            myModel=new MyViewModel();
            DataContext = myModel;
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            myModel.Search(myModel.SearchClause);
        }
    }
}

Le modèle de vue:

    using System.ComponentModel;
using System.Threading;
using System.Windows;

namespace TriggerSpike
{
    class MyViewModel:DependencyObject
    {

        public string SearchClause{ get;set;}

        public bool IsBusy
        {
            get { return (bool)GetValue(IsBusyProperty); }
            set { SetValue(IsBusyProperty, value); }
        }

        public static readonly DependencyProperty IsBusyProperty =
            DependencyProperty.Register("IsBusy", typeof(bool), typeof(MyViewModel), new UIPropertyMetadata(false));



        public string Result
        {
            get { return (string)GetValue(ResultProperty); }
            set { SetValue(ResultProperty, value); }
        }

        public static readonly DependencyProperty ResultProperty =
            DependencyProperty.Register("Result", typeof(string), typeof(MyViewModel), new UIPropertyMetadata(string.Empty));

        public void Search(string search_clause)
        {
            Result = string.Empty;
            SearchClause = search_clause;
            var worker = new BackgroundWorker();
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;
            IsBusy = true;
            worker.RunWorkerAsync();
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            IsBusy=false;
            Result = "Sorry, no results found for: " + SearchClause;
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(5000);
        }
    }
}

J'espère que ça aide!

Autres conseils

Bien que la réponse qui propose d'associer l'animation directement à l'élément à animer résolve ce problème dans des cas simples, cela ne fonctionne pas vraiment si vous avez une animation complexe qui doit cibler plusieurs éléments. (Vous pouvez bien sûr attacher une animation à chaque élément, mais sa gestion est assez horrible.)

Il existe donc un autre moyen de résoudre ce problème, qui vous permet d’utiliser un DataTrigger pour exécuter une animation ciblant des éléments nommés.

WPF peut associer des déclencheurs à trois endroits: éléments, styles et modèles. Cependant, les deux premières options ne fonctionnent pas ici. Le premier est exclu car WPF ne prend pas en charge l'utilisation d'un DataTrigger directement sur un élément. (Pour autant que je sache, il n'y a pas de bonne raison à cela. Si je me souviens bien, lorsque j'ai interrogé les membres de l'équipe WPF à ce sujet il y a plusieurs années, ils ont dit qu'ils auraient aimé pouvoir le soutenir, mais ne l'ont pas fait. avez le temps de le faire fonctionner.) Et les styles sont supprimés car, comme le message d'erreur que vous avez signalé, vous ne pouvez pas cibler les éléments nommés dans une animation associée à un style.

Cela laisse donc des modèles. Et vous pouvez utiliser des modèles de contrôle ou de données pour cela.

<ContentControl>
    <ContentControl.Template>
        <ControlTemplate TargetType="ContentControl">
            <ControlTemplate.Resources>
                <Storyboard x:Key="myAnimation">

                    <!-- Your animation goes here... -->

                </Storyboard>
            </ControlTemplate.Resources>
            <ControlTemplate.Triggers>
                <DataTrigger
                    Binding="{Binding MyProperty}"
                    Value="DesiredValue">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard
                            x:Name="beginAnimation"
                            Storyboard="{StaticResource myAnimation}" />
                    </DataTrigger.EnterActions>
                    <DataTrigger.ExitActions>
                        <StopStoryboard
                            BeginStoryboardName="beginAnimation" />
                    </DataTrigger.ExitActions>
                </DataTrigger>
            </ControlTemplate.Triggers>

            <!-- Content to be animated goes here -->

        </ControlTemplate>
    </ContentControl.Template>
<ContentControl>

Avec cette construction, WPF est heureux de laisser l'animation se référer à des éléments nommés à l'intérieur du modèle. (J'ai laissé l'animation et le contenu du modèle vides ici. Évidemment, vous y ajouterez votre contenu et votre animation réelle.)

La raison pour laquelle cela fonctionne dans un modèle, mais pas dans un style, c'est que lorsque vous appliquez un modèle, les éléments nommés qu'il définit sont toujours présents. Par conséquent, les animations définies dans la portée de ce modèle font référence à ces éléments. Ce n'est généralement pas le cas avec un style, car les styles peuvent être appliqués à plusieurs éléments différents, chacun pouvant avoir des arbres visuels très différents. (C'est un peu frustrant que cela vous empêche de le faire même dans des scénarios où vous pouvez être certain que les éléments requis seront là, mais il y a peut-être quelque chose qui rend très difficile pour l'animation d'être liée aux éléments nommés à droite. Je sais qu’il existe de nombreuses optimisations dans WPF qui permettent de réutiliser efficacement des éléments d’un style, c’est peut-être l’une des raisons pour lesquelles cela est difficile à prendre en charge.)

Je recommanderais d'utiliser RoutedEvent à la place de votre propriété IsBusy. Il suffit de déclencher les événements OnBusyStarted et OnBusyStopped et d’utiliser le déclencheur d’événement sur les éléments appropriés.

Vous pouvez vous abonner à l'événement PropertyChanged de la classe DataObject et déclencher un incendie RoutedEvent à partir du niveau Usercontrol.

Pour que RoutedEvent fonctionne, la classe doit être dérivée de DependancyObject

Vous pouvez utiliser Trigger.EnterAction pour lancer une animation lorsqu'une propriété est modifiée.

<Trigger Property="IsBusy" Value="true">
    <Trigger.EnterActions>
        <BeginStoryboard x:Name="BeginBusy" Storyboard="{StaticResource MyStoryboard}" />
    </Trigger.EnterActions>
    <Trigger.ExitActions>
        <StopStoryboard BeginStoryboardName="BeginBusy" />
    </Trigger.ExitActions>
</Trigger>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top