Domanda

Ho creato una proprietà di dipendenza collegata per Storyboard, con l'intenzione di consentirmi di chiamare un metodo sul mio ViewModel quando viene generato un evento Storyboard completato:

public static class StoryboardExtensions
{
    public static ICommand GetCompletedCommand(DependencyObject target)
    {
        return (ICommand)target.GetValue(CompletedCommandProperty);
    }

    public static void SetCompletedCommand(DependencyObject target, ICommand value)
    {
        target.SetValue(CompletedCommandProperty, value);
    }

    public static readonly DependencyProperty CompletedCommandProperty =
        DependencyProperty.RegisterAttached(
            "CompletedCommand",
            typeof(ICommand),
            typeof(StoryboardExtensions),
            new FrameworkPropertyMetadata(null, OnCompletedCommandChanged));

    static void OnCompletedCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        Storyboard storyboard = target as Storyboard;
        if (storyboard == null) throw new InvalidOperationException("This behavior can be attached to Storyboard item only.");
        storyboard.Completed += new EventHandler(OnStoryboardCompleted);
    }

    static void OnStoryboardCompleted(object sender, EventArgs e)
    {                        
        Storyboard item = ... // snip
        ICommand command = GetCompletedCommand(item);
        command.Execute(null);
    }
}

quindi provo ad usarlo in XAML, con una sintassi Binding:

<Grid>
    <Grid.Resources>
        <Storyboard x:Key="myStoryboard" my:StoryboardExtensions.CompletedCommand="{Binding AnimationCompleted}">
            <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:5" />
        </Storyboard>

        <Style x:Key="myStyle" TargetType="{x:Type Label}">
            <Style.Triggers>
                <DataTrigger 
                 Binding="{Binding Path=QuestionState}" Value="Correct">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard Storyboard="{StaticResource myStoryboard}" />
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>

    </Grid.Resources>
    <Label x:Name="labelHello" Grid.Row="0" Style="{StaticResource myStyle}">Hello</Label>
</Grid>

Ciò fallisce con la seguente eccezione:

  

Si è verificata System.Windows.Markup.XamlParseException   Messaggio = " Impossibile convertire il valore nell'attributo 'Stile' in oggetto di tipo 'System.Windows.Style'. Impossibile congelare l'albero della sequenza temporale di Storyboard per l'utilizzo tra i thread. Errore nell'oggetto 'labelHello' nel file di markup 'TestWpfApp; component / window1.xaml'

Esiste un modo per far funzionare la sintassi Binding con una proprietà ICommand collegata per uno Storyboard?

È stato utile?

Soluzione 3

Per aggirare questo problema, ho creato un gruppo di Proprietà associate, chiamate Storyboard Helpers ( codice sorgente qui ). Ho smesso di provare a collegarli allo Storyboard stesso e ora mi collego a qualsiasi elemento (arbitrario) del framework per chiamare un ICommand sul mio ViewModel quando lo storyboard è completo, oltre a essere associato a un evento particolare sul mio ViewModel per lanciare lo Storyboard . Una terza proprietà allegata specifica lo Storyboard di cui ci stiamo occupando:

<FrameworkElement
   my:StoryboardHelpers.Storyboard="{StaticResource rightAnswerAnimation}"
   my:StoryboardHelpers.Completed="{Binding CompletedCommand}"
   my:StoryboardHelpers.BeginEvent="{Binding StartCorrectAnswer}" />

Altri suggerimenti

Questo è qualcosa di design. Se si dispone di un oggetto congelabile inserito in uno stile, lo stile verrà congelato per consentire l'accesso cross-thread. Ma l'associazione è essenzialmente un'espressione che significa che non può essere congelata poiché l'associazione dati è a thread singolo.

Se è necessario eseguire questa operazione, posizionare il trigger all'esterno dello stile in un elemento framework anziché in uno stile. Puoi farlo nella sezione Grid.Triggers. Questo fa schifo un po 'perché il tuo stile non è più completo e devi duplicare i grilletti ma è un "design di progettazione" funzione in WPF.

La risposta completa sui forum sociali di MSDN è qui .

Potresti creare una nuova classe derivata da Freezable per lanciare uno storyboard come shim. Associare una proprietà su quell'oggetto shim al nome dello storyboard. In questo modo, non dovrai duplicare i trigger o memorizzarli al di fuori dello stile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top