Pregunta

Estoy intentando activar una animación de progreso cada vez que el modelo de vista/modelo de presentación está ocupado.Tengo una propiedad IsBusy y ViewModel está configurado como DataContext de UserControl.¿Cuál es la mejor manera de activar un guión gráfico de "progresoAnimación" cuando la propiedad IsBusy es verdadera?Blend solo me permite agregar activadores de eventos en un nivel de control de usuario, y solo puedo crear activadores de propiedades en mis plantillas de datos.

El "progressAnimation" se define como un recurso en el control del usuario.

Intenté agregar DataTriggers como estilo en UserControl, pero cuando intento iniciar StoryBoard aparece el siguiente error:

'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 es el nombre del objeto que estoy intentando animar, por lo que eliminar el nombre del objetivo obviamente NO es lo que quiero.

Esperaba resolver esto en XAML usando técnicas de enlace de datos, en lugar de tener que exponer eventos e iniciar/detener la animación a través del código.

¿Fue útil?

Solución

Lo que quieres es posible declarando la animación en la propia rueda de progreso:El 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>

Código detrás:

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

El modelo de vista:

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

¡Espero que esto ayude!

Otros consejos

Aunque la respuesta que propone adjuntar la animación directamente al elemento a animar resuelve este problema en casos simples, esto no es realmente viable cuando tienes una animación compleja que necesita apuntar a múltiples elementos.(Puedes adjuntar una animación a cada elemento, por supuesto, pero resulta bastante horrible de manejar).

Entonces hay una manera alternativa de resolver esto que te permite usar un DataTrigger para ejecutar una animación dirigida a elementos con nombre.

Hay tres lugares donde puede adjuntar desencadenadores en WPF:elementos, estilos y plantillas.Sin embargo, las dos primeras opciones no funcionan aquí.Lo primero se descarta porque WPF no admite el uso de un DataTrigger directamente sobre un elemento.(Hasta donde yo sé, no hay ninguna razón particularmente buena para esto.Hasta donde recuerdo, cuando le pregunté a la gente del equipo de WPF sobre esto hace muchos años, dijeron que les hubiera gustado admitirlo pero que no tenían tiempo para hacerlo funcionar). Y los estilos están fuera porque, como el mensaje de error que ha informado dice que no puede apuntar a elementos con nombre en una animación asociada con un estilo.

Entonces eso deja plantillas.Y puede utilizar plantillas de control o de datos para esto.

<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 esta construcción, WPF se complace en permitir que la animación haga referencia a elementos con nombre dentro de la plantilla.(Dejé vacíos tanto la animación como el contenido de la plantilla; obviamente, debes completarlo con tu animación y contenido reales).

La razón por la que esto funciona en una plantilla pero no en un estilo es que cuando aplica una plantilla, los elementos con nombre que define siempre estarán presentes, por lo que es seguro que las animaciones definidas dentro del alcance de esa plantilla hagan referencia a esos elementos.Generalmente, este no es el caso con un estilo, porque los estilos se pueden aplicar a múltiples elementos diferentes, cada uno de los cuales puede tener árboles visuales bastante diferentes.(Es un poco frustrante que te impida hacer esto incluso en escenarios en los que puedes estar seguro de que los elementos requeridos estarán allí, pero tal vez haya algo que hace que sea muy difícil que la animación se vincule a los elementos nombrados a la derecha. tiempo.Sé que hay muchas optimizaciones en WPF para permitir que los elementos de un estilo se reutilicen de manera eficiente, por lo que quizás una de ellas sea la que hace que esto sea difícil de admitir).

Recomendaría utilizar RoutedEvent en lugar de su propiedad IsBusy.Simplemente active los eventos OnBusyStarted y OnBusyStopped y use el activador de eventos en los elementos apropiados.

Puede suscribirse al evento PropertyChanged de la clase DataObject y activar un RoutedEvent desde el nivel de control de usuario.

Para que RoutedEvent funcione necesitamos tener la clase derivada de DependancyObject

Puede utilizar Trigger.EnterAction para iniciar una animación cuando se cambia una propiedad.

<Trigger Property="IsBusy" Value="true">
    <Trigger.EnterActions>
        <BeginStoryboard x:Name="BeginBusy" Storyboard="{StaticResource MyStoryboard}" />
    </Trigger.EnterActions>
    <Trigger.ExitActions>
        <StopStoryboard BeginStoryboardName="BeginBusy" />
    </Trigger.ExitActions>
</Trigger>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top