阅读量:4
WPF实现一个带旋转动画的菜单栏
一、创建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>