Domanda

Sto cercando di attivare un'animazione di avanzamento ogni volta che ViewModel/Presentation Model è occupato.Ho una proprietà IsBusy e ViewModel è impostato come DataContext di UserControl.Qual è il modo migliore per attivare uno storyboard "progressAnimation" quando la proprietà IsBusy è vera?Blend consente solo a Med di aggiungere trigger di eventi a livello di controllo utente e posso creare solo trigger di proprietà nei miei modelli di dati.

"progressAnimation" è definita come risorsa nel controllo utente.

Ho provato ad aggiungere DataTriggers come stile su UserControl, ma quando provo ad avviare StoryBoard ottengo il seguente errore:

'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 è il nome dell'oggetto che sto cercando di animare, quindi rimuovere il nome di destinazione NON è ovviamente ciò che voglio.

Speravo di risolvere questo problema in XAML utilizzando tecniche di associazione dati, invece di dover esporre eventi e avviare/interrompere l'animazione tramite codice.

È stato utile?

Soluzione

Ciò che desideri è possibile dichiarando l'animazione sulla progressWheel stessa:L'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>

Codice dietro:

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

Il modello di visualizzazione:

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

Spero che questo ti aiuti!

Altri suggerimenti

Sebbene la risposta che propone di allegare l'animazione direttamente all'elemento da animare risolva questo problema in casi semplici, ciò non è realmente praticabile quando si ha un'animazione complessa che deve avere come target più elementi.(Ovviamente puoi allegare un'animazione a ciascun elemento, ma diventa piuttosto orribile da gestire.)

Quindi esiste un modo alternativo per risolvere questo problema che ti consente di utilizzare a DataTrigger per eseguire un'animazione che prende di mira gli elementi con nome.

Sono disponibili tre posizioni in cui è possibile allegare i trigger in WPF:elementi, stili e modelli.Tuttavia, le prime due opzioni non funzionano qui.Il primo è escluso perché WPF non supporta l'uso di a DataTrigger direttamente su un elemento.(Non c'è una ragione particolarmente valida per questo, per quanto ne so.Per quanto mi ricordo, quando ho chiesto informazioni a persone del team WPF molti anni fa, hanno detto che gli sarebbe piaciuto supportarlo ma non hanno avuto il tempo di farlo funzionare.) E gli stili sono fuori moda perché, come il messaggio di errore che hai segnalato dice che non puoi scegliere come target elementi con nome in un'animazione associata a uno stile.

Quindi rimangono i modelli.E puoi utilizzare modelli di controllo o di dati per questo.

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

Con questa costruzione, WPF è felice di lasciare che l'animazione faccia riferimento a elementi con nome all'interno del modello.(Qui ho lasciato vuoti sia l'animazione che il contenuto del modello: ovviamente lo completeresti con l'animazione e il contenuto effettivi.)

Il motivo per cui funziona in un modello ma non in uno stile è che quando applichi un modello, gli elementi con nome che definisce saranno sempre presenti e quindi è sicuro che le animazioni definite nell'ambito di quel modello facciano riferimento a tali elementi.Questo non è generalmente il caso degli stili, poiché gli stili possono essere applicati a più elementi diversi, ognuno dei quali può avere alberi visivi molto diversi.(È un po' frustrante che ti impedisca di farlo anche in scenari in cui puoi essere certo che gli elementi richiesti saranno presenti, ma forse c'è qualcosa che rende molto difficile vincolare l'animazione agli elementi con nome a destra tempo.So che ci sono molte ottimizzazioni in WPF per consentire il riutilizzo efficiente degli elementi di uno stile, quindi forse una di queste è ciò che rende difficile il supporto.)

Consiglierei di utilizzare RoutedEvent invece della proprietà IsBusy.Basta attivare gli eventi OnBusyStarted e OnBusyStopped e utilizzare il trigger di evento sugli elementi appropriati.

È possibile sottoscrivere l'evento PropertyChanged della classe DataObject e attivare un RoutedEvent dal livello Usercontrol.

Affinché RoutedEvent funzioni, è necessario che la classe derivi da DependancyObject

È possibile utilizzare Trigger.EnterAction per avviare un'animazione quando viene modificata una proprietà.

<Trigger Property="IsBusy" Value="true">
    <Trigger.EnterActions>
        <BeginStoryboard x:Name="BeginBusy" Storyboard="{StaticResource MyStoryboard}" />
    </Trigger.EnterActions>
    <Trigger.ExitActions>
        <StopStoryboard BeginStoryboardName="BeginBusy" />
    </Trigger.ExitActions>
</Trigger>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top