分享一个WPF 实现抽屉菜单
抽屉菜单
作者:WPFDevelopersOrg
原文链接:https://github.com/WPFDevelopersOrg/WPFDevelopers
框架使用大于等于.NET40;
Visual Studio 2022;
项目使用 MIT 开源许可协议;
更多效果可以通过GitHub[1]|码云[2]下载代码;
由于在WPF中没有现成的类似UWP的抽屉菜单,所以我们自己实现一个。
1) DrawerMenu.cs 代码如下。
- using System.Collections.Generic;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Media;
-
- namespace WPFDevelopers.Controls
- {
- public class DrawerMenu : ContentControl
- {
- public new static readonly DependencyProperty ContentProperty =
- DependencyProperty.Register("Content", typeof(List
), typeof(DrawerMenu), - new FrameworkPropertyMetadata(null));
-
- public static readonly DependencyProperty IsOpenProperty =
- DependencyProperty.Register("IsOpen", typeof(bool), typeof(DrawerMenu), new PropertyMetadata(true));
-
- public static readonly DependencyProperty MenuIconColorProperty =
- DependencyProperty.Register("MenuIconColor", typeof(Brush), typeof(DrawerMenu),
- new PropertyMetadata(Brushes.White));
-
- public static readonly DependencyProperty SelectionIndicatorColorProperty =
- DependencyProperty.Register("SelectionIndicatorColor", typeof(Brush), typeof(DrawerMenu),
- new PropertyMetadata(DrawingContextHelper.Brush));
-
- public static readonly DependencyProperty MenuItemForegroundProperty =
- DependencyProperty.Register("MenuItemForeground", typeof(Brush), typeof(DrawerMenu),
- new PropertyMetadata(Brushes.Transparent));
-
- static DrawerMenu()
- {
- DefaultStyleKeyProperty.OverrideMetadata(typeof(DrawerMenu),
- new FrameworkPropertyMetadata(typeof(DrawerMenu)));
- }
-
- public new List
Content - {
- get => (List
)GetValue(ContentProperty); - set => SetValue(ContentProperty, value);
- }
-
- public bool IsOpen
- {
- get => (bool)GetValue(IsOpenProperty);
- set => SetValue(IsOpenProperty, value);
- }
-
-
- public Brush MenuIconColor
- {
- get => (Brush)GetValue(MenuIconColorProperty);
- set => SetValue(MenuIconColorProperty, value);
- }
-
-
- public Brush SelectionIndicatorColor
- {
- get => (Brush)GetValue(SelectionIndicatorColorProperty);
- set => SetValue(SelectionIndicatorColorProperty, value);
- }
-
- public Brush MenuItemForeground
- {
- get => (Brush)GetValue(MenuItemForegroundProperty);
- set => SetValue(MenuItemForegroundProperty, value);
- }
-
- public override void BeginInit()
- {
- Content = new List
(); - base.BeginInit();
- }
- }
- }
2) DrawerMenuItem.cs 代码如下。
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Input;
- using System.Windows.Media;
-
- namespace WPFDevelopers.Controls
- {
- public class DrawerMenuItem : ListBoxItem
- {
- public static readonly DependencyProperty TextProperty =
- DependencyProperty.Register("Text", typeof(string), typeof(DrawerMenuItem),
- new PropertyMetadata(string.Empty));
-
- public static readonly DependencyProperty IconProperty =
- DependencyProperty.Register("Icon", typeof(ImageSource), typeof(DrawerMenuItem),
- new PropertyMetadata(null));
-
- public static readonly DependencyProperty SelectionIndicatorColorProperty =
- DependencyProperty.Register("SelectionIndicatorColor", typeof(Brush), typeof(DrawerMenuItem),
- new PropertyMetadata(DrawingContextHelper.Brush));
-
- public static readonly DependencyProperty SelectionCommandProperty =
- DependencyProperty.Register("SelectionCommand", typeof(ICommand), typeof(DrawerMenuItem),
- new PropertyMetadata(null));
-
- static DrawerMenuItem()
- {
- DefaultStyleKeyProperty.OverrideMetadata(typeof(DrawerMenuItem),
- new FrameworkPropertyMetadata(typeof(DrawerMenuItem)));
- }
-
- public string Text
- {
- get => (string)GetValue(TextProperty);
- set => SetValue(TextProperty, value);
- }
-
-
- public ImageSource Icon
- {
- get => (ImageSource)GetValue(IconProperty);
- set => SetValue(IconProperty, value);
- }
-
- public Brush SelectionIndicatorColor
- {
- get => (Brush)GetValue(SelectionIndicatorColorProperty);
- set => SetValue(SelectionIndicatorColorProperty, value);
- }
-
- public ICommand SelectionCommand
- {
- get => (ICommand)GetValue(SelectionCommandProperty);
- set => SetValue(SelectionCommandProperty, value);
- }
- }
- }
3) DrawerMenu.xaml 代码如下。
- <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
- xmlns:controls="clr-namespace:WPFDevelopers.Controls">
- <ResourceDictionary.MergedDictionaries>
- <ResourceDictionary Source="Basic/ControlBasic.xaml"/>
- ResourceDictionary.MergedDictionaries>
-
- <Style x:Key="DrawerMenuToggleButton" TargetType="ToggleButton" BasedOn="{StaticResource ControlBasicStyle}">
- <Setter Property="IsChecked" Value="False"/>
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type ToggleButton}">
- <Grid Background="{TemplateBinding Background}">
- <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
- Grid>
- ControlTemplate>
- Setter.Value>
- Setter>
- <Style.Triggers>
- <Trigger Property="IsMouseOver" Value="True">
- <Setter Property="Opacity" Value="0.8" />
- <Setter Property="Cursor" Value="Hand" />
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type ToggleButton}">
- <Border
- Background="{TemplateBinding Background}"
- BorderBrush="Black"
- BorderThickness="1">
- <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
- Border>
- ControlTemplate>
- Setter.Value>
- Setter>
- Trigger>
- Style.Triggers>
- Style>
- <Style x:Key="DrawerMenuListBox" TargetType="ListBox" BasedOn="{StaticResource ControlBasicStyle}">
- <Setter Property="Background" Value="Transparent" />
- <Setter Property="BorderBrush" Value="Transparent" />
- <Setter Property="BorderThickness" Value="0"/>
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate>
- <ScrollViewer>
- <ItemsPresenter Margin="0" />
- ScrollViewer>
- ControlTemplate>
- Setter.Value>
- Setter>
- Style>
- <Style x:Key="ButtonFocusVisual">
- <Setter Property="Control.Template">
- <Setter.Value>
- <ControlTemplate>
- <Border>
- <Rectangle
- Margin="2"
- StrokeThickness="1"
- Stroke="#60000000"
- StrokeDashArray="1 2"/>
- Border>
- ControlTemplate>
- Setter.Value>
- Setter>
- Style>
-
-
-
- <SolidColorBrush x:Key="NormalBrush" Color="Transparent" po:Freeze="True"/>
- <SolidColorBrush x:Key="DarkBrush" Color="#ddd" po:Freeze="True"/>
- <SolidColorBrush x:Key="PressedBrush" Color="#80FFFFFF" po:Freeze="True"/>
- <SolidColorBrush x:Key="DisabledForegroundBrush" Color="Transparent" po:Freeze="True"/>
- <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="Transparent" po:Freeze="True"/>
-
-
-
- <SolidColorBrush x:Key="NormalBorderBrush" Color="Transparent" po:Freeze="True"/>
- <SolidColorBrush x:Key="PressedBorderBrush" Color="Transparent" po:Freeze="True"/>
- <SolidColorBrush x:Key="DefaultedBorderBrush" Color="Transparent" po:Freeze="True"/>
- <SolidColorBrush x:Key="DisabledBorderBrush" Color="Transparent" po:Freeze="True"/>
-
-
- <Style x:Key="DrawerMenuItemButtonStyle" TargetType="Button" BasedOn="{StaticResource ControlBasicStyle}">
- <Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
- <Setter Property="MinHeight" Value="23"/>
- <Setter Property="MinWidth" Value="75"/>
- <Setter Property="Cursor" Value="Hand" />
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="Button">
- <Border
- x:Name="Border"
- CornerRadius="0"
- BorderThickness="0"
- Background="Transparent"
- BorderBrush="Transparent">
- <ContentPresenter
- HorizontalAlignment="Stretch"
- VerticalAlignment="Stretch"
- RecognizesAccessKey="True"/>
- Border>
- <ControlTemplate.Triggers>
- <Trigger Property="IsKeyboardFocused" Value="true">
- <Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource DefaultedBorderBrush}" />
- Trigger>
- <Trigger Property="IsDefaulted" Value="true">
- <Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource DefaultedBorderBrush}" />
- Trigger>
- <Trigger Property="IsMouseOver" Value="true">
- <Setter TargetName="Border" Property="Background" Value="{DynamicResource DarkBrush}" />
- Trigger>
- <Trigger Property="IsPressed" Value="true">
- <Setter TargetName="Border" Property="Background" Value="{DynamicResource PressedBrush}" />
- <Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource PressedBorderBrush}" />
- Trigger>
- <Trigger Property="IsEnabled" Value="false">
- <Setter TargetName="Border" Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" />
- <Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" />
- <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
- Trigger>
- ControlTemplate.Triggers>
- ControlTemplate>
- Setter.Value>
- Setter>
- Style>
-
-
- <Style TargetType="controls:DrawerMenuItem">
- <Setter Property="HorizontalContentAlignment" Value="Stretch" />
- <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:DrawerMenu}}, Path=MenuItemForeground}"/>
- <Setter Property="SelectionIndicatorColor" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:DrawerMenu}}, Path=SelectionIndicatorColor}"/>
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="controls:DrawerMenuItem">
- <Button x:Name="PART_Button" Height="44"
- Command="{TemplateBinding SelectionCommand}"
- ToolTip="{TemplateBinding Text}"
- HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
- Style="{StaticResource DrawerMenuItemButtonStyle}">
- <Grid>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="5"/>
- <ColumnDefinition/>
- Grid.ColumnDefinitions>
- <Grid Grid.ColumnSpan="2">
- <Grid Width="300">
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="45"/>
- <ColumnDefinition/>
- Grid.ColumnDefinitions>
- <Image Grid.Column="0" Source="{TemplateBinding Icon}" Margin="10,5,5,5"/>
- <TextBlock Text="{TemplateBinding Text}" Grid.Column="1"
- Margin="10,0,0,0" HorizontalAlignment="Left"
- VerticalAlignment="Center"
- FontSize="{StaticResource TitleFontSize}"
- Foreground="{TemplateBinding Foreground}"
- TextWrapping="Wrap"/>
- Grid>
- Grid>
- <Grid Name="PART_ItemSelectedIndicator"
- Grid.Column="0"
- Background="{TemplateBinding SelectionIndicatorColor}"
- Visibility="Collapsed" />
- Grid>
- Button>
- <ControlTemplate.Triggers>
- <Trigger Property="IsSelected" Value="True">
- <Setter TargetName="PART_ItemSelectedIndicator" Property="Visibility" Value="Visible" />
- Trigger>
- <Trigger SourceName="PART_Button" Property="IsPressed" Value="True">
- <Trigger.ExitActions>
- <BeginStoryboard>
- <Storyboard>
- <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected">
- <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True" />
- BooleanAnimationUsingKeyFrames>
- Storyboard>
- BeginStoryboard>
- Trigger.ExitActions>
- Trigger>
- ControlTemplate.Triggers>
- ControlTemplate>
- Setter.Value>
- Setter>
- Style>
-
- <Style TargetType="controls:DrawerMenu">
- <Setter Property="Width" Value="50"/>
- <Setter Property="Visibility" Value="Visible"/>
- <Setter Property="IsOpen" Value="True"/>
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="controls:DrawerMenu">
- <Grid Background="{TemplateBinding Background}">
- <ToggleButton HorizontalAlignment="Left" Background="#333"
- VerticalAlignment="Top" Height="40" Width="50"
- IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:DrawerMenu}}, Path=IsOpen}"
- Style="{StaticResource DrawerMenuToggleButton}">
- <Path HorizontalAlignment="Center"
- VerticalAlignment="Center"
- Stretch="Uniform" Width="20"
- Fill="{TemplateBinding MenuIconColor}"
- Data="{StaticResource PathMenu}"/>
- ToggleButton>
- <ListBox ItemsSource="{TemplateBinding Content}"
- HorizontalAlignment="Left" Margin="0,40,0,0"
- VerticalAlignment="Top"
- Style="{StaticResource DrawerMenuListBox}"
- ScrollViewer.HorizontalScrollBarVisibility="Disabled"
- SelectedIndex="0"/>
- Grid>
- ControlTemplate>
- Setter.Value>
- Setter>
- <Style.Triggers>
- <Trigger Property="IsOpen" Value="False">
- <Trigger.EnterActions>
- <BeginStoryboard>
- <Storyboard>
- <DoubleAnimation
- Storyboard.TargetProperty="Width"
- To="180"
- Duration="0:0:0.2"/>
- Storyboard>
- BeginStoryboard>
- Trigger.EnterActions>
- <Trigger.ExitActions>
- <BeginStoryboard>
- <Storyboard>
- <DoubleAnimation
- Storyboard.TargetProperty="Width"
- To="50"
- Duration="0:0:0.2"/>
- Storyboard>
- BeginStoryboard>
- Trigger.ExitActions>
- Trigger>
- Style.Triggers>
- Style>
- ResourceDictionary>
4) DrawerMenuExample.xaml 代码如下。
- <UserControl x:Class="WPFDevelopers.Samples.ExampleViews.DrawerMenu.DrawerMenuExample"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews.DrawerMenu"
- xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"
- mc:Ignorable="d"
- d:DesignHeight="450" d:DesignWidth="800">
- <Grid Background="#FF7B7BFF">
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="Auto"/>
- <ColumnDefinition/>
- Grid.ColumnDefinitions>
- <wpfdev:DrawerMenu Background="#eee"
- SelectionIndicatorColor="{StaticResource PrimaryPressedSolidColorBrush}"
- MenuItemForeground="{StaticResource BlackSolidColorBrush}" HorizontalAlignment="Left">
- <wpfdev:DrawerMenu.Content>
- <wpfdev:DrawerMenuItem Icon="pack://application:,,,/Images/CircularMenu/2.png" Text="主页"
- SelectionCommand="{Binding HomeCommand,RelativeSource={RelativeSource AncestorType=local:DrawerMenuExample}}"/>
- <wpfdev:DrawerMenuItem Icon="pack://application:,,,/Images/CircularMenu/4.png" Text="Edge"
- SelectionCommand="{Binding EdgeCommand,RelativeSource={RelativeSource AncestorType=local:DrawerMenuExample}}"/>
- <wpfdev:DrawerMenuItem Icon="pack://application:,,,/Images/CircularMenu/1.png" Text="云盘"
- SelectionCommand="{Binding CloudCommand,RelativeSource={RelativeSource AncestorType=local:DrawerMenuExample}}"/>
- <wpfdev:DrawerMenuItem Icon="pack://application:,,,/Images/CircularMenu/8.png" Text="邮件"
- SelectionCommand="{Binding MailCommand,RelativeSource={RelativeSource AncestorType=local:DrawerMenuExample}}"/>
- <wpfdev:DrawerMenuItem Icon="pack://application:,,,/Images/CircularMenu/6.png" Text="视频"
- SelectionCommand="{Binding VideoCommand,RelativeSource={RelativeSource AncestorType=local:DrawerMenuExample}}"/>
- wpfdev:DrawerMenu.Content>
- wpfdev:DrawerMenu>
- <Frame Name="myFrame" Grid.Column="1" Margin="0,40,0,0"
- NavigationUIVisibility="Hidden">Frame>
- Grid>
- UserControl>
-
5) DrawerMenuExample.xaml.cs 代码如下。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Input;
- using System.Windows.Media;
- using WPFDevelopers.Samples.Helpers;
-
- namespace WPFDevelopers.Samples.ExampleViews.DrawerMenu
- {
- ///
- /// Win10MenuExample.xaml 的交互逻辑
- ///
- public partial class DrawerMenuExample : UserControl
- {
- private List
_uriList = new List() - {
- new Uri("ExampleViews/DrawerMenu/HomePage.xaml",UriKind.Relative),
- new Uri("ExampleViews/DrawerMenu/EdgePage.xaml",UriKind.Relative),
- };
- public DrawerMenuExample()
- {
- InitializeComponent();
- myFrame.Navigate(_uriList[0]);
- }
-
- public ICommand HomeCommand => new RelayCommand(obj =>
- {
- myFrame.Navigate(_uriList[0]);
- });
- public ICommand EdgeCommand => new RelayCommand(obj =>
- {
- myFrame.Navigate(_uriList[1]);
- });
- public ICommand CloudCommand => new RelayCommand(obj =>
- {
- WPFDevelopers.Minimal.Controls.MessageBox.Show("点击了云盘","提示");
- });
- public ICommand MailCommand => new RelayCommand(obj =>
- {
- WPFDevelopers.Minimal.Controls.MessageBox.Show("点击了邮件","提示");
- });
- public ICommand VideoCommand => new RelayCommand(obj =>
- {
- WPFDevelopers.Minimal.Controls.MessageBox.Show("点击了视频","提示");
- });
- }
- }
6) HomePage.xaml.cs 代码如下。
- <Page x:Class="WPFDevelopers.Samples.ExampleViews.DrawerMenu.HomePage"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews.DrawerMenu"
- mc:Ignorable="d"
- d:DesignHeight="450" d:DesignWidth="800"
- Title="HomePage" Background="{StaticResource PrimaryTextSolidColorBrush}">
-
- <Grid>
- <TextBlock VerticalAlignment="Center"
- HorizontalAlignment="Center"
- Width="400" TextAlignment="Center"
- TextWrapping="Wrap"
- Margin="0,0,0,40"
- FontSize="{StaticResource NormalFontSize}">
- <Run Foreground="White">HomeRun>
- <Run Text="微信公众号 WPFDevelopers" FontSize="40"
- Foreground="#A9CC32" FontWeight="Bold">Run>
- <LineBreak/>
- <Hyperlink NavigateUri="https://github.com/WPFDevelopersOrg/WPFDevelopers.git"
- RequestNavigate="GithubHyperlink_RequestNavigate"> Github 源代码Hyperlink>
- <Run/>
- <Run/>
- <Run/>
- <Hyperlink NavigateUri="https://gitee.com/yanjinhua/WPFDevelopers.git"
- RequestNavigate="GiteeHyperlink_RequestNavigate"> 码云源代码Hyperlink>
- TextBlock>
- Grid>
- Page>
-
7) EdgePage.xaml.cs 代码如下。
- <Page x:Class="WPFDevelopers.Samples.ExampleViews.DrawerMenu.EdgePage"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews.DrawerMenu"
- mc:Ignorable="d"
- d:DesignHeight="450" d:DesignWidth="800"
- Title="EdgePage" Background="{DynamicResource PrimaryPressedSolidColorBrush}">
-
- <Grid>
- <StackPanel VerticalAlignment="Center"
- Margin="0,0,0,40">
- <Image Source="pack://application:,,,/Images/CircularMenu/4.png" Stretch="Uniform"
- Width="50"/>
- <TextBlock VerticalAlignment="Center"
- HorizontalAlignment="Center"
- TextAlignment="Center"
- Foreground="White"
- Text="即将跳转至Edge浏览器"
- FontSize="{StaticResource TitleFontSize}"
- Padding="0,10">
- TextBlock>
- StackPanel>
- Grid>
- Page>
WPF开发者
,赞18
参考①[3]参考②[4]
[1]
GitHub: https://github.com/WPFDevelopersOrg/WPFDevelopers
[2]
码云: https://github.com/WPFDevelopersOrg/WPFDevelopers
[3]
参考①: https://github.com/WPFDevelopersOrg/WPFDevelopers/tree/master/src/WPFDevelopers/Controls/DrawerMenu
[4]
参考②: https://gitee.com/WPFDevelopersOrg/WPFDevelopers/tree/master/src/WPFDevelopers/Controls/DrawerMenu