根据dependencyproperty使用动画
本文关键字:动画 dependencyproperty 根据 | 更新日期: 2023-09-27 18:12:57
我终于在WPF中创建了我的第一个自定义控件!
它基本上是一个容器,一旦你把鼠标悬停在它上面,一个新的容器就会从窗口的左边缘滑进来。
我现在试着让它能够选择从上、下、左或右滑动。
我想知道我是否可以在默认模板中实现一个逻辑或一些东西,可以根据属性或史密斯来确定使用哪个动画。如果我有一个属性,我们叫它SlideInFrom它是SlideInFrom = "left"那么我的动画就能很好地做到这一点。我不知道如何实现这样的逻辑!
这是我的完整源代码:
类:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace TestCustomControl
{
[TemplatePart(Name = "SlideContainerClosed", Type = typeof(ContentPresenter)),
TemplatePart(Name = "SlideContainerExpanded", Type = typeof(ContentPresenter)),
TemplateVisualState(Name = "Closed", GroupName = "ViewStates"),
TemplateVisualState(Name = "Expanded", GroupName = "ViewStates")]
public class SlideGrid : System.Windows.Controls.Control
{
public static readonly DependencyProperty ClosedStateProperty = DependencyProperty.Register("ClosedState", typeof(object), typeof(SlideGrid), null);
public static readonly DependencyProperty ExpandedStateProperty = DependencyProperty.Register("ExpandedState", typeof(object), typeof(SlideGrid), null);
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(SlideGrid), null);
static SlideGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SlideGrid), new FrameworkPropertyMetadata(typeof(SlideGrid)));
}
public object ClosedState
{
get
{
return base.GetValue(ClosedStateProperty);
}
set
{
base.SetValue(ClosedStateProperty, value);
}
}
public object ExpandedState
{
get
{
return base.GetValue(ExpandedStateProperty);
}
set
{
base.SetValue(ExpandedStateProperty, value);
}
}
public bool IsExpanded
{
get
{
return (bool)base.GetValue(IsExpandedProperty);
}
set
{
base.SetValue(IsExpandedProperty, value);
ChangeVisualState(true);
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ContentPresenter slideContainerClosed = base.GetTemplateChild("ClosedState") as ContentPresenter;
if (slideContainerClosed != null) slideContainerClosed.MouseEnter += slideContainerClosed_GotMouseCapture;
ContentPresenter slideContainerExpanded = base.GetTemplateChild("ExpandedState") as ContentPresenter;
if (slideContainerExpanded != null) slideContainerExpanded.MouseLeave += slideContainerExpanded_LostMouseCapture;
this.ChangeVisualState(false);
}
private void slideContainerClosed_GotMouseCapture(object sender, MouseEventArgs e)
{
this.IsExpanded = true;
}
private void slideContainerExpanded_LostMouseCapture(object sender, MouseEventArgs e)
{
this.IsExpanded = false;
}
private void ChangeVisualState(bool useTransitions)
{
if (!this.IsExpanded)
{
VisualStateManager.GoToState(this, "Closed", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Expanded", useTransitions);
}
UIElement closed = ClosedState as UIElement;
if (closed != null)
{
if (IsExpanded)
{
closed.Visibility = Visibility.Hidden;
}
else
{
closed.Visibility = Visibility.Visible;
}
}
UIElement expanded = ExpandedState as UIElement;
if (expanded != null)
{
if (IsExpanded)
{
expanded.Visibility = Visibility.Visible;
}
else
{
expanded.Visibility = Visibility.Hidden;
}
}
}
}
}
包含默认模板的字典:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestCustomControl">
<Style TargetType="{x:Type local:SlideGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SlideGrid}">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ViewStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.3" To="Expanded">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FlipButtonTransform"
Storyboard.TargetProperty="X" To="0" Duration="0:0:0.3"></DoubleAnimation>
</Storyboard>
</VisualTransition>
<VisualTransition GeneratedDuration="0:0:0.3" To="Closed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FlipButtonTransform"
Storyboard.TargetProperty="X" To="-300" Duration="0:0:0.3"></DoubleAnimation>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Closed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpandedState"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" ></DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="Expanded">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ClosedState"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" ></DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FlipButtonTransform"
Storyboard.TargetProperty="X"
To="0"
Duration="0"></DoubleAnimation>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- This is the Closed content. -->
<ContentPresenter x:Name="ClosedState"
Content="{TemplateBinding ClosedState}">
</ContentPresenter>
<!-- This is the Expanded content. -->
<ContentPresenter x:Name="ExpandedState"
Content="{TemplateBinding ExpandedState}">
<ContentPresenter.RenderTransform>
<TranslateTransform x:Name="FlipButtonTransform" X="-300"></TranslateTransform>
</ContentPresenter.RenderTransform>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Matthew macdonald的技巧是:在TestCustomControl组件中创建一个名为"theme"的文件夹通用。Xaml被放置在主题文件夹中,下面是Xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/TestCustomControl;component/SlideGrid.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
最后这是主窗口使用它:
<Window x:Class="TestCustomControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:lib="clr-namespace:TestCustomControl">
<Grid x:Name="LayoutRoot" Background="White">
<lib:SlideGrid x:Name="panel" IsExpanded="False"
Margin="10">
<lib:SlideGrid.ClosedState>
<StackPanel Background="Aqua">
</StackPanel>
</lib:SlideGrid.ClosedState>
<lib:SlideGrid.ExpandedState>
<Grid Background="Black">
</Grid>
</lib:SlideGrid.ExpandedState>
</lib:SlideGrid>
</Grid>
</Window>
提前感谢!
可能有更好的方法来做到这一点,但这是一种方法。
首先,用你的SlideInFrom方向创建一个枚举。确保这个enum不是在你的SlideGrid类中定义的,否则它不会工作(至少对我来说不是)。
public enum SlideInFromDirection
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3,
}
接下来,在你的ControlTemplate中为Y属性创建第二个DoubleAnimation,并将X和Y命名为DoubleAnimation。这样的:
<VisualTransition GeneratedDuration="0:0:0.3" To="Expanded">
<Storyboard>
<DoubleAnimation x:Name="VSExpandedX" Storyboard.TargetName="FlipButtonTransform" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.3"></DoubleAnimation>
<DoubleAnimation x:Name="VSExpandedY" Storyboard.TargetName="FlipButtonTransform" Storyboard.TargetProperty="Y" To="0" Duration="0:0:0.3"></DoubleAnimation>
</Storyboard>
</VisualTransition>
在你的SlideGrid类中,创建两个私有成员变量来保存对这些DoubleAnimations的引用:
private System.Windows.Media.Animation.DoubleAnimation VSExpandedX;
private System.Windows.Media.Animation.DoubleAnimation VSExpandedY;
在OnApplyTemplate()中添加填充这些的代码。我在ChangeVisualState(false)行之前添加了它。我们一会儿会讲到slideInFromPropertyChangedCallback。我们需要在这里调用它,因为在XAML解析期间设置SlideInFromProperty时动画变量还没有设置:
this.VSExpandedX = base.GetTemplateChild("VSExpandedX") as System.Windows.Media.Animation.DoubleAnimation;
this.VSExpandedY = base.GetTemplateChild("VSExpandedY") as System.Windows.Media.Animation.DoubleAnimation;
slideInFromPropertyChangedCallback(this, new DependencyPropertyChangedEventArgs(SlideInFromProperty, this.SlideInFrom, this.SlideInFrom));
现在,创建一个DependencyProperty指定SlideInFrom的方向。我们需要知道它的值何时发生变化所以我们还需要slideInFromPropertyChangedCallback方法:
public SlideInFromDirection SlideInFrom
{
get { return (SlideInFromDirection)GetValue(SlideInFromProperty); }
set { SetValue(SlideInFromProperty, value); }
}
public static readonly DependencyProperty SlideInFromProperty =
DependencyProperty.Register("SlideInFrom", typeof(SlideInFromDirection), typeof(SlideGrid), new PropertyMetadata(SlideInFromDirection.Left, slideInFromPropertyChangedCallback));
private static void slideInFromPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SlideGrid sg = d as SlideGrid;
if (sg != null && e.NewValue is SlideInFromDirection)
{
if (sg.VSExpandedX == null || sg.VSExpandedY == null)
return;
SlideInFromDirection sd = (SlideInFromDirection)e.NewValue;
switch (sd)
{
case SlideInFromDirection.Left:
sg.VSExpandedX.From = -300.0;
sg.VSExpandedY.From = 0.0;
break;
case SlideInFromDirection.Right:
sg.VSExpandedX.From = 300.0;
sg.VSExpandedY.From = 0.0;
break;
case SlideInFromDirection.Top:
sg.VSExpandedX.From = 0.0;
sg.VSExpandedY.From = -300.0;
break;
case SlideInFromDirection.Bottom:
sg.VSExpandedX.From = 0.0;
sg.VSExpandedY.From = 300.0;
break;
}
}
}
就是这样。
这是完整的源代码:
MainWindow.xaml
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication5"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:SlideGrid x:Name="panel" IsExpanded="False" Margin="10" SlideInFrom="Bottom" >
<local:SlideGrid.ClosedState>
<StackPanel Background="Aqua" />
</local:SlideGrid.ClosedState>
<local:SlideGrid.ExpandedState>
<Grid Background="Black">
</Grid>
</local:SlideGrid.ExpandedState>
</local:SlideGrid>
</Grid>
SlideGrid.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication5
{
public enum SlideInFromDirection
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3,
}
[TemplatePart(Name = "SlideContainerClosed", Type = typeof(ContentPresenter)),
TemplatePart(Name = "SlideContainerExpanded", Type = typeof(ContentPresenter)),
TemplateVisualState(Name = "Closed", GroupName = "ViewStates"),
TemplateVisualState(Name = "Expanded", GroupName = "ViewStates")]
public class SlideGrid : System.Windows.Controls.Control
{
public static readonly DependencyProperty ClosedStateProperty = DependencyProperty.Register("ClosedState", typeof(object), typeof(SlideGrid), null);
public static readonly DependencyProperty ExpandedStateProperty = DependencyProperty.Register("ExpandedState", typeof(object), typeof(SlideGrid), null);
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(SlideGrid), null);
public SlideInFromDirection SlideInFrom
{
get { return (SlideInFromDirection)GetValue(SlideInFromProperty); }
set { SetValue(SlideInFromProperty, value); }
}
public static readonly DependencyProperty SlideInFromProperty =
DependencyProperty.Register("SlideInFrom", typeof(SlideInFromDirection), typeof(SlideGrid), new PropertyMetadata(SlideInFromDirection.Left, slideInFromPropertyChangedCallback));
private static void slideInFromPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SlideGrid sg = d as SlideGrid;
if (sg != null && e.NewValue is SlideInFromDirection)
{
if (sg.VSExpandedX == null || sg.VSExpandedY == null)
return;
SlideInFromDirection sd = (SlideInFromDirection)e.NewValue;
switch (sd)
{
case SlideInFromDirection.Left:
sg.VSExpandedX.From = -300.0;
sg.VSExpandedY.From = 0.0;
break;
case SlideInFromDirection.Right:
sg.VSExpandedX.From = 300.0;
sg.VSExpandedY.From = 0.0;
break;
case SlideInFromDirection.Top:
sg.VSExpandedX.From = 0.0;
sg.VSExpandedY.From = -300.0;
break;
case SlideInFromDirection.Bottom:
sg.VSExpandedX.From = 0.0;
sg.VSExpandedY.From = 300.0;
break;
}
}
}
private System.Windows.Media.Animation.DoubleAnimation VSExpandedX;
private System.Windows.Media.Animation.DoubleAnimation VSExpandedY;
static SlideGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SlideGrid), new FrameworkPropertyMetadata(typeof(SlideGrid)));
}
public object ClosedState
{
get
{
return base.GetValue(ClosedStateProperty);
}
set
{
base.SetValue(ClosedStateProperty, value);
}
}
public object ExpandedState
{
get
{
return base.GetValue(ExpandedStateProperty);
}
set
{
base.SetValue(ExpandedStateProperty, value);
}
}
public bool IsExpanded
{
get
{
return (bool)base.GetValue(IsExpandedProperty);
}
set
{
base.SetValue(IsExpandedProperty, value);
ChangeVisualState(true);
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ContentPresenter slideContainerClosed = base.GetTemplateChild("ClosedState") as ContentPresenter;
if (slideContainerClosed != null) slideContainerClosed.MouseEnter += slideContainerClosed_GotMouseCapture;
ContentPresenter slideContainerExpanded = base.GetTemplateChild("ExpandedState") as ContentPresenter;
if (slideContainerExpanded != null) slideContainerExpanded.MouseLeave += slideContainerExpanded_LostMouseCapture;
this.VSExpandedX = base.GetTemplateChild("VSExpandedX") as System.Windows.Media.Animation.DoubleAnimation;
this.VSExpandedY = base.GetTemplateChild("VSExpandedY") as System.Windows.Media.Animation.DoubleAnimation;
slideInFromPropertyChangedCallback(this, new DependencyPropertyChangedEventArgs(SlideInFromProperty, this.SlideInFrom, this.SlideInFrom));
this.ChangeVisualState(false);
}
private void slideContainerClosed_GotMouseCapture(object sender, MouseEventArgs e)
{
this.IsExpanded = true;
}
private void slideContainerExpanded_LostMouseCapture(object sender, MouseEventArgs e)
{
this.IsExpanded = false;
}
private void ChangeVisualState(bool useTransitions)
{
if (!this.IsExpanded)
{
VisualStateManager.GoToState(this, "Closed", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Expanded", useTransitions);
}
UIElement closed = ClosedState as UIElement;
if (closed != null)
{
if (IsExpanded)
{
closed.Visibility = Visibility.Hidden;
}
else
{
closed.Visibility = Visibility.Visible;
}
}
UIElement expanded = ExpandedState as UIElement;
if (expanded != null)
{
if (IsExpanded)
{
expanded.Visibility = Visibility.Visible;
}
else
{
expanded.Visibility = Visibility.Hidden;
}
}
}
}
}
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication5">
<Style TargetType="{x:Type local:SlideGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SlideGrid}">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ViewStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.3" To="Expanded">
<Storyboard>
<DoubleAnimation x:Name="VSExpandedX" Storyboard.TargetName="FlipButtonTransform" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.3"></DoubleAnimation>
<DoubleAnimation x:Name="VSExpandedY" Storyboard.TargetName="FlipButtonTransform" Storyboard.TargetProperty="Y" To="0" Duration="0:0:0.3"></DoubleAnimation>
</Storyboard>
</VisualTransition>
<VisualTransition GeneratedDuration="0:0:0.3" To="Closed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FlipButtonTransform" Storyboard.TargetProperty="X" To="-300" Duration="0:0:0.3"></DoubleAnimation>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Closed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpandedState"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" ></DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="Expanded">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ClosedState"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" ></DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="FlipButtonTransform"
Storyboard.TargetProperty="X"
To="0"
Duration="0"></DoubleAnimation>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- This is the Closed content. -->
<ContentPresenter x:Name="ClosedState" Content="{TemplateBinding ClosedState}">
</ContentPresenter>
<!-- This is the Expanded content. -->
<ContentPresenter x:Name="ExpandedState" Content="{TemplateBinding ExpandedState}">
<ContentPresenter.RenderTransform>
<TranslateTransform x:Name="FlipButtonTransform" X="-300" Y="0"></TranslateTransform>
</ContentPresenter.RenderTransform>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>