WPF实现抽屉菜单效果的示例代码

2022-11-13 14:11:52 示例 菜单 抽屉

WPF 实现抽屉菜单

  • 框架使用大于等于.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>

效果图

到此这篇关于WPF实现抽屉菜单效果的示例代码的文章就介绍到这了,更多相关WPF抽屉菜单内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章