WPF實現Win10漢堡選單

驚鏵發表於2021-10-18

WPF開發者QQ群: 340500857  | 微信群 -> 進入公眾號主頁 加入組織

前言

      有小夥伴提出需要實現Win10漢堡選單效果。      

 

 由於在WPF中沒有現成的類似UWP的漢堡選單,所以我們自己實現一個。

 

一、建立 Win10Menu.cs 選單繼承 ContentControl 程式碼如下。

  1、IsOpen :判定是否展開、收起 。

  2、Content :存放選單集合。

  3、SelectionIndicatorColor :選中選單狀態列顏色。                 

  4、MenuItemForeground:選單集字型顏色。

 

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace WPFDevelopers.Controls
{
    public class Win10Menu : ContentControl
    {
        public new List<Win10MenuItem> Content
        {
            get { return (List<Win10MenuItem>)GetValue(ContentProperty); }
            set { SetValue(ContentProperty, value); }
        }

        public new static readonly DependencyProperty ContentProperty =
            DependencyProperty.Register("Content", typeof(List<Win10MenuItem>), typeof(Win10Menu),new FrameworkPropertyMetadata(null));

        static Win10Menu()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Win10Menu), new FrameworkPropertyMetadata(typeof(Win10Menu)));
        }

        public override void BeginInit()
        {
            Content = new List<Win10MenuItem>();
            base.BeginInit();
        }

        public bool IsOpen
        {
            get { return (bool)GetValue(IsOpenProperty); }
            set
            {
                SetValue(IsOpenProperty, value);
            }
        }

        public static readonly DependencyProperty IsOpenProperty =
            DependencyProperty.Register("IsOpen", typeof(bool), typeof(Win10Menu), new PropertyMetadata(true));


        public System.Windows.Media.Brush MenuIconColor
        {
            get { return (System.Windows.Media.Brush)GetValue(MenuIconColorProperty); }
            set { SetValue(MenuIconColorProperty, value); }
        }

        public static readonly DependencyProperty MenuIconColorProperty =
            DependencyProperty.Register("MenuIconColor", typeof(System.Windows.Media.Brush), typeof(Win10Menu), new PropertyMetadata(Brushes.White));


        public Brush SelectionIndicatorColor
        {
            get { return (Brush)GetValue(SelectionIndicatorColorProperty); }
            set { SetValue(SelectionIndicatorColorProperty, value); }
        }

        public static readonly DependencyProperty SelectionIndicatorColorProperty =
            DependencyProperty.Register("SelectionIndicatorColor", typeof(Brush), typeof(Win10Menu), new PropertyMetadata(Brushes.Red));

        public Brush MenuItemForeground
        {
            get { return (Brush)GetValue(MenuItemForegroundProperty); }
            set { SetValue(MenuItemForegroundProperty, value); }
        }

        public static readonly DependencyProperty MenuItemForegroundProperty =
            DependencyProperty.Register("MenuItemForeground", typeof(Brush), typeof(Win10Menu), new PropertyMetadata(Brushes.Transparent));
    }
}

 

 

二、建立 Win10Menu.xaml 為 Win10Menu.cs 進行佈局 程式碼如下

 

Win10Menu.xaml 思路如下

1、ToggleButton :控制IsChecked動畫如下

 IsOpen = False:DoubleAnimation修改controls:Win10MenuWidth為 180。

 IsOpen = TrueDoubleAnimation修改controls:Win10MenuWidth為 50。

 2、ListBox:ItemsSource="{TemplateBinding Content}"展示選單集合。          

需注意如下 

ScrollViewer.HorizontalScrollBarVisibility="Disabled",不然當Win10MenuWidth 為50時會出現滾動條。

 

<Style TargetType="ToggleButton">
        <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 TargetType="ListBox">
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="BorderBrush" Value="Transparent" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <ScrollViewer>
                        <ItemsPresenter Margin="0" />
                    </ScrollViewer>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>
<Style TargetType="controls:Win10Menu">
        <Setter Property="Width" Value="50"/>
        <Setter Property="Visibility" Value="Visible"/>
        <Setter Property="IsOpen" Value="True"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="controls:Win10Menu">
                    <Grid Background="{TemplateBinding Background}">
                        <ToggleButton HorizontalAlignment="Left" Background="#333"
                                      VerticalAlignment="Top" Height="40" Width="50"
                                      IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:Win10Menu}}, Path=IsOpen}">
                            <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" 
                                 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>

 

三、建立 Win10MenuItem.cs 繼承 ListBoxItem 程式碼如下。

Win10MenuItem.cs 思路如下

1、Text 選單文字內容展示。

2、Icon 選單圖示為ImageSource型別。

3、SelectionIndicatorColor選中的側邊狀態顏色。

4、SelectionCommand 選中事件。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace WPFDevelopers.Controls
{
    public class Win10MenuItem : ListBoxItem
    {
        static Win10MenuItem()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Win10MenuItem), new FrameworkPropertyMetadata(typeof(Win10MenuItem)));
        }
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(Win10MenuItem), new PropertyMetadata(string.Empty));


        public ImageSource Icon
        {
            get { return (ImageSource)GetValue(IconProperty); }
            set { SetValue(IconProperty, value); }
        }

        public static readonly DependencyProperty IconProperty =
            DependencyProperty.Register("Icon", typeof(ImageSource), typeof(Win10MenuItem), new PropertyMetadata(null));

        public Brush SelectionIndicatorColor
        {
            get { return (Brush)GetValue(SelectionIndicatorColorProperty); }
            set { SetValue(SelectionIndicatorColorProperty, value); }
        }

        public static readonly DependencyProperty SelectionIndicatorColorProperty =
            DependencyProperty.Register("SelectionIndicatorColor", typeof(Brush), typeof(Win10MenuItem), new PropertyMetadata(Brushes.Blue));

        public ICommand SelectionCommand
        {
            get { return (ICommand)GetValue(SelectionCommandProperty); }
            set { SetValue(SelectionCommandProperty, value); }
        }

        public static readonly DependencyProperty SelectionCommandProperty =
            DependencyProperty.Register("SelectionCommand", typeof(ICommand), typeof(Win10MenuItem), new PropertyMetadata(null));
    }
}

 

四、建立 Win10MenuItem.xaml 為 Win10MenuItem.cs 進行佈局 程式碼如下 

<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" />
    <SolidColorBrush x:Key="DarkBrush" Color="#ddd" />
    <SolidColorBrush x:Key="PressedBrush" Color="#80FFFFFF" />
    <SolidColorBrush x:Key="DisabledForegroundBrush" Color="Transparent" />
    <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="Transparent" />

    <!-- Border Brushes -->

    <SolidColorBrush x:Key="NormalBorderBrush" Color="Transparent" />
    <SolidColorBrush x:Key="PressedBorderBrush" Color="Transparent" />
    <SolidColorBrush x:Key="DefaultedBorderBrush" Color="Transparent" />
    <SolidColorBrush x:Key="DisabledBorderBrush" Color="Transparent" />


    <Style TargetType="Button">
        <Setter Property="SnapsToDevicePixels" Value="true"/>
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <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="{StaticResource NormalBrush}"
          BorderBrush="{StaticResource NormalBorderBrush}">
                        <ContentPresenter 
            HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch"
            RecognizesAccessKey="True"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsKeyboardFocused" Value="true">
                            <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DefaultedBorderBrush}" />
                        </Trigger>
                        <Trigger Property="IsDefaulted" Value="true">
                            <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DefaultedBorderBrush}" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" />
                        </Trigger>
                        <Trigger Property="IsPressed" Value="true">
                            <Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource PressedBorderBrush}" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
                            <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>
    <Style TargetType="controls:Win10MenuItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:Win10Menu}}, Path=MenuItemForeground}"/>
        <Setter Property="SelectionIndicatorColor" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:Win10Menu}}, Path=SelectionIndicatorColor}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="controls:Win10MenuItem">
                    <Button x:Name="PART_Button" Height="44"
                            Command="{TemplateBinding SelectionCommand}" 
                            ToolTip="{TemplateBinding Text}"
                            HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="5"/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <Grid Grid.ColumnSpan="2">
                                <Grid Margin="0" 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>

 

五、建立Win10MenuExample.xaml程式碼如下

Win10MenuExample.xaml實現思路如下

1、Grid佈局分為兩列設定第零列WidthAuto自適應。

2、第零列為Win10menu

3、第一列為Frame設定NavigationUIVisibility="Hidden"

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.Win10Menu.Win10MenuExample"
             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.Win10Menu"
             xmlns:wpfdev="https://github.com/yanjinhuagood/WPFDevelopers"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Background="#FF7B7BFF">
        <Grid.ColumnDefinitions>
            <ColumnDefinition  Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <wpfdev:Win10Menu Background="#eee"
                          SelectionIndicatorColor="{StaticResource PrimaryPressedSolidColorBrush}" 
                          MenuItemForeground="{StaticResource BlackSolidColorBrush}" HorizontalAlignment="Left">
            <wpfdev:Win10Menu.Content>
                <wpfdev:Win10MenuItem Icon="pack://application:,,,/Images/CircularMenu/2.png" Text="主頁"
                                      SelectionCommand="{Binding HomeCommand,RelativeSource={RelativeSource AncestorType=local:Win10MenuExample}}"/>
                <wpfdev:Win10MenuItem Icon="pack://application:,,,/Images/CircularMenu/4.png" Text="Edge"
                                      SelectionCommand="{Binding EdgeCommand,RelativeSource={RelativeSource AncestorType=local:Win10MenuExample}}"/>
                <wpfdev:Win10MenuItem Icon="pack://application:,,,/Images/CircularMenu/1.png" Text="雲盤"
                                      SelectionCommand="{Binding CloudCommand,RelativeSource={RelativeSource AncestorType=local:Win10MenuExample}}"/>
                <wpfdev:Win10MenuItem Icon="pack://application:,,,/Images/CircularMenu/8.png" Text="郵件"
                                      SelectionCommand="{Binding MailCommand,RelativeSource={RelativeSource AncestorType=local:Win10MenuExample}}"/>
                <wpfdev:Win10MenuItem Icon="pack://application:,,,/Images/CircularMenu/6.png" Text="視訊"
                                      SelectionCommand="{Binding VideoCommand,RelativeSource={RelativeSource AncestorType=local:Win10MenuExample}}"/>
            </wpfdev:Win10Menu.Content>
        </wpfdev:Win10Menu>
        <Frame Name="myFrame" Grid.Column="1" Margin="0,40,0,0"
               NavigationUIVisibility="Hidden"></Frame>
    </Grid>
</UserControl>

 

六、建立Win10MenuExample.xaml.cs程式碼如下

Win10MenuExample.xaml實現思路如下

1、定義List<Uri>賦值集合為選單需要跳轉的頁面。

2、構造為myFrame.Navigate(_uriList[0]);第零頁。

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using WPFDevelopers.Samples.Helpers;

namespace WPFDevelopers.Samples.ExampleViews.Win10Menu
{
    /// <summary>
    /// Win10MenuExample.xaml 的互動邏輯
    /// </summary>
    public partial class Win10MenuExample : UserControl
    {
        private List<Uri> _uriList = new List<Uri>()
        {
            new Uri("ExampleViews/Win10Menu/HomePage.xaml",UriKind.Relative),
            new Uri("ExampleViews/Win10Menu/EdgePage.xaml",UriKind.Relative),
        };
        public Win10MenuExample()
        {
            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 =>
        {
            MessageBox.Show("點選了雲盤");
        });
        public ICommand MailCommand => new RelayCommand(obj =>
        {
            MessageBox.Show("點選了郵件");
        });
        public ICommand VideoCommand => new RelayCommand(obj =>
        {
            MessageBox.Show("點選了視訊");
        });
    }
}

      效果預覽

 

更多教程歡迎關注微信公眾號:加微信群限時

WPF開發者QQ群: 340500857 

blogs: https://www.cnblogs.com/yanjinhua/p/14345136.html

原始碼Github:https://github.com/yanjinhuagood/WPFDevelopers.git

gitee:https://gitee.com/yanjinhua/WPFDevelopers.git

 

 

相關文章