WPF, анимирующий RenderTransform с помощью DataTrigger - PullRequest
0 голосов
/ 11 июня 2018

У меня проблемы с анимацией преобразования рендера объекта Canvas с использованием триггера данных.В качестве тестового примера у меня есть эллипс на холсте и две кнопки.Когда я нажимаю каждую кнопку, я хочу, чтобы положение эллипса анимировалось по координате X этой кнопки:

enter image description here

Тот факт, что я использую кнопки вэтот конкретный пример ни здесь, ни там, дело в том, что я пытаюсь вызвать эти анимации с помощью DataTrigger, привязанного к свойству в моей модели представления:

// this is the property my DataTrigger will bind to
private string _Anim;
public string Anim
{
    get { return this._Anim; }
    set
    {
        this._Anim = value;
        RaisePropertyChanged(() => this.Anim);
    }
}

// a command handler for the buttons that sets my animation property
public ICommand AnimCommand { get { return new RelayCommand<string>(OnAnim); } }
private void OnAnim(string value)
{
    this.Anim = String.Empty;   // cancel any existing animation, probably not needed...
    this.Anim = value;
}

Вот Xaml, который я используюсделать это.По сути, это просто холст, содержащий эллипс и две кнопки, а эллипс имеет 2 раскадровки, которые запускаются в ответ на задание для свойства backend различных строк (т. Е. «Left» и «Right»):

<Viewbox xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <Canvas Width="350" Height="150" Background="CornflowerBlue">
        <Ellipse x:Name="MyEllipse" Width="20" Height="20" Fill="Red">
            <Ellipse.RenderTransform>
                <TranslateTransform x:Name="myTransform" X="50" Y="50" />
            </Ellipse.RenderTransform>
            <Ellipse.Style>
                <Style TargetType="Ellipse">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Anim}" Value="Left">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="100" Duration="0:0:1" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding Anim}" Value="Right">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="200" Duration="0:0:1" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Ellipse.Style>
        </Ellipse>

        <Button x:Name="btnLeft" Content="Left" Command="{Binding AnimCommand}" CommandParameter="Left" Canvas.Left="100" Canvas.Top="100" Width="32" Height="24" />
        <Button x:Name="btnRight" Content="Right" Command="{Binding AnimCommand}" CommandParameter="Right" Canvas.Left="200" Canvas.Top="100" Width="32" Height="24" />

    </Canvas>
</Viewbox>

Когда я запускаю это, я могу нажать на первую анимацию, а затем на вторую, и поведение будет правильным оба раза.Однако нажатие на них в обратном порядке не работает ... кнопка "вправо" анимируется правильно, а кнопка "влево" ничего не делает.Это как если бы «правильная» анимация все еще работала или блокировала свойство или что-то еще, случай еще более подкреплялся тем, что ситуация изменилась, если я поменяю местами порядок объявлений раскадровки в Xaml.

Это отлично работает с EventTriggers, я вижу это только с DataTriggers.Я также пытался играть с FillBehaviours и HandoffBehaviors, но безрезультатно.Может кто-нибудь объяснить, почему это происходит и как это исправить?

1 Ответ

0 голосов
/ 11 июня 2018

Вы должны удалить Storyboard после завершения анимации.Вы можете использовать класс RemoveStoryboard для этого.Просто присвойте элементу BeginStoryboard Name, а затем добавьте элемент RemoveStoryboard к ExitActions из DataTrigger:

<Style TargetType="Ellipse">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Anim}" Value="Left">
            <DataTrigger.EnterActions>
                <BeginStoryboard Name="sb">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="100" Duration="0:0:1" />
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
            <DataTrigger.ExitActions>
                <RemoveStoryboard BeginStoryboardName="sb" />
            </DataTrigger.ExitActions>
        </DataTrigger>
        <DataTrigger Binding="{Binding Anim}" Value="Right">
            <DataTrigger.EnterActions>
                <BeginStoryboard Name="sb2">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="200" Duration="0:0:1" />
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
            <DataTrigger.ExitActions>
                <RemoveStoryboard BeginStoryboardName="sb2" />
            </DataTrigger.ExitActions>
        </DataTrigger>
    </Style.Triggers>
</Style>

Другой вариант будет установлен для FillBehaviorот Storyboard до Stop, но после завершения анимации положение Ellipse будет восстановлено.

Боюсь, не работает.Нажатие влево, затем вправо сначала работает, но затем снова щелчок влево заставляет анимацию перезапускаться с позиции 0, даже если я установил HandoffBehaviors и FillBehaviours.

Избавьтесь от DataTriggers в вашем XAML и затем напишите некоторый пользовательский код.Это работает:

private void MyEllipse_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    INotifyPropertyChanged inpc = MyEllipse.DataContext as INotifyPropertyChanged;
    if (inpc != null)
        WeakEventManager<INotifyPropertyChanged, PropertyChangedEventArgs>.AddHandler(inpc, nameof(INotifyPropertyChanged.PropertyChanged), OnChanged);
}

private PropertyInfo _pi;
private void OnChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "Anim")
    {
        _pi = _pi ?? sender.GetType().GetProperty(e.PropertyName);
        string anim = _pi?.GetValue(sender) as string;
        switch (anim)
        {
            case "Left":
                myTransform.BeginAnimation(TranslateTransform.XProperty, new DoubleAnimation() { To = 100.0, Duration = TimeSpan.FromSeconds(1) });
                break;
            case "Right":
                myTransform.BeginAnimation(TranslateTransform.XProperty, new DoubleAnimation() { To = 200.0, Duration = TimeSpan.FromSeconds(1) });
                break;
        }
    }
}

XAML:

<Viewbox xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <Canvas Width="350" Height="150" Background="CornflowerBlue">
        <Ellipse x:Name="MyEllipse" Width="20" Height="20" Fill="Red"
                         DataContextChanged="MyEllipse_DataContextChanged">
            <Ellipse.RenderTransform>
                <TranslateTransform x:Name="myTransform" X="50" Y="50" />
            </Ellipse.RenderTransform>
        </Ellipse>

        <Button x:Name="btnLeft" Content="Left" Command="{Binding AnimCommand}" CommandParameter="Left" Canvas.Left="100" Canvas.Top="100" Width="32" Height="24" />
        <Button x:Name="btnRight" Content="Right" Command="{Binding AnimCommand}" CommandParameter="Right" Canvas.Left="200" Canvas.Top="100" Width="32" Height="24" />
    </Canvas>
</Viewbox>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...