Анимировать кнопку из одной точки в другую - PullRequest
4 голосов
/ 10 мая 2019

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

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

Я могу немного приблизиться, используя смесь и рисуя линию между двумя кнопками и превращая ее в траекторию движения. Однако, используя этот метод, очень трудно получить точное позиционирование. Часть меня чувствует, что вытягивание пути немного усложняет ситуацию. Все, что я хочу, чтобы кнопка А оживила, чтобы стать кнопкой Б.

В настоящее время меня интересует только расположение кнопки. Две кнопки имеют разные размеры, но анимация ширины и высоты кнопки более прямолинейна:)

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


<Window x:Class="test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:test"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Margin" Value="5" />
            <Setter Property="Background" Value="Red" />
            <Setter Property="Foreground" Value="White" />
        </Style>
    </Window.Resources>

    <Grid>
        <Expander Margin="5" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" IsExpanded="True">
            <Expander.Header>
                <DockPanel Margin="5">
                    <TextBlock Margin="5" VerticalAlignment="Center">Hello World</TextBlock>
                    <Button Padding="5">Button</Button>
                </DockPanel>

            </Expander.Header>
            <Expander.Content>
                <StackPanel Orientation="Horizontal">
                    <Canvas Margin="5" Width="300" Background="Black" />

                    <StackPanel>
                        <StackPanel.Resources>
                            <Style TargetType="{x:Type RadioButton}">
                                <Setter Property="Margin" Value="5" />
                            </Style>
                        </StackPanel.Resources>
                        <RadioButton IsChecked="True">Option 1</RadioButton>
                        <RadioButton>Option 2</RadioButton>
                        <RadioButton>Option 3</RadioButton>
                        <RadioButton>Option 4</RadioButton>

                        <Button Width="100" Height="50">Button</Button>
                    </StackPanel>
                </StackPanel>
            </Expander.Content>
        </Expander>
    </Grid>
</Window>

Любая помощь очень ценится. Я пытался найти некоторые ресурсы, чтобы помочь, но все, что я смог найти, - это перемещать элемент по пути, а не к определенной точке, если я не пропускаю что-то очевидное.

Спасибо

Ответы [ 2 ]

6 голосов
/ 10 мая 2019

Это не тривиальная вещь.

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

Создание шаблона вашего расширителя не должно быть слишком сложным, просто наведите курсор на его XAML, перейдите в свойствавыберите вкладку «Шаблон» и щелкните маленький квадрат справа, чтобы сгенерировать весь код для нового шаблона ControlTemplate.Я сделал это для вас в приведенном ниже коде, но, вероятно, все будет неправильно, поэтому вы захотите сделать это сами.

Следующим шагом будет создание двух прямоугольников, одного в заголовке расширителей иодин в своей области содержания.Установите размер этих кнопок так, как вы хотите, чтобы ваша кнопка была, но установите заливку прозрачной;это только заполнители, помогающие с макетом, и вы будете использовать их только для выравнивания элемента управления кнопки в дальнейшем.

Теперь вам нужно вернуться к ControlTemplate и обернуть весь контент (т.е.граница верхнего уровня) в сетке.Вы также захотите поместить свою фактическую кнопку как дочернюю для этой сетки.

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

Соберите все это вместе, и вы получите следующее:

 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
 xmlns:behaviors="clr-namespace:WpfApp1.Behaviors"


<UserControl.Resources>
    <ControlTemplate x:Key="ExpanderControlTemplate1" TargetType="{x:Type Expander}">
        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="3" SnapsToDevicePixels="True">
            <Grid x:Name="Grid">
                <DockPanel>
                    <ToggleButton x:Name="HeaderSite" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" DockPanel.Dock="Top" Foreground="{TemplateBinding Foreground}" FontWeight="{TemplateBinding FontWeight}" FontStyle="{TemplateBinding FontStyle}" FontStretch="{TemplateBinding FontStretch}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" MinWidth="0" MinHeight="0" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
                        <ToggleButton.FocusVisualStyle>
                            <Style>
                                <Setter Property="Control.Template">
                                    <Setter.Value>
                                        <ControlTemplate>
                                            <Border>
                                                <Rectangle Margin="0" SnapsToDevicePixels="True" Stroke="Black" StrokeThickness="1" StrokeDashArray="1 2"/>
                                            </Border>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </ToggleButton.FocusVisualStyle>
                        <ToggleButton.Style>
                            <Style TargetType="{x:Type ToggleButton}">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type ToggleButton}">
                                            <Border Padding="{TemplateBinding Padding}">
                                                <Grid Background="Transparent" SnapsToDevicePixels="False">
                                                    <Grid.ColumnDefinitions>
                                                        <ColumnDefinition Width="19"/>
                                                        <ColumnDefinition Width="*"/>
                                                    </Grid.ColumnDefinitions>
                                                    <Ellipse x:Name="circle" Fill="White" HorizontalAlignment="Center" Height="19" Stroke="#FF333333" VerticalAlignment="Center" Width="19"/>
                                                    <Path x:Name="arrow" Data="M1,1.5L4.5,5 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="False" Stroke="#FF333333" StrokeThickness="2" VerticalAlignment="Center"/>
                                                    <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="1" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="Left" Margin="4,0,0,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Center"/>
                                                </Grid>
                                            </Border>
                                            <ControlTemplate.Triggers>
                                                <Trigger Property="IsChecked" Value="True">
                                                    <Setter Property="Data" TargetName="arrow" Value="M1,4.5L4.5,1 8,4.5"/>
                                                </Trigger>
                                                <Trigger Property="IsMouseOver" Value="True">
                                                    <Setter Property="Stroke" TargetName="circle" Value="#FF5593FF"/>
                                                    <Setter Property="Fill" TargetName="circle" Value="#FFF3F9FF"/>
                                                    <Setter Property="Stroke" TargetName="arrow" Value="Black"/>
                                                </Trigger>
                                                <Trigger Property="IsPressed" Value="True">
                                                    <Setter Property="Stroke" TargetName="circle" Value="#FF3C77DD"/>
                                                    <Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
                                                    <Setter Property="Fill" TargetName="circle" Value="#FFD9ECFF"/>
                                                    <Setter Property="Stroke" TargetName="arrow" Value="Black"/>
                                                </Trigger>
                                                <Trigger Property="IsEnabled" Value="False">
                                                    <Setter Property="Stroke" TargetName="circle" Value="#FFBCBCBC"/>
                                                    <Setter Property="Fill" TargetName="circle" Value="#FFE6E6E6"/>
                                                    <Setter Property="Stroke" TargetName="arrow" Value="#FF707070"/>
                                                </Trigger>
                                            </ControlTemplate.Triggers>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </ToggleButton.Style>
                    </ToggleButton>
                    <ContentPresenter x:Name="ExpandSite" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" DockPanel.Dock="Bottom" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Visibility="Collapsed" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </DockPanel>
                <Button Width="100" Height="50" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top">
                    <i:Interaction.Behaviors>
                        <behaviors:AnimatedMarginBehavior ElementA="{Binding ElementName=ElementA}" ElementB="{Binding ElementName=ElementB}" Grid="{Binding ElementName=Grid}" Parent="{Binding RelativeSource={RelativeSource AncestorType=Expander}}" />
                    </i:Interaction.Behaviors>
                </Button>
            </Grid>
        </Border>
        <ControlTemplate.Triggers>
            <Trigger Property="IsExpanded" Value="True">
                <Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
            </Trigger>
            <Trigger Property="ExpandDirection" Value="Right">
                <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Right"/>
                <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Left"/>
                <Setter Property="Style" TargetName="HeaderSite">
                    <Setter.Value>
                        <Style TargetType="{x:Type ToggleButton}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                                        <Border Padding="{TemplateBinding Padding}">
                                            <Grid Background="Transparent" SnapsToDevicePixels="False">
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="19"/>
                                                    <RowDefinition Height="*"/>
                                                </Grid.RowDefinitions>
                                                <Grid>
                                                    <Grid.LayoutTransform>
                                                        <TransformGroup>
                                                            <RotateTransform Angle="-90"/>
                                                        </TransformGroup>
                                                    </Grid.LayoutTransform>
                                                    <Ellipse x:Name="circle" Fill="White" HorizontalAlignment="Center" Height="19" Stroke="#FF333333" VerticalAlignment="Center" Width="19"/>
                                                    <Path x:Name="arrow" Data="M1,1.5L4.5,5 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="False" Stroke="#FF333333" StrokeThickness="2" VerticalAlignment="Center"/>
                                                </Grid>
                                                <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="Center" Margin="0,4,0,0" Grid.Row="1" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Top"/>
                                            </Grid>
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsChecked" Value="True">
                                                <Setter Property="Data" TargetName="arrow" Value="M1,4.5L4.5,1 8,4.5"/>
                                            </Trigger>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter Property="Stroke" TargetName="circle" Value="#FF5593FF"/>
                                                <Setter Property="Fill" TargetName="circle" Value="#FFF3F9FF"/>
                                                <Setter Property="Stroke" TargetName="arrow" Value="Black"/>
                                            </Trigger>
                                            <Trigger Property="IsPressed" Value="True">
                                                <Setter Property="Stroke" TargetName="circle" Value="#FF3C77DD"/>
                                                <Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
                                                <Setter Property="Fill" TargetName="circle" Value="#FFD9ECFF"/>
                                                <Setter Property="Stroke" TargetName="arrow" Value="Black"/>
                                            </Trigger>
                                            <Trigger Property="IsEnabled" Value="False">
                                                <Setter Property="Stroke" TargetName="circle" Value="#FFBCBCBC"/>
                                                <Setter Property="Fill" TargetName="circle" Value="#FFE6E6E6"/>
                                                <Setter Property="Stroke" TargetName="arrow" Value="#FF707070"/>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Setter.Value>
                </Setter>
            </Trigger>
            <Trigger Property="ExpandDirection" Value="Up">
                <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Top"/>
                <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Bottom"/>
                <Setter Property="Style" TargetName="HeaderSite">
                    <Setter.Value>
                        <Style TargetType="{x:Type ToggleButton}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                                        <Border Padding="{TemplateBinding Padding}">
                                            <Grid Background="Transparent" SnapsToDevicePixels="False">
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="19"/>
                                                    <ColumnDefinition Width="*"/>
                                                </Grid.ColumnDefinitions>
                                                <Grid>
                                                    <Grid.LayoutTransform>
                                                        <TransformGroup>
                                                            <RotateTransform Angle="180"/>
                                                        </TransformGroup>
                                                    </Grid.LayoutTransform>
                                                    <Ellipse x:Name="circle" Fill="White" HorizontalAlignment="Center" Height="19" Stroke="#FF333333" VerticalAlignment="Center" Width="19"/>
                                                    <Path x:Name="arrow" Data="M1,1.5L4.5,5 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="False" Stroke="#FF333333" StrokeThickness="2" VerticalAlignment="Center"/>
                                                </Grid>
                                                <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="1" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="Left" Margin="4,0,0,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Center"/>
                                            </Grid>
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsChecked" Value="True">
                                                <Setter Property="Data" TargetName="arrow" Value="M1,4.5L4.5,1 8,4.5"/>
                                            </Trigger>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter Property="Stroke" TargetName="circle" Value="#FF5593FF"/>
                                                <Setter Property="Fill" TargetName="circle" Value="#FFF3F9FF"/>
                                                <Setter Property="Stroke" TargetName="arrow" Value="Black"/>
                                            </Trigger>
                                            <Trigger Property="IsPressed" Value="True">
                                                <Setter Property="Stroke" TargetName="circle" Value="#FF3C77DD"/>
                                                <Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
                                                <Setter Property="Fill" TargetName="circle" Value="#FFD9ECFF"/>
                                                <Setter Property="Stroke" TargetName="arrow" Value="Black"/>
                                            </Trigger>
                                            <Trigger Property="IsEnabled" Value="False">
                                                <Setter Property="Stroke" TargetName="circle" Value="#FFBCBCBC"/>
                                                <Setter Property="Fill" TargetName="circle" Value="#FFE6E6E6"/>
                                                <Setter Property="Stroke" TargetName="arrow" Value="#FF707070"/>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Setter.Value>
                </Setter>
            </Trigger>
            <Trigger Property="ExpandDirection" Value="Left">
                <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Left"/>
                <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Right"/>
                <Setter Property="Style" TargetName="HeaderSite">
                    <Setter.Value>
                        <Style TargetType="{x:Type ToggleButton}">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                                        <Border Padding="{TemplateBinding Padding}">
                                            <Grid Background="Transparent" SnapsToDevicePixels="False">
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="19"/>
                                                    <RowDefinition Height="*"/>
                                                </Grid.RowDefinitions>
                                                <Grid>
                                                    <Grid.LayoutTransform>
                                                        <TransformGroup>
                                                            <RotateTransform Angle="90"/>
                                                        </TransformGroup>
                                                    </Grid.LayoutTransform>
                                                    <Ellipse x:Name="circle" Fill="White" HorizontalAlignment="Center" Height="19" Stroke="#FF333333" VerticalAlignment="Center" Width="19"/>
                                                    <Path x:Name="arrow" Data="M1,1.5L4.5,5 8,1.5" HorizontalAlignment="Center" SnapsToDevicePixels="False" Stroke="#FF333333" StrokeThickness="2" VerticalAlignment="Center"/>
                                                </Grid>
                                                <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="Center" Margin="0,4,0,0" Grid.Row="1" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Top"/>
                                            </Grid>
                                        </Border>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsChecked" Value="True">
                                                <Setter Property="Data" TargetName="arrow" Value="M1,4.5L4.5,1 8,4.5"/>
                                            </Trigger>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter Property="Stroke" TargetName="circle" Value="#FF5593FF"/>
                                                <Setter Property="Fill" TargetName="circle" Value="#FFF3F9FF"/>
                                                <Setter Property="Stroke" TargetName="arrow" Value="Black"/>
                                            </Trigger>
                                            <Trigger Property="IsPressed" Value="True">
                                                <Setter Property="Stroke" TargetName="circle" Value="#FF3C77DD"/>
                                                <Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
                                                <Setter Property="Fill" TargetName="circle" Value="#FFD9ECFF"/>
                                                <Setter Property="Stroke" TargetName="arrow" Value="Black"/>
                                            </Trigger>
                                            <Trigger Property="IsEnabled" Value="False">
                                                <Setter Property="Stroke" TargetName="circle" Value="#FFBCBCBC"/>
                                                <Setter Property="Fill" TargetName="circle" Value="#FFE6E6E6"/>
                                                <Setter Property="Stroke" TargetName="arrow" Value="#FF707070"/>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Setter.Value>
                </Setter>
            </Trigger>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
</UserControl.Resources>

<Grid>
    <Expander Margin="5" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" IsExpanded="True" Template="{DynamicResource ExpanderControlTemplate1}">
        <Expander.Header>
            <DockPanel Margin="5">
                <TextBlock Margin="5" VerticalAlignment="Center">Hello World</TextBlock>
                <Rectangle x:Name="ElementA" Width="100" Height="50" Fill="Transparent" />
            </DockPanel>
        </Expander.Header>
        <Expander.Content>
            <StackPanel Orientation="Horizontal">
                <Canvas Margin="5" Width="300" Background="Black" />

                <StackPanel>
                    <StackPanel.Resources>
                        <Style TargetType="{x:Type RadioButton}">
                            <Setter Property="Margin" Value="5" />
                        </Style>
                    </StackPanel.Resources>
                    <RadioButton IsChecked="True">Option 1</RadioButton>
                    <RadioButton>Option 2</RadioButton>
                    <RadioButton>Option 3</RadioButton>
                    <RadioButton>Option 4</RadioButton>
                    <Rectangle x:Name="ElementB" Width="100" Height="50" Fill="Transparent" />
                </StackPanel>
            </StackPanel>
        </Expander.Content>
    </Expander>
</Grid>

И поведение:

public class AnimatedMarginBehavior : Behavior<FrameworkElement>
{
    private ThicknessAnimation Animation = new ThicknessAnimation();

    public FrameworkElement ElementA
    {
        get { return GetValue(ElementAProperty) as FrameworkElement; }
        set { SetValue(ElementAProperty, value); }
    }

    public static readonly DependencyProperty ElementAProperty =
        DependencyProperty.Register("ElementA", typeof(FrameworkElement), typeof(AnimatedMarginBehavior),
            new PropertyMetadata(default(FrameworkElement), (d, e) => (d as AnimatedMarginBehavior).OnBindingChanged(e)));

    public FrameworkElement ElementB
    {
        get { return GetValue(ElementBProperty) as FrameworkElement; }
        set { SetValue(ElementBProperty, value); }
    }

    public static readonly DependencyProperty ElementBProperty =
        DependencyProperty.Register("ElementB", typeof(FrameworkElement), typeof(AnimatedMarginBehavior),
            new PropertyMetadata(default(FrameworkElement), (d, e) => (d as AnimatedMarginBehavior).OnBindingChanged(e)));

    public Expander Parent
    {
        get { return GetValue(ParentProperty) as Expander; }
        set { SetValue(ParentProperty, value); }
    }

    public static readonly DependencyProperty ParentProperty =
        DependencyProperty.Register("Parent", typeof(Expander), typeof(AnimatedMarginBehavior),
            new PropertyMetadata(default(Expander), (d, e) => (d as AnimatedMarginBehavior).OnBindingChanged(e)));

    public Grid Grid
    {
        get { return GetValue(GridProperty) as Grid; }
        set { SetValue(GridProperty, value); }
    }

    public static readonly DependencyProperty GridProperty =
        DependencyProperty.Register("Grid", typeof(Grid), typeof(AnimatedMarginBehavior),
            new PropertyMetadata(default(Grid), (d, e) => (d as AnimatedMarginBehavior).OnBindingChanged(e)));

    private void OnBindingChanged(DependencyPropertyChangedEventArgs e)
    {
        if (this.ElementA == null)
            return;
        if (this.ElementB == null)
            return;
        if (this.Parent == null)
            return;
        if (this.Grid == null)
            return;

        // set initial position based on whether or not the control is expanded
        var currentElement = this.Parent.IsExpanded ? this.ElementB : this.ElementA;
        UIElement container = VisualTreeHelper.GetParent(this.Grid) as UIElement;
        var pos = currentElement.TranslatePoint(new Point(0, 0), container);
        this.AssociatedObject.SetValue(FrameworkElement.MarginProperty, new Thickness(pos.X-1, pos.Y-1, 0, 0));

        // get notification when the expander changes state
        this.Parent.Collapsed += (_s1, _e1) =>
        {
            container = VisualTreeHelper.GetParent(this.Grid) as UIElement;
            var from = this.ElementB.TranslatePoint(new Point(0, 0), container);
            var to = this.ElementA.TranslatePoint(new Point(0, 0), container);
            this.Animation.From = new Thickness(from.X, from.Y, 0, 0);
            this.Animation.To = new Thickness(to.X, to.Y, 0, 0);
            this.Animation.Duration = TimeSpan.FromMilliseconds(500);
            this.Animation.EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseInOut };
            this.AssociatedObject.BeginAnimation(FrameworkElement.MarginProperty, this.Animation);
        };

        this.Parent.Expanded += (_s2, _e2) =>
        {
            container = VisualTreeHelper.GetParent(this.Grid) as UIElement;
            var from = this.ElementA.TranslatePoint(new Point(0, 0), container);
            var to = this.ElementB.TranslatePoint(new Point(0, 0), container);
            this.Animation.From = new Thickness(from.X, from.Y, 0, 0);
            this.Animation.To = new Thickness(to.X, to.Y, 0, 0);
            this.Animation.Duration = TimeSpan.FromMilliseconds(500);
            this.Animation.EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseInOut };
            this.AssociatedObject.BeginAnimation(FrameworkElement.MarginProperty, this.Animation);
        };
    }
}

Это очень грубый и готовый пример, который дает общее представление о том, как это сделать, вам определенно захочется пройтись по нему подробно и немного его почистить.

Результат:

enter image description here

0 голосов
/ 10 мая 2019

напрямую пробовал с xaml только без кода на c #.надеюсь, что это работает для вас.

  <Window.Resources>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Margin" Value="5" />
            <Setter Property="Background" Value="Red" />
            <Setter Property="Foreground" Value="White" />
        </Style>
        <Storyboard x:Key="Storyboard1">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="button">
                <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="221"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="button">
                <EasingDoubleKeyFrame KeyTime="0:0:0.0001" Value="161"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="Storyboard2">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="button">
                <EasingDoubleKeyFrame KeyTime="0" Value="221">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ElasticEase EasingMode="EaseInOut" Oscillations="0" Springiness="0"/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
                <EasingDoubleKeyFrame KeyTime="0" Value="161">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ElasticEase EasingMode="EaseInOut" Oscillations="0" Springiness="0"/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
                <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="-3">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ElasticEase EasingMode="EaseInOut" Oscillations="0" Springiness="0"/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
                <!-- <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="0"/> -->
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="button">
                <EasingDoubleKeyFrame KeyTime="0" Value="156">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ElasticEase EasingMode="EaseInOut" Oscillations="0" Springiness="0"/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
                <EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="-1">
                    <EasingDoubleKeyFrame.EasingFunction>
                        <ElasticEase EasingMode="EaseInOut" Oscillations="0" Springiness="0"/>
                    </EasingDoubleKeyFrame.EasingFunction>
                </EasingDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded" SourceName="window">
            <StopStoryboard x:Name="Storyboard1_Storyboard" BeginStoryboardName="Storyboard1_Storyboard"/>
            <BeginStoryboard x:Name="Storyboard1_BeginStoryboard" Storyboard="{StaticResource Storyboard1}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="Expander.Expanded" SourceName="expander">
            <BeginStoryboard Storyboard="{StaticResource Storyboard1}"/>
        </EventTrigger>
        <EventTrigger RoutedEvent="Expander.Collapsed" SourceName="expander">
            <BeginStoryboard Storyboard="{StaticResource Storyboard2}"/>
        </EventTrigger>
    </Window.Triggers>

    <Grid>
        <Expander x:Name="expander" Margin="5" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch" IsExpanded="False">
            <Expander.Header>
                <StackPanel Width="195.313">
                    <Grid x:Name="grid" RenderTransformOrigin="0.5,0.5">
                        <Grid.RenderTransform>
                            <TransformGroup>
                                <ScaleTransform/>
                                <SkewTransform/>
                                <RotateTransform/>
                                <TranslateTransform/>
                            </TransformGroup>
                        </Grid.RenderTransform>
                        <TextBlock x:Name="textBlock" Margin="5" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
                            <TextBlock.RenderTransform>
                                <TransformGroup>
                                    <ScaleTransform/>
                                    <SkewTransform/>
                                    <RotateTransform/>
                                    <TranslateTransform/>
                                </TransformGroup>
                            </TextBlock.RenderTransform><Run Text="Hello World"/></TextBlock>
                        <Button x:Name="button" Padding="5" RenderTransformOrigin="0.5,0.5" Content="Button" Margin="93,5,5,5">
                            <Button.RenderTransform>
                                <TransformGroup>
                                    <ScaleTransform/>
                                    <SkewTransform/>
                                    <RotateTransform/>
                                    <TranslateTransform/>
                                </TransformGroup>
                            </Button.RenderTransform>
                        </Button>
                    </Grid>
                </StackPanel>


            </Expander.Header>
            <StackPanel Orientation="Horizontal">
                <Canvas Margin="5" Width="300" Background="Black" />

                <StackPanel>
                    <StackPanel.Resources>
                        <Style TargetType="{x:Type RadioButton}">
                            <Setter Property="Margin" Value="5" />
                        </Style>
                    </StackPanel.Resources>
                    <RadioButton IsChecked="True" Content="Option 1"/>
                    <RadioButton Content="Option 2"/>
                    <RadioButton Content="Option 3"/>
                    <RadioButton Content="Option 4"/>
                </StackPanel>
            </StackPanel>
        </Expander>
    </Grid>

будьте осторожны с дизайном.

...