动画按钮从一点到另一点
Animate button from one point to another
我有一个包含一些元素和一个按钮的扩展器。当扩展器折叠时,我想在扩展器的 header 中显示按钮以便于访问。当扩展器展开时,我希望按钮成为扩展器内容的一部分。
我可以让 header 中的按钮在扩展器展开和折叠时淡入和淡出,但我想更进一步。我希望展开器内容中的按钮在折叠时向上移动到 header 中的位置,并在展开时向下移动到内容的位置。
我可以通过使用混合并在两个按钮之间画一条线并将其转换为运动路径来接近一些。然而,使用这种方法很难准确定位。我的一部分感觉画出路径有点 over-complicating 的事情。我只想让按钮 A 动画成为按钮 B。
目前我只关心按钮的位置。这两个按钮的大小不同,但动画按钮的宽度和高度更直接:)
这是我快速制作的示例程序,用于演示我正在使用的内容。它主要只是提供一个视觉布局。本例中不包括淡入淡出动画和路径动画。
<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>
非常感谢任何帮助。我试图找到一些资源来提供帮助,但我所能找到的只是沿着路径移动元素,而不是移动到特定点,除非我遗漏了一些明显的东西。
谢谢
这不是一件小事。
基本上,您正在尝试为可视化树的两个完全不同部分之间的按钮设置动画,因此您将不得不对整个事物进行模板化,并将所有内容包装在一个 parent 布局中,其中包含您的扩展器和您要设置动画的按钮。
模板化您的扩展器应该不会太难,只需将光标移动到它的 XAML,转到属性选项卡,select "Template" 然后单击小方块为新的 ControlTemplate 生成所有代码的权利。我已经在下面的代码中为您完成了,但是主题可能完全错误,因此您需要自己完成。
下一步是创建两个矩形,一个在扩展器 header 中,一个在其内容区域中。将这些的大小设置为您想要的任何按钮,但将填充设置为 Transparent;它们只是帮助布局的占位符,稍后您将使用它们来对齐按钮控件。
现在您需要返回到您的 ControlTemplate 并将所有内容(即 top-level 边框)包裹在一个网格中。您还需要将实际按钮作为此网格的 child 放置。
最后,您需要一个行为绑定到您创建的两个矩形(因此它可以计算起点和终点)、parent 网格(因此它有一个 parent控制它可以使您的按钮相对于)和扩展器(因此它可以触发动画以响应扩展器的扩展和折叠)。实际的动画本身可以是一个 ThicknessAnimation,它为按钮的边距设置动画。
把所有这些放在一起,你会得到这个:
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);
};
}
}
这是一个非常 rough-and-ready 的示例,给出了如何执行此操作的一般概念,您肯定想详细了解它并稍微清理一下。
结果:
仅在没有任何 C# 代码的情况下直接使用 xaml 进行了尝试。希望这对你有用。
<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>
注意设计。
我有一个包含一些元素和一个按钮的扩展器。当扩展器折叠时,我想在扩展器的 header 中显示按钮以便于访问。当扩展器展开时,我希望按钮成为扩展器内容的一部分。
我可以让 header 中的按钮在扩展器展开和折叠时淡入和淡出,但我想更进一步。我希望展开器内容中的按钮在折叠时向上移动到 header 中的位置,并在展开时向下移动到内容的位置。
我可以通过使用混合并在两个按钮之间画一条线并将其转换为运动路径来接近一些。然而,使用这种方法很难准确定位。我的一部分感觉画出路径有点 over-complicating 的事情。我只想让按钮 A 动画成为按钮 B。
目前我只关心按钮的位置。这两个按钮的大小不同,但动画按钮的宽度和高度更直接:)
这是我快速制作的示例程序,用于演示我正在使用的内容。它主要只是提供一个视觉布局。本例中不包括淡入淡出动画和路径动画。
<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>
非常感谢任何帮助。我试图找到一些资源来提供帮助,但我所能找到的只是沿着路径移动元素,而不是移动到特定点,除非我遗漏了一些明显的东西。
谢谢
这不是一件小事。
基本上,您正在尝试为可视化树的两个完全不同部分之间的按钮设置动画,因此您将不得不对整个事物进行模板化,并将所有内容包装在一个 parent 布局中,其中包含您的扩展器和您要设置动画的按钮。
模板化您的扩展器应该不会太难,只需将光标移动到它的 XAML,转到属性选项卡,select "Template" 然后单击小方块为新的 ControlTemplate 生成所有代码的权利。我已经在下面的代码中为您完成了,但是主题可能完全错误,因此您需要自己完成。
下一步是创建两个矩形,一个在扩展器 header 中,一个在其内容区域中。将这些的大小设置为您想要的任何按钮,但将填充设置为 Transparent;它们只是帮助布局的占位符,稍后您将使用它们来对齐按钮控件。
现在您需要返回到您的 ControlTemplate 并将所有内容(即 top-level 边框)包裹在一个网格中。您还需要将实际按钮作为此网格的 child 放置。
最后,您需要一个行为绑定到您创建的两个矩形(因此它可以计算起点和终点)、parent 网格(因此它有一个 parent控制它可以使您的按钮相对于)和扩展器(因此它可以触发动画以响应扩展器的扩展和折叠)。实际的动画本身可以是一个 ThicknessAnimation,它为按钮的边距设置动画。
把所有这些放在一起,你会得到这个:
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);
};
}
}
这是一个非常 rough-and-ready 的示例,给出了如何执行此操作的一般概念,您肯定想详细了解它并稍微清理一下。
结果:
仅在没有任何 C# 代码的情况下直接使用 xaml 进行了尝试。希望这对你有用。
<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>
注意设计。