【WPF】自定义下拉菜单控件DropDownButton的实现,下拉箭头旋转,ContextMenu、MenuItem的样式及FontAwesome字体图标的引用
DropDownButton控件自定义,ContextMenu、MenuItem的样式及FontAwesome字体图标的引用
效果如图,弹出收起动画等的效果还没做。
xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:FTools.Controls"> <Style TargetType="{x:Type local:DropDownButton}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:DropDownButton}"> <ControlTemplate.Resources> <Storyboard x:Key="Storyboard1"> <DoubleAnimation Storyboard.TargetName="img" Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(RotateTransform.Angle)" To="-180" Duration="0:0:0.2" FillBehavior="HoldEnd" /> </Storyboard> <Storyboard x:Key="Storyboard2"> <DoubleAnimation Storyboard.TargetName="img" Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(RotateTransform.Angle)" To="0" Duration="0:0:0.2" FillBehavior="HoldEnd" /> </Storyboard> </ControlTemplate.Resources> <Grid> <Border x:Name="bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5" Width="{TemplateBinding Width}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="8*"/> <ColumnDefinition Width="2*" MinWidth="20"/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0" /> <Path x:Name="Part_Arrow" Grid.Column="1" Data="M0,0 L4,4 8,0 Z" VerticalAlignment="Center" HorizontalAlignment="Center" Fill="{TemplateBinding Foreground}" Margin="0,0,5,0"> <Path.LayoutTransform> <RotateTransform Angle="0"/> </Path.LayoutTransform> </Path> </Grid> </Border> </Grid> <ControlTemplate.Triggers> <Trigger SourceName="bd" Property="IsMouseOver" Value="True"> <Setter TargetName="bd" Property="Background" Value="LightGray"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--FontAwesome字体下载及在WPF中的使用 https://fontawesome.com/ ##########下载 下载地址:https://fontawesome.com/download 下载内容:Free For Web ##########使用 双击**.ttf,进入安装页可以看到“字体名称……”,记住字体名称 将 **.ttf复制到wpf工程自定内资源目录,FontFamily属性值=资源目录/#字体名称,如"../Resources/#Font Awesome 6 Free Solid" Text的值为 FontAwesome Icon的十六进制 Unicode值,如 Text="" --> <Style x:Key="FontAwesome"> <Setter Property="TextElement.FontFamily" Value="../Resources/#Font Awesome 6 Free Solid" /> <Setter Property="TextBlock.Width" Value="20"></Setter> <Setter Property="TextBlock.Height" Value="20"></Setter> <Setter Property="TextBlock.TextAlignment" Value="Center"></Setter> <Setter Property="TextBlock.FontSize" Value="15"></Setter> </Style> <!--MenuItem样式,DropDownButton控件中嵌套ContextMenu中MenuItem引用此样式--> <Style x:Key="FTools.Controls.DropDownButton.MenuItemStyle" TargetType="{x:Type MenuItem}"> <!--<Setter Property="OverridesDefaultStyle" Value="True"/>--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type MenuItem}"> <Border x:Name="mi_Border" Width="{TemplateBinding Width}" Background="Transparent" CornerRadius="5"> <StackPanel Orientation="Horizontal" > <Border VerticalAlignment="Center" BorderBrush="{TemplateBinding Foreground }" BorderThickness="0,0,0.5,0"> <TextBlock x:Name="mi_Icon" Text="{TemplateBinding Icon}" Foreground="{TemplateBinding Foreground}" Style="{DynamicResource FontAwesome}" Margin="5"/> </Border> <ContentPresenter Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center" ContentSource= "Header" RecognizesAccessKey="True" /> </StackPanel> </Border> <ControlTemplate.Triggers> <Trigger SourceName="mi_Border" Property="IsMouseOver" Value="True"> <!--<Setter TargetName="mi_Icon" Property="Foreground" Value="Black"/>--> <Setter TargetName="mi_Border" Property="Background" Value="lightgray"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--ContextMenu样式,DropDownButton控件中嵌套ContextMenu引用此样式--> <Style x:Key="FTools.Controls.DropDownButton.ContextMenuStyle" TargetType="ContextMenu"> <Setter Property="SnapsToDevicePixels" Value="True" /> <Setter Property="OverridesDefaultStyle" Value="True" /> <!--<Setter Property="Grid.IsSharedSizeScope" Value="True" />--> <Setter Property="HasDropShadow" Value="True" /> <Setter Property="Foreground" Value="Black"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ContextMenu"> <Border Background="White" BorderBrush="DarkGray" BorderThickness="0.7" CornerRadius="5" Width="{TemplateBinding Width}"> <ItemsPresenter/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
C#代码:
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace FTools.Controls { [TemplatePart(Name = DropDownButton.part_Arrow, Type = typeof(Popup))] public class DropDownButton : ToggleButton { static DropDownButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DropDownButton), new FrameworkPropertyMetadata(typeof(DropDownButton))); } public DropDownButton() { Binding binding = new Binding("Menu.IsOpen"); binding.Source = this; this.SetBinding(IsCheckedProperty, binding); DataContextChanged += (sender, args) => { if (Menu != null) Menu.DataContext = DataContext; }; } public override void OnApplyTemplate() { base.OnApplyTemplate(); this.path=GetTemplateChild(part_Arrow) as Path; if (this.path != null && this.Menu!=null) this.Menu.Closed += this.Menu_Closed; if (this.path != null && this.Menu != null) this.Menu.Opened += this.Menu_Opened; } public const string part_Arrow = "Part_Arrow"; private Path path; public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text",typeof(string), typeof(DropDownButton)); public static readonly DependencyProperty MenuProperty = DependencyProperty.Register("Menu", typeof(ContextMenu), typeof(DropDownButton), new UIPropertyMetadata(null, OnMenuChanged)); public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } public ContextMenu Menu { get { return (ContextMenu)GetValue(MenuProperty); } set { SetValue(MenuProperty, value); } } private static void OnMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var dropDownButton = (DropDownButton)d; var contextMenu = (ContextMenu)e.NewValue; contextMenu.DataContext = dropDownButton.DataContext; } protected override void OnClick() { if (Menu != null) { Menu.PlacementTarget = this; Menu.Placement = PlacementMode.Bottom; Menu.IsOpen = true; Menu.Background = new System.Windows.Media.SolidColorBrush(Color.FromRgb(255,255,255)); } } private void Menu_Opened(object sender, EventArgs e) { RotateTransform rotateTransform = new RotateTransform(); this.path.LayoutTransform = rotateTransform; DoubleAnimation ani = new DoubleAnimation(); ani.From = 0; ani.To = -180; ani.Duration = TimeSpan.FromSeconds(0.5); Storyboard storyboard = new Storyboard(); storyboard.Children.Add(ani); Storyboard.SetTarget(ani, this.path); Storyboard.SetTargetProperty(ani, new PropertyPath("(FrameworkElement.LayoutTransform).(RotateTransform.Angle)")); storyboard.Begin(); } private void Menu_Closed(object sender, EventArgs e) { RotateTransform rotateTransform = new RotateTransform(); this.path.LayoutTransform = rotateTransform; DoubleAnimation ani = new DoubleAnimation { From = -180, To = 0, Duration = TimeSpan.FromSeconds(0.5) }; Storyboard storyboard = new Storyboard(); storyboard.Children.Add(ani); Storyboard.SetTarget(ani, this.path); Storyboard.SetTargetProperty(ani, new PropertyPath("(FrameworkElement.LayoutTransform).(RotateTransform.Angle)")); storyboard.Begin(); } } }
控件的使用Xaml
<FControls:DropDownButton Grid.Column="6" Background="White" Foreground="{DynamicResource foregroundColor}" FontFamily="宋体" FontSize="12" Width="80" Text="开始操作" HorizontalAlignment="Right" > <FControls:DropDownButton.Menu> <ContextMenu Style="{DynamicResource FTools.Controls.DropDownButton.ContextMenuStyle}" Width="Auto"> <MenuItem Style="{DynamicResource FTools.Controls.DropDownButton.MenuItemStyle}" x:Name="miPathClean" Header="复制文件/文件夹" Icon="" Foreground="{StaticResource foregroundColor}"/> <MenuItem Style="{DynamicResource FTools.Controls.DropDownButton.MenuItemStyle}" x:Name="miPathExport" Header="移动文件/文件夹" Icon="" Foreground="{StaticResource foregroundColor}"/> <MenuItem Style="{DynamicResource FTools.Controls.DropDownButton.MenuItemStyle}" x:Name="miPathInput" Header="重命名文件/文件夹" Icon="" Foreground="{StaticResource foregroundColor}"/> </ContextMenu> </FControls:DropDownButton.Menu> </FControls:DropDownButton>