Как правильно анимировать мою Границу, чтобы двигаться вправо - PullRequest
1 голос
/ 18 октября 2019

Итак, у меня есть этот стиль, который нацелен на мой ToggleButton, и я пытаюсь изменить HorizontalAlignment границы с именем ThumbCircle слева направо, я прочитал, что изменить это свойство не так просто, как просто изменитьзначение, я собираюсь сделать что-то вроде LayoutTransform, однако это не сработало, когда я нажимаю кнопку, ничего не происходит, она не двигается.

Так что мой вопрос, как мнеполучите ThumbCircle, чтобы перейти к правой стороне Border, в которой он находится в данный момент.

<Style x:Key="MyToggleButton"
       TargetType="{x:Type ToggleButton}">

        <Style.Resources>
            <Color x:Key="Color.MyBtn.PrimaryColor">#2ecc71</Color>
            <Color x:Key="Color.MyBtn.SecondaryColor">#27ae60</Color>
        </Style.Resources>


        <Setter Property="Template">

            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Grid>
                        <Border Width="50" Height="12" Background="White" CornerRadius="6">

                        </Border>

                        <Border Width="25"
                                Background="#2ecc71"
                                CornerRadius="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}"
                                HorizontalAlignment="Left"
                                x:Name="ThumbCircle">
                            <Border.Triggers>
                                <EventTrigger RoutedEvent="PreviewMouseDown">
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <!-- Dont forget easing -->
                                            <DoubleAnimation Storyboard.TargetProperty="(LayoutTransform).(ScaleTransform.ScaleX)" Storyboard.TargetName="ThumbCircle" To="10" Duration="0:0:0.5" />
                                        </Storyboard>
                                    </BeginStoryboard>

                                </EventTrigger>
                            </Border.Triggers>
                        </Border>
                    </Grid>

                </ControlTemplate>
            </Setter.Value>

        </Setter>
    </Style>

1 Ответ

0 голосов
/ 18 октября 2019

Почему ничего не появляется

Причина, по которой вы не видите ничего движущегося, заключается в том, что вы нацеливаетесь на свойство (LayoutTransform).(ScaleTransform.ScaleX) вашего ThumbCircle, но это не такдля него не установлено значение LayoutTransform.

Если вы добавите это к своему ThumbCircle Border:

    <Border.LayoutTransform>
        <ScaleTransform/>
    </Border.LayoutTransform>

Тогда вы увидите, что что-то происходит. Но вы увидите масштабирование, а не перевод! Вам нужно перевести с одной стороны на другую.

Интуитивное исправление не работает ...

Самый простой способ - это сначала заменитьLayoutTransform с RenderTransform и ScaleTransform с TranslateTransform следующим образом:

    <Border.RenderTransform>
        <TranslateTransform x:Name="MyTranslate"/>
    </Border.LayoutTransform>

Затем дайте имя вашему Grid, например:

<Grid x:Name="MyGrid">
    ...
</Grid>

А затем анимируем X свойство TranslateTransform из 0 в Grid.ActualWidth следующим образом:

<!-- This won't run -->
<DoubleAnimation Storyboard.TargetProperty="X" Storyboard.TargetName="MyTranslate" To="{Binding ElementName=MyGrid, Path=ActualWidth}" Duration="0:0:0.5" />

Но этого достичь невозможно, так как невозможноустановите Binding для любого свойства Animation при использовании подобным образом, потому что WPF делает некоторые оптимизации, которые предотвращают это , как описано здесь .

XAML-интенсивный способчтобы сделать это

Таким образом, способ сделать это состоит в том, чтобы определить прокси-элементы, свойства которых мы анимируем от 0 до 1, и мы делаем умножение с MyGrid.ActualWidth в другом месте.

Таким образом, весь ваш стиль XAML становится:

<Style x:Key="MyToggleButton" TargetType="{x:Type ToggleButton}">

    <Style.Resources>
        <Color x:Key="Color.MyBtn.PrimaryColor">#2ecc71</Color>
        <Color x:Key="Color.MyBtn.SecondaryColor">#27ae60</Color>
        <!-- Aded some converters here -->
        <views:MultiMultiplierConverter x:Key="MultiMultiplierConverter"></views:MultiMultiplierConverter>
        <views:OppositeConverter x:Key="OppositeConverter"></views:OppositeConverter>
    </Style.Resources>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Grid x:Name="ContainerGrid">
                    <Border Width="50" Height="12" Background="Red" CornerRadius="6">

                    </Border>
                    <Border Width="25"
                        Background="#2ecc71"
                        CornerRadius="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}"
                        HorizontalAlignment="Left"
                        x:Name="ThumbCircle">
                        <Border.Resources>
                            <!-- Proxy object whose X property gets animated from 0 to 1. -->
                            <!-- Could be any DependencyObject with a property of type double. -->
                            <TranslateTransform x:Key="unusedKey" x:Name="Proxy"></TranslateTransform>
                        </Border.Resources>
                        <Border.RenderTransform>
                            <TransformGroup>
                                <!-- Main translation to move from one side of the grid to the other -->
                                <TranslateTransform>
                                    <TranslateTransform.X>
                                        <MultiBinding Converter="{StaticResource MultiMultiplierConverter}" ConverterParameter="2">
                                            <Binding ElementName="Proxy" Path="X"></Binding>
                                            <Binding ElementName="ContainerGrid" Path="ActualWidth"></Binding>
                                        </MultiBinding>
                                    </TranslateTransform.X>
                                </TranslateTransform>

                                <!-- Secondary translation to adjust to the actual width of the object to translate -->
                                <TranslateTransform>
                                    <TranslateTransform.X>
                                        <MultiBinding Converter="{StaticResource MultiMultiplierConverter}" ConverterParameter="2">
                                            <Binding ElementName="Proxy" Path="X"></Binding>
                                            <Binding ElementName="ThumbCircle" Path="ActualWidth" Converter="{StaticResource OppositeConverter}"></Binding>
                                        </MultiBinding>
                                    </TranslateTransform.X>
                                </TranslateTransform>
                            </TransformGroup>
                        </Border.RenderTransform>
                        <Border.Triggers>
                            <EventTrigger RoutedEvent="MouseDown">
                                <BeginStoryboard>
                                    <Storyboard>
                                        <!-- Dont forget easing -->
                                        <DoubleAnimation Storyboard.TargetProperty="X" Storyboard.TargetName="Proxy" To="1" Duration="0:0:0.5" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Border.Triggers>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

И вам потребуется определить два IValueConverter для выполнения некоторого базового арОперации на вашем Binding s:

Один для умножения всех предоставленных значений в MultiBinding:

/// <summary>
/// Defines a converter which multiplies all provided values.
/// The given parameter indicates number of arguments to multiply.
/// </summary>
public class MultiMultiplierConverter : IMultiValueConverter {
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        double result = 1;
        int count = int.Parse((string)parameter);
        for (int i = 0; i < count; i++) {
            result *= (double)values[i];
        }
        return result;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
        throw new NotSupportedException("Cannot convert back");
    }
}

И один для умножения ввода на -1:

/// <summary>
/// Defines a converter which multiplies the provided value by -1
/// </summary>
public class OppositeConverter : IValueConverter {
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        return (dynamic)value * -1;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        throw new NotSupportedException("Cannot convert back");
    }
}

Это не элегантный способ, но он работает!

Как реализовать анимацию вперед-назад?

Пока нам удалось оживить большой палецвправо, по щелчку. Но это еще не все, не так ли?

То, что мы шаблонируем, - это ToggleButton: при каждом щелчке должна запускаться анимация на противоположной стороне. Точнее, всякий раз, когда свойство IsChecked получает True, мы должны запускать анимацию справа, а всякий раз, когда свойство IsChecked получает False, мы должны запускать анимацию слева.

Это возможно путем добавления некоторых Trigger объектов в коллекцию ControlTemplate.Triggers. Trigger должен быть подключен к свойству IsChecked (которое мы не можем контролировать) и прослушать его изменения. Мы можем указать EnterAction, который является нашей анимацией справа, и ExitAction, который является нашей анимацией слева.

Полный Style становится:

<Style x:Key="MyToggleButton" TargetType="{x:Type ToggleButton}">

    <Style.Resources>
        <Color x:Key="Color.MyBtn.PrimaryColor">#2ecc71</Color>
        <Color x:Key="Color.MyBtn.SecondaryColor">#27ae60</Color>
        <!-- Aded some converters here -->
        <views:MultiMultiplierConverter x:Key="MultiMultiplierConverter"></views:MultiMultiplierConverter>
        <views:OppositeConverter x:Key="OppositeConverter"></views:OppositeConverter>
    </Style.Resources>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <!-- Animation to the right -->
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <!-- Dont forget easing -->
                                    <DoubleAnimation Storyboard.TargetProperty="X" Storyboard.TargetName="Proxy" To="1" Duration="0:0:0.5" />
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>

                        <!-- Animation to the left -->
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <!-- Dont forget easing -->
                                    <DoubleAnimation Storyboard.TargetProperty="X" Storyboard.TargetName="Proxy" To="0" Duration="0:0:0.5" />
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
                <Grid x:Name="ContainerGrid">
                    <Border Width="50" Height="12" Background="Red" CornerRadius="6">

                    </Border>
                    <Border Width="25"
                Background="#2ecc71"
                CornerRadius="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}"
                HorizontalAlignment="Left"
                x:Name="ThumbCircle">
                        <Border.Resources>
                            <!-- Proxy object whose X property gets animated from 0 to 1. -->
                            <!-- Could be any DependencyObject with a property of type double. -->
                            <TranslateTransform x:Key="unusedKey" x:Name="Proxy"></TranslateTransform>
                        </Border.Resources>
                        <Border.RenderTransform>
                            <TransformGroup>
                                <!-- Main translation to move from one side of the grid to the other -->
                                <TranslateTransform>
                                    <TranslateTransform.X>
                                        <MultiBinding Converter="{StaticResource MultiMultiplierConverter}" ConverterParameter="2">
                                            <Binding ElementName="Proxy" Path="X"></Binding>
                                            <Binding ElementName="ContainerGrid" Path="ActualWidth"></Binding>
                                        </MultiBinding>
                                    </TranslateTransform.X>
                                </TranslateTransform>

                                <!-- Secondary translation to adjust to the actual width of the object to translate -->
                                <TranslateTransform>
                                    <TranslateTransform.X>
                                        <MultiBinding Converter="{StaticResource MultiMultiplierConverter}" ConverterParameter="2">
                                            <Binding ElementName="Proxy" Path="X"></Binding>
                                            <Binding ElementName="ThumbCircle" Path="ActualWidth" Converter="{StaticResource OppositeConverter}"></Binding>
                                        </MultiBinding>
                                    </TranslateTransform.X>
                                </TranslateTransform>
                            </TransformGroup>
                        </Border.RenderTransform>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Обратите внимание, что Trigger допускается только внутри коллекции ControlTemplate.Triggers, такую ​​оригинальную коллекцию Border.Triggers поместить в Trigger невозможно, вы можете прочитать подробнее об этом здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...