WPF 实现抽屉菜单
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF 实现抽屉菜单相关的知识,希望对你有一定的参考价值。
分享一个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<DrawerMenuItem>), 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<DrawerMenuItem> Content
get => (List<DrawerMenuItem>)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<DrawerMenuItem>();
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>
<!-- Fill Brushes -->
<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"/>
<!-- Border Brushes -->
<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
/// <summary>
/// Win10MenuExample.xaml 的交互逻辑
/// </summary>
public partial class DrawerMenuExample : UserControl
private List<Uri> _uriList = new List<Uri>()
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">Home</Run>
<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>
参考①[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
以上是关于WPF 实现抽屉菜单的主要内容,如果未能解决你的问题,请参考以下文章