C# 自定义控件x:类问题和动画
我试图创建一个菜单自定义控件,其中包含一些动画,使事情看起来更“流畅”。这就是我遇到问题的地方,我希望自定义控件的宽度由动画驱动,以模拟菜单的扩展。我可以通过在模板中使用触发器来实现这一点,但使用这种方法,我似乎找不到绑定“to”的方法,我读到这是不允许的,因为线程安全,所以我想我应该只使用“Click”事件,并在代码后面制作动画 当我试图将我的类添加到资源字典时,我最终得到了一堆错误,在类文件中说“DefaultStyleKeyProperty”在当前上下文中不存在。我需要将我的类添加到资源字典中,以便能够访问任何UI对象。所以我有点被困在如何实现这一点上,还是我走错了方向 以下是我到目前为止的情况: NavMenu.xaml Generic.xamlC# 自定义控件x:类问题和动画,c#,wpf,xaml,custom-controls,C#,Wpf,Xaml,Custom Controls,我试图创建一个菜单自定义控件,其中包含一些动画,使事情看起来更“流畅”。这就是我遇到问题的地方,我希望自定义控件的宽度由动画驱动,以模拟菜单的扩展。我可以通过在模板中使用触发器来实现这一点,但使用这种方法,我似乎找不到绑定“to”的方法,我读到这是不允许的,因为线程安全,所以我想我应该只使用“Click”事件,并在代码后面制作动画 当我试图将我的类添加到资源字典时,我最终得到了一堆错误,在类文件中说“DefaultStyleKeyProperty”在当前上下文中不存在。我需要将我的类添加到资源字
我尝试将xClass添加到Generic.xaml中,但也没有成功,当我这样做时,我收到了另一个错误:“NavMenu的部分声明不能指定不同的基类”
编辑
我知道必须有一个更好的方法来做到这一点,使用互动库。我最后做的是专门为动画创建一个新类。然后我可以将新的TriggerAction附加到我的控件上,我发现我需要为目标动画元素提供一个ListContainer。这是因为无法实现从0到“自动”的动画,面板提供了一种获取子对象并在代码中计算“自动”宽度的方法。以下是我的更新: NavMenu.cs
namespace Happ.UI.Controls{
公共类导航菜单:菜单{
静态导航菜单(){
DefaultStyleKeyProperty.OverrideMetadata(typeof(NavMenu)),new FrameworkPropertyMetadata(typeof(NavMenu));
}
///
///指示菜单是否展开的标志
///
公共图书馆被扩展了{
得到{
返回(bool)GetValue(IsExpandedProperty);
}
设置{
SetValue(IsExpandedProperty,值);
}
}
公共静态只读从属属性IsExpandedProperty=
DependencyProperty.Register(“IsExpanded”、typeof(bool)、typeof(NavMenu)、new PropertyMetadata(true));
}
}
NavMenu.Animations.cs
namespace Happ.UI.Controls{
公共类导航设置:System.Windows.Interactivity.TriggerAction{
///
///设置动画的目标元素
///
公众小组目标小组{
得到{
返回(面板)GetValue(TargetPanelProperty);
}
设置{
SetValue(TargetPanelProperty,value);
}
}
公共静态只读DependencyProperty TargetPanelProperty=
从属属性寄存器(“TargetPanel”、类型of(Panel)、类型of(NavMenuAnimations),
新属性元数据(空);
///
///设置动画的目标元素
///
公共双秒{
得到{
返回(双精度)GetValue(SecondsProperty);
}
设置{
设置值(第二属性,值);
}
}
公共静态只读从属属性SecondsProperty=
从属属性寄存器(“秒”、typeof(双精度)、typeof(导航状态),
新的房地产数据((双)0.5);
///
///这是每次触发时调用的主要动画方法
///
///
受保护的覆盖无效调用(对象参数){
双最大宽度=0;
//确保我们有一个目标元素
如果(TargetPanel==null){
抛出新异常(“没有为动画指定目标元素。(NavMenu动画)”;
}
//确保设置了最小宽度和高度
TargetPanel.MinWidth=(宽度>0)?宽度:TargetPanel.MinWidth;
//检查是否已设置MaxWidth
if(Double.IsInfinity(TargetPanel.MaxWidth)){
//在孩子们中间绕一圈,得到宽度
foreach(TargetPanel.Children中的UIElement元素){
//更新面板的最大宽度
maxWidth+=elem.RenderSize.Width;
}
//检查是否找到MaxWidth。如果未找到,则返回
如果(maxWidth==0)
返回;
//指定新的MaxWidth
TargetPanel.MaxWidth=MaxWidth;
}
//检查宽度是否为最小值(收缩)
if(TargetPanel.Width==TargetPanel.MinWidth){
BeginAnimation(FrameworkElement.WidthProperty,新的DoubleAnimation(TargetPanel.MaxWidth,TimeSpan.FromSeconds(秒));
}
否则{
BeginAnimation(FrameworkElement.WidthProperty,新的DoubleAnimation(TargetPanel.MinWidth,TimeSpan.FromSeconds(秒));
}
}
}
}
NavMenuTemplate.xaml
<ResourceDictionary x:Class="Happ.UI.Controls.NavMenu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Happ.UI.Controls">
<Style TargetType="{x:Type local:NavMenu}">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Background="Yellow" Orientation="Vertical"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:NavMenu}">
<Border Background="{TemplateBinding Background}" BorderBrush="Transparent">
<ToggleButton x:Name="btnMenu"
Margin="10" Padding="4" Background="Transparent"
HorizontalAlignment="Left" VerticalAlignment="Top"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:NavMenu}}, Path=IsExpanded}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
namespace Happ.UI.Controls {
public partial class NavMenu : Menu {
static NavMenu() {
DefaultStyleKeyProperty.OverrideMetadata( typeof( NavMenu ), new FrameworkPropertyMetadata( typeof( NavMenu ) ) );
}
public bool IsExpanded {
get {
return (bool)GetValue( IsExpandedProperty );
}
set {
TimeSpan tsTime = new TimeSpan( 0, 0, 0, 0, 500 );
DoubleAnimation mnuAnim = new DoubleAnimation( MinWidth, tsTime );
btnMenu.BeginAnimation( Width, mnuAnim );
SetValue( IsExpandedProperty, value );
}
}
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register( "IsExpanded", typeof( bool ), typeof( NavMenu ), new PropertyMetadata( true ) );
}
}
}
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Happ.UI.Controls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Happ.UI.Controls;component/Templates/NavMenu.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
namespace Happ.UI.Controls {
public class NavMenu : Menu {
static NavMenu() {
DefaultStyleKeyProperty.OverrideMetadata( typeof( NavMenu ), new FrameworkPropertyMetadata( typeof( NavMenu ) ) );
}
/// <summary>
/// Flag to Tell if the Menu is Expanded
/// </summary>
public bool IsExpanded {
get {
return (bool)GetValue( IsExpandedProperty );
}
set {
SetValue( IsExpandedProperty, value );
}
}
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register( "IsExpanded", typeof( bool ), typeof( NavMenu ), new PropertyMetadata( true ) );
}
}
namespace Happ.UI.Controls {
public class NavMenuAnimations : System.Windows.Interactivity.TriggerAction<UIElement> {
/// <summary>
/// Sets the Target Element for the Animation
/// </summary>
public Panel TargetPanel {
get {
return (Panel)GetValue( TargetPanelProperty );
}
set {
SetValue( TargetPanelProperty, value );
}
}
public static readonly DependencyProperty TargetPanelProperty =
DependencyProperty.Register( "TargetPanel", typeof( Panel ), typeof( NavMenuAnimations ),
new PropertyMetadata( null ) );
/// <summary>
/// Sets the Target Element for the Animation
/// </summary>
public double Seconds {
get {
return (double)GetValue( SecondsProperty );
}
set {
SetValue( SecondsProperty, value );
}
}
public static readonly DependencyProperty SecondsProperty =
DependencyProperty.Register( "Seconds", typeof( double ), typeof( NavMenuAnimations ),
new PropertyMetadata( (double)0.5 ) );
/// <summary>
/// This is the Main Animation Method that is called each time the Trigger occurs
/// </summary>
/// <param name="parameter"></param>
protected override void Invoke( object parameter ) {
double maxWidth = 0;
//Make sure that we have a Target Element
if( TargetPanel == null ) {
throw new Exception( "No Target Element specified for the animation. (NavMenu Animations)" );
}
//Make sure the Min Width and Height are Set
TargetPanel.MinWidth = ( Width > 0 ) ? Width : TargetPanel.MinWidth;
//Check if the MaxWidth has been set
if( Double.IsInfinity( TargetPanel.MaxWidth ) ) {
//Loop through the Children and Get Width
foreach( UIElement elem in TargetPanel.Children ) {
//Update the Max Width of the Panel
maxWidth += elem.RenderSize.Width;
}
//Check if we found a MaxWidth. if not return
if( maxWidth == 0 )
return;
//Assign the new MaxWidth
TargetPanel.MaxWidth = maxWidth;
}
//Check if Width is at Minumum (Shrunk)
if( TargetPanel.Width == TargetPanel.MinWidth ) {
TargetPanel.BeginAnimation( FrameworkElement.WidthProperty, new DoubleAnimation( TargetPanel.MaxWidth, TimeSpan.FromSeconds( Seconds ) ) );
}
else {
TargetPanel.BeginAnimation( FrameworkElement.WidthProperty, new DoubleAnimation( TargetPanel.MinWidth, TimeSpan.FromSeconds( Seconds ) ) );
}
}
}
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:Happ.UI.Controls">
<Style TargetType="{x:Type local:NavMenu}">
<Setter Property="Width" Value="50"/>
<Setter Property="Padding" Value="10" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel x:Name="navMenu" Orientation="Vertical">
</StackPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:NavMenu}">
<Border Background="{TemplateBinding Background}"
BorderBrush="Transparent"
Padding="{TemplateBinding Padding}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Viewbox Grid.Row="0"
HorizontalAlignment="Left" VerticalAlignment="Top"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:NavMenu}}, Path=IconSize}"
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:NavMenu}}, Path=IconSize}">
<ToggleButton x:Name="btnMenu" Grid.Row="0"
Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:NavMenu}}, Path=MenuIcon}"
Background="Transparent"
Padding="0"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:NavMenu}}, Path=IsExpanded}">
</ToggleButton>
</Viewbox>
<ItemsPresenter Grid.Row="1" MinWidth="50">
<i:Interaction.Triggers>
<i:EventTrigger SourceName="btnMenu" EventName="Click" >
<local:NavMenuAnimations TargetPanel="{Binding ElementName=navMenu}" Technique="ExpandWidth" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ItemsPresenter>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
public class NavMenu : Menu
{
static NavMenu()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NavMenu), new FrameworkPropertyMetadata(typeof(NavMenu)));
}
public bool IsExpanded
{
get
{
return (bool)GetValue(IsExpandedProperty);
}
set
{
SetValue(IsExpandedProperty, value);
}
}
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register("IsExpanded", typeof(bool), typeof(NavMenu), new PropertyMetadata(true));
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
System.Windows.Controls.Primitives.ToggleButton btnMenu = Template.FindName("btnMenu", this) as System.Windows.Controls.Primitives.ToggleButton;
TimeSpan tsTime = new TimeSpan(0, 0, 0, 0, 500);
DoubleAnimation mnuAnim = new DoubleAnimation(0.0, MinWidth, tsTime);
btnMenu.BeginAnimation(WidthProperty, mnuAnim);
}
}