WPF实现一个带旋转动画的菜单栏
- 一、创建WPF项目及文件
- 1、创建项目
- 2、创建文件夹及文件
- 3、添加引用
- 二、代码实现
- 2.ControlAttachProperty类
一、创建WPF项目及文件
1、创建项目
打开VS2022,创建一个WPF项目,如下所示
2、创建文件夹及文件
创建资源文件夹,添加字体图标文件,添加 Menu样式文件,如下所示
创建公共附加属性用控件类库,并创建对应的 ControlAttachProperty 类文件如下:
3、添加引用
右键 Menu_WPF 添加引用,将类库引用到当前项目
二、代码实现
2.ControlAttachProperty类
代码如下(示例):
using Microsoft.Win32;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using CheckBox = System.Windows.Controls.CheckBox;
using ComboBox = System.Windows.Controls.ComboBox;
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
using RadioButton = System.Windows.Controls.RadioButton;
using RichTextBox = System.Windows.Controls.RichTextBox;
using TextBox = System.Windows.Controls.TextBox;
namespace Common.UserControl.Extession
{
/// <summary>
/// 公共附加属性
/// </summary>
public static class ControlAttachProperty
{
#region FocusBorderBrush 焦点边框色,输入控件
public static readonly DependencyProperty FocusBorderBrushProperty = DependencyProperty.RegisterAttached(
"FocusBorderBrush", typeof(Brush), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null));
public static void SetFocusBorderBrush(DependencyObject element, Brush value)
{
element.SetValue(FocusBorderBrushProperty, value);
}
public static Brush GetFocusBorderBrush(DependencyObject element)
{
return (Brush)element.GetValue(FocusBorderBrushProperty);
}
#endregion
#region MouseOverBorderBrush 鼠标进入边框色,输入控件
public static readonly DependencyProperty MouseOverBorderBrushProperty =
DependencyProperty.RegisterAttached("MouseOverBorderBrush", typeof(Brush), typeof(ControlAttachProperty),
new FrameworkPropertyMetadata(Brushes.Transparent,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// Sets the brush used to draw the mouse over brush.
/// </summary>
public static void SetMouseOverBorderBrush(DependencyObject obj, Brush value)
{
obj.SetValue(MouseOverBorderBrushProperty, value);
}
/// <summary>
/// Gets the brush used to draw the mouse over brush.
/// </summary>
[AttachedPropertyBrowsableForType(typeof(TextBox))]
[AttachedPropertyBrowsableForType(typeof(CheckBox))]
[AttachedPropertyBrowsableForType(typeof(RadioButton))]
[AttachedPropertyBrowsableForType(typeof(DatePicker))]
[AttachedPropertyBrowsableForType(typeof(ComboBox))]
[AttachedPropertyBrowsableForType(typeof(RichTextBox))]
public static Brush GetMouseOverBorderBrush(DependencyObject obj)
{
return (Brush)obj.GetValue(MouseOverBorderBrushProperty);
}
#endregion
#region AttachContentProperty 附加组件模板
/// <summary>
/// 附加组件模板
/// </summary>
public static readonly DependencyProperty AttachContentProperty = DependencyProperty.RegisterAttached(
"AttachContent", typeof(ControlTemplate), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null));
public static ControlTemplate GetAttachContent(DependencyObject d)
{
return (ControlTemplate)d.GetValue(AttachContentProperty);
}
public static void SetAttachContent(DependencyObject obj, ControlTemplate value)
{
obj.SetValue(AttachContentProperty, value);
}
#endregion
#region WatermarkProperty 水印
/// <summary>
/// 水印
/// </summary>
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
"Watermark", typeof(string), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(""));
public static string GetWatermark(DependencyObject d)
{
return (string)d.GetValue(WatermarkProperty);
}
public static void SetWatermark(DependencyObject obj, string value)
{
obj.SetValue(WatermarkProperty, value);
}
#endregion
#region FIconProperty 字体图标
/// <summary>
/// 字体图标
/// </summary>
public static readonly DependencyProperty FIconProperty = DependencyProperty.RegisterAttached(
"FIcon", typeof(string), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(""));
public static string GetFIcon(DependencyObject d)
{
return (string)d.GetValue(FIconProperty);
}
public static void SetFIcon(DependencyObject obj, string value)
{
obj.SetValue(FIconProperty, value);
}
#endregion
#region FIconSizeProperty 字体图标大小
/// <summary>
/// 字体图标
/// </summary>
public static readonly DependencyProperty FIconSizeProperty = DependencyProperty.RegisterAttached(
"FIconSize", typeof(double), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(12D));
public static double GetFIconSize(DependencyObject d)
{
return (double)d.GetValue(FIconSizeProperty);
}
public static void SetFIconSize(DependencyObject obj, double value)
{
obj.SetValue(FIconSizeProperty, value);
}
#endregion
#region FIconMarginProperty 字体图标边距
/// <summary>
/// 字体图标
/// </summary>
public static readonly DependencyProperty FIconMarginProperty = DependencyProperty.RegisterAttached(
"FIconMargin", typeof(Thickness), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null));
public static Thickness GetFIconMargin(DependencyObject d)
{
return (Thickness)d.GetValue(FIconMarginProperty);
}
public static void SetFIconMargin(DependencyObject obj, Thickness value)
{
obj.SetValue(FIconMarginProperty, value);
}
#endregion
#region AllowsAnimationProperty 启用旋转动画
/// <summary>
/// 启用旋转动画
/// </summary>
public static readonly DependencyProperty AllowsAnimationProperty = DependencyProperty.RegisterAttached("AllowsAnimation"
, typeof(bool), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(false, AllowsAnimationChanged));
public static bool GetAllowsAnimation(DependencyObject d)
{
return (bool)d.GetValue(AllowsAnimationProperty);
}
public static void SetAllowsAnimation(DependencyObject obj, bool value)
{
obj.SetValue(AllowsAnimationProperty, value);
}
/// <summary>
/// 旋转动画刻度
/// </summary>
private static DoubleAnimation RotateAnimation = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(200)));
/// <summary>
/// 绑定动画事件
/// </summary>
private static void AllowsAnimationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uc = d as FrameworkElement;
if (uc == null) return;
if (uc.RenderTransformOrigin == new Point(0, 0))
{
uc.RenderTransformOrigin = new Point(0.5, 0.5);
RotateTransform trans = new RotateTransform(0);
uc.RenderTransform = trans;
}
var value = (bool)e.NewValue;
if (value)
{
RotateAnimation.To = 180;
uc.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, RotateAnimation);
}
else
{
RotateAnimation.To = 0;
uc.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, RotateAnimation);
}
}
#endregion
#region CornerRadiusProperty Border圆角
/// <summary>
/// Border圆角
/// </summary>
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached(
"CornerRadius", typeof(CornerRadius), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null));
public static CornerRadius GetCornerRadius(DependencyObject d)
{
return (CornerRadius)d.GetValue(CornerRadiusProperty);
}
public static void SetCornerRadius(DependencyObject obj, CornerRadius value)
{
obj.SetValue(CornerRadiusProperty, value);
}
#endregion
#region LabelProperty TextBox的头部Label
/// <summary>
/// TextBox的头部Label
/// </summary>
public static readonly DependencyProperty LabelProperty = DependencyProperty.RegisterAttached(
"Label", typeof(string), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null));
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static string GetLabel(DependencyObject d)
{
return (string)d.GetValue(LabelProperty);
}
public static void SetLabel(DependencyObject obj, string value)
{
obj.SetValue(LabelProperty, value);
}
#endregion
#region LabelTemplateProperty TextBox的头部Label模板
/// <summary>
/// TextBox的头部Label模板
/// </summary>
public static readonly DependencyProperty LabelTemplateProperty = DependencyProperty.RegisterAttached(
"LabelTemplate", typeof(ControlTemplate), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null));
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static ControlTemplate GetLabelTemplate(DependencyObject d)
{
return (ControlTemplate)d.GetValue(LabelTemplateProperty);
}
public static void SetLabelTemplate(DependencyObject obj, ControlTemplate value)
{
obj.SetValue(LabelTemplateProperty, value);
}
#endregion
}
}
Menu样式文件实现
<!--背景透明的HeaderItem样式,带旋转动画-->
<Style x:Key="TransparentHeaderMenuItem" TargetType="{x:Type MenuItem}">
<Setter Property="BorderBrush" Value="{StaticResource MenuBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="{StaticResource MenuBackground}"/>
<Setter Property="Foreground" Value="{StaticResource TextForeground}"/>
<Setter Property="FontSize" Value="{StaticResource FontSize}"/>
<Setter Property="Margin" Value="2,0,2,0"/>
<Setter Property="Height" Value="30"/>
<Setter Property="local:ControlAttachProperty.FIconSize" Value="18"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Grid x:Name="Bg" VerticalAlignment="{TemplateBinding VerticalAlignment}">
<StackPanel Orientation="Horizontal" x:Name="border" VerticalAlignment="Center" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}">
<!--icon-->
<TextBlock x:Name="PART_Icon" Text="{TemplateBinding Icon}" Foreground="{TemplateBinding Foreground}"
local:ControlAttachProperty.AllowsAnimation="{Binding IsSubmenuOpen,RelativeSource={RelativeSource TemplatedParent}}"
FontSize="{TemplateBinding local:ControlAttachProperty.FIconSize}" Style="{StaticResource FIcon}" />
<TextBlock x:Name="txtHeader" Margin="5 0 0 0" FontSize="{TemplateBinding FontSize}" HorizontalAlignment="Stretch"
Text="{TemplateBinding Header}" VerticalAlignment="Center" Grid.Column="1" Foreground="{TemplateBinding Foreground}"/>
</StackPanel>
<!--弹出子集菜单容器-->
<Popup x:Name="SubMenuPopup" AllowsTransparency="true" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}"
Placement="Bottom" Focusable="false" VerticalOffset="0"
PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}">
<Border Background="{TemplateBinding Background}" CornerRadius="0" Margin="5" Effect="{StaticResource DefaultDropShadow}"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid x:Name="SubMenu" Grid.IsSharedSizeScope="True">
<StackPanel Margin="10" IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Grid>
</Border>
</Popup>
</Grid>
<!--触发器-->
<ControlTemplate.Triggers>
<!--高亮状态-->
<Trigger Property="IsHighlighted" Value="true">
<Setter Property="Foreground" Value="{StaticResource MouseOverForeground}"></Setter>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Foreground" Value="{StaticResource PressedForeground}"></Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="border" Value="{StaticResource DisableOpacity}" Property="Opacity"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow样式实现
<Menu Width="80" Height="30" Margin="3" Background="Transparent" >
<MenuItem Header="展开菜单" Style="{StaticResource TransparentHeaderMenuItem}" Padding="0" Icon="" >
<MenuItem Style="{StaticResource TransparentHeaderMenuItem}" Icon="" Header="设置" />
<MenuItem Style="{StaticResource TransparentHeaderMenuItem}" Icon="" Header="插件管理" />
<MenuItem Style="{StaticResource TransparentHeaderMenuItem}" Icon="" Header="用户管理" />
<MenuItem Style="{StaticResource TransparentHeaderMenuItem}" Icon="" Header="修改密码" />
<MenuItem Style="{StaticResource TransparentHeaderMenuItem}" Icon="" Header="在线更新" />
<Separator Background="SpringGreen" Style="{StaticResource HorizontalSeparatorStyle}"/>
<MenuItem Style="{StaticResource TransparentHeaderMenuItem}" Icon="" Header="问题反馈" />
<MenuItem Style="{StaticResource TransparentHeaderMenuItem}" Icon="" Header="技术支持" />
<MenuItem Style="{StaticResource TransparentHeaderMenuItem}" Icon="" Header="帮助" />
<MenuItem Style="{StaticResource TransparentHeaderMenuItem}" Icon="" Header="关于" />
</MenuItem>
</Menu>