Вопрос

Я пытаюсь запустить анимацию прогресса, когда модель ViewModel/Presentation занята.У меня есть свойство IsBusy, а ViewModel установлен как DataContext пользовательского элемента управления.Как лучше всего запустить раскадровку «progressAnimation», когда свойство IsBusy имеет значение true?Blend позволяет добавлять триггеры событий только на уровне UserControl, и я могу создавать триггеры свойств только в своих шаблонах данных.

«progressAnimation» определяется как ресурс в пользовательском элементе управления.

Я попытался добавить DataTriggers в качестве стиля в UserControl, но когда я пытаюсь запустить StoryBoard, я получаю следующую ошибку:

'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 — это имя объекта, который я пытаюсь анимировать, поэтому удаление целевого имени явно НЕ то, что я хочу.

Я надеялся решить эту проблему в XAML, используя методы привязки данных, вместо того, чтобы выявлять события и запускать/останавливать анимацию с помощью кода.

Это было полезно?

Решение

То, что вы хотите, возможно, объявив анимацию на самом ProgressWheel: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>

Код позади:

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

Модель просмотра:

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

Надеюсь это поможет!

Другие советы

Хотя ответ, предлагающий прикрепить анимацию непосредственно к анимируемому элементу, решает эту проблему в простых случаях, на самом деле это не осуществимо, когда у вас есть сложная анимация, которая должна быть ориентирована на несколько элементов.(Конечно, вы можете прикрепить анимацию к каждому элементу, но управлять ею будет довольно ужасно.)

Итак, есть альтернативный способ решить эту проблему, который позволяет вам использовать DataTrigger для запуска анимации, нацеленной на именованные элементы.

В WPF можно прикрепить триггеры в трех местах:элементы, стили и шаблоны.Однако первые два варианта здесь не работают.Первый вариант исключен, поскольку WPF не поддерживает использование DataTrigger непосредственно на элементе.(Насколько мне известно, для этого нет особенно веской причины.Насколько я помню, когда я спросил об этом людей из команды WPF много лет назад, они сказали, что хотели бы поддержать это, но не успели заставить это работать.) А стили отсутствуют, потому что, поскольку в сообщении об ошибке, о котором вы сообщили, говорится, что вы не можете использовать именованные элементы в анимации, связанной со стилем.

Так что остаются шаблоны.И для этого вы можете использовать либо шаблоны управления, либо данные.

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

Благодаря этой конструкции WPF позволяет анимации ссылаться на именованные элементы внутри шаблона.(Я оставил здесь пустыми и анимацию, и содержимое шаблона — очевидно, вы заполните их фактической анимацией и содержимым.)

Причина, по которой это работает в шаблоне, но не в стиле, заключается в том, что при применении шаблона именованные элементы, которые он определяет, всегда будут присутствовать, и поэтому анимации, определенные в области действия этого шаблона, могут безопасно ссылаться на эти элементы.Обычно это не относится к стилям, поскольку стили можно применять к множеству разных элементов, каждый из которых может иметь совершенно разные визуальные деревья.(Немного расстраивает то, что это не позволяет вам сделать это даже в тех сценариях, когда вы можете быть уверены, что необходимые элементы будут там, но, возможно, есть что-то, что очень затрудняет привязку анимации к именованным элементам справа. время.Я знаю, что в WPF довольно много оптимизаций, позволяющих эффективно повторно использовать элементы стиля, поэтому, возможно, одна из них и затрудняет поддержку.)

Я бы рекомендовал использовать RoutedEvent вместо вашего свойства IsBusy.Просто запустите события OnBusyStarted и OnBusyStopped и используйте триггер Event для соответствующих элементов.

Вы можете подписаться на событие PropertyChanged класса DataObject и запустить RoutedEvent на уровне Usercontrol.

Чтобы RoutedEvent работал, нам нужен класс, производный от DependancyObject.

Вы можете использовать Trigger.EnterAction для запуска анимации при изменении свойства.

<Trigger Property="IsBusy" Value="true">
    <Trigger.EnterActions>
        <BeginStoryboard x:Name="BeginBusy" Storyboard="{StaticResource MyStoryboard}" />
    </Trigger.EnterActions>
    <Trigger.ExitActions>
        <StopStoryboard BeginStoryboardName="BeginBusy" />
    </Trigger.ExitActions>
</Trigger>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top