【WPF】自定义控件:MenuWithSubMenuControl-二级菜单导航


使用

//导航
<Border Grid.Column="1" Grid.Row="1" BorderThickness="0.5,0.5,0,0" BorderBrush="Black">
    <componentView:MenuWithSubMenuControl HasIcon="True" SubMenuPadding="15,0,0,0"/>
</Border>

//页面
<Frame x:Name="MainWindowFrame" Grid.Row="1" Grid.Column="1" NavigationUIVisibility="Hidden"/>

ViewModel

using bueatifulApp.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace bueatifulApp.Components.MenuWithSubMenuList.ViewModel
{
    internal class SideMenuViewModel
    {
        ResourceDictionary iconDict = Application.LoadComponent(new Uri("/bueatifulApp;component/Components/MenuWithSubMenuList/Icons/MenuIcons.xaml", UriKind.RelativeOrAbsolute)) as ResourceDictionary;

        public List<MenuItemsData> MenuList
        {
            get
            {
                return new List<MenuItemsData> {
                    new MenuItemsData{IconPath=(PathGeometry)iconDict["icon_dashboard"],MenuText="Dashboard",MenuTip="",SubMenuList=null },
                    new MenuItemsData{IconPath=(PathGeometry)iconDict["icon_users"],MenuText="Profile",MenuTip="",
                        SubMenuList=new List<SubMenuItemsData>{
                            new SubMenuItemsData{ IconPath=(PathGeometry)iconDict["icon_adduser"],SubMenuText="Add User",SubMenuTip=""},
                            new SubMenuItemsData{ IconPath=(PathGeometry)iconDict["icon_allusers"],SubMenuText="All Users",SubMenuTip=""},
                    }},
                    new MenuItemsData{IconPath=(PathGeometry)iconDict["icon_mails"],MenuText="Mails",MenuTip="",
                        SubMenuList=new List<SubMenuItemsData>{
                            new SubMenuItemsData{ IconPath=(PathGeometry)iconDict["icon_inbox"],SubMenuText="Inbox",SubMenuTip=""},
                            new SubMenuItemsData{ IconPath=(PathGeometry)iconDict["icon_outbox"],SubMenuText="Outbox",SubMenuTip=""},
                            new SubMenuItemsData{ IconPath=(PathGeometry)iconDict["icon_sentmail"],SubMenuText="Sent Mail",SubMenuTip=""},
                    }},
                    new MenuItemsData{IconPath=(PathGeometry)iconDict["icon_settings"],MenuText="Settings",MenuTip="",SubMenuList=null },
                };
            }
        }
    }

    public class MenuItemsData
    {
        public PathGeometry IconPath { get; set; }
        public string MenuText { get; set; }
        public string MenuTip { get; set; }
        public List<SubMenuItemsData> SubMenuList { get; set; }

        public MenuItemsData() {
            Command = new RelayCommand(o=>Excute());
        }

        public RelayCommand Command { get; }

        private void Excute() {
            if (SubMenuList != null) return;
            string MT = MenuText.Replace(" ", string.Empty);
            if (!string.IsNullOrEmpty(MT)) {
                navigateToPage(MT);
            }
        }
        private void navigateToPage(string menu) {
            foreach (Window window in Application.Current.Windows)
            {
                if (window.GetType() == typeof(MainWindow)) {
                    (window as MainWindow).MainWindowFrame.Navigate(new Uri(string.Format("{0}{1}{2}","MVVM/View/Pages/",menu,".xaml"),UriKind.RelativeOrAbsolute));
                }
            }
        }
    }

    public class SubMenuItemsData
    {
        public PathGeometry IconPath { get; set; }
        public string SubMenuText { get; set; }
        public string SubMenuTip { get; set; }

        public SubMenuItemsData()
        {
            SubMenuCommand = new RelayCommand(o => Excute());
        }

        public RelayCommand SubMenuCommand { get; }

        private void Excute()
        {
            string MT = SubMenuText.Replace(" ", string.Empty);
            if (!string.IsNullOrEmpty(MT))
            {
                navigateToPage(MT);
            }
        }
        private void navigateToPage(string menu)
        {
            foreach (Window window in Application.Current.Windows)
            {
                if (window.GetType() == typeof(MainWindow))
                {
                    (window as MainWindow).MainWindowFrame.Navigate(new Uri(string.Format("{0}{1}{2}", "MVVM/View/Pages/", menu, ".xaml"), UriKind.RelativeOrAbsolute));
                }
            }
        }
    }
}

View

<UserControl x:Class="bueatifulApp.Components.MenuWithSubMenuList.View.MenuWithSubMenuControl"
             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:bueatifulApp.Components.MenuWithSubMenuList.View"
             mc:Ignorable="d" x:Name="this">

    <UserControl.Resources>
        <PathGeometry x:Key="icon_closeSubMenu" Figures=".."/>
        <PathGeometry x:Key="icon_openSubMenu" Figures=".."/>
        <PathGeometry x:Key="icon_default" Figures=".."/>
        <Thickness x:Key="DefaultSubMenuPadding" Left="10"/>
        <Style TargetType="{x:Type TextBlock}" x:Key="MenuTextTheme">
            <Setter Property="Foreground" Value="#898F9D"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ToggleButton}}" Value="True">
                    <Setter Property="Foreground" Value="White"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsChecked,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ToggleButton}}" Value="True">
                    <Setter Property="Foreground" Value="White"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}" Value="True">
                    <Setter Property="Foreground" Value="White"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsSelected,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}" Value="True">
                    <Setter Property="Foreground" Value="White"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type Path}" x:Key="IconPathTheme">
            <Setter Property="Fill" Value="#898F9D"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ToggleButton}}" Value="True">
                    <Setter Property="Fill" Value="White"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsChecked,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ToggleButton}}" Value="True">
                    <Setter Property="Fill" Value="White"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}" Value="True">
                    <Setter Property="Fill" Value="White"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsSelected,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}" Value="True">
                    <Setter Property="Fill" Value="White"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type Path}" x:Key="SubMenuOpenIconTheme">
            <Setter Property="Data" Value="{StaticResource icon_closeSubMenu}"/>
            <Setter Property="Fill" Value="#898F9D"/>
            <Setter Property="Visibility" Value="Visible"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding SubMenuList}" Value="{x:Null}">
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsChecked,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ToggleButton}}" Value="True">
                    <Setter Property="Data" Value="{StaticResource icon_openSubMenu}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type Border}" x:Key="MenuBorderTheme">
            <Setter Property="Background" Value="Transparent"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ToggleButton}}" Value="True">
                    <Setter Property="Background" Value="#3A3F4B"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}" Value="True">
                    <Setter Property="Background" Value="#3A3F4B"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsSelected,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}" Value="True">
                    <Setter Property="Background" Value="#417CF1"/>
                </DataTrigger>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding SubMenuList}" Value="{x:Null}"/>
                        <Condition Binding="{Binding IsChecked,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ToggleButton}}" Value="True"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Property="Background" Value="#417CF1"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
        <Style BasedOn="{StaticResource {x:Type ToggleButton}}" TargetType="{x:Type RadioButton}"
               x:Key="MainMenuTheme">
            <Setter Property="Height" Value="30"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Background" Value="Transparent"/>
            <!--<Setter Property="Content" Value="{Binding SubMenuText,FallbackValue=MenuText,TargetNullValue=MenuText}"/>-->
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="RadioButton">
                        <Border CornerRadius="5" 
                                VerticalAlignment="Stretch"
                                HorizontalAlignment="Stretch"
                                Padding="{TemplateBinding Padding}"
                                Margin="5,0"
                                Style="{StaticResource MenuBorderTheme}"
                                >
                            <Grid Margin="5,0,0,0">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="20"/>
                                    <ColumnDefinition Width="20"/>
                                    <ColumnDefinition/>
                                </Grid.ColumnDefinitions>
                                <Grid Grid.Column="1" >
                                    <Grid.Style>
                                        <Style TargetType="{x:Type Grid}">
                                            <Style.Triggers>
                                                <DataTrigger Binding="{Binding HasIcon,ElementName=this,TargetNullValue=False,FallbackValue=False}" Value="False">
                                                    <Setter Property="Visibility" Value="Collapsed"/>
                                                </DataTrigger>
                                            </Style.Triggers>
                                        </Style>
                                    </Grid.Style>
                                    <Path Data="{Binding IconPath,FallbackValue={StaticResource icon_default},TargetNullValue={StaticResource icon_default}}"
                                          Width="17" Height="17"
                                          Stretch="Uniform"
                                          Style="{StaticResource IconPathTheme}"
                                          />
                                </Grid>

                                <TextBlock Text="{TemplateBinding Content}"
                                           VerticalAlignment="Center"
                                           Margin="10,0,0,0"
                                           Grid.Column="2"
                                           Style="{StaticResource MenuTextTheme}"
                                   />
                                <Path Grid.Column="0" 
                                      Width="10" Height="10"
                                      Stretch="Uniform"
                                      Style="{StaticResource SubMenuOpenIconTheme}"/>
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="Content" Value="Profile">
                                <Setter Property="IsChecked" Value="True"/>
                            </Trigger>
                            <Trigger Property="Content" Value="Add User">
                                <Setter Property="IsChecked" Value="True"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="{x:Type ListBox}">
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="SelectedIndex" Value="0"/>
            <Setter Property="SelectionMode" Value="Single"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsChecked,ElementName=menu}" Value="False">
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding SubMenuList}" Value="{x:Null}">
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Height" Value="30"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Border CornerRadius="5" 
                                Background="{TemplateBinding Background}"
                                VerticalAlignment="Stretch"
                                HorizontalAlignment="Stretch" 
                               >
                            <RadioButton Content="{Binding SubMenuText,FallbackValue=MenuText,TargetNullValue=MenuText}"
                                         GroupName="SubMenu"
                                         Height="30"
                                         Style="{StaticResource MainMenuTheme}" 
                                         Padding="{Binding SubMenuPadding,ElementName=this,FallbackValue={StaticResource DefaultSubMenuPadding},TargetNullValue={StaticResource DefaultSubMenuPadding}}"
                                         x:Name="subMenu"
                                         Command="{Binding SubMenuCommand}"/>

                        </Border>
                        <ControlTemplate.Triggers>
                            <DataTrigger Binding="{Binding IsChecked,ElementName=subMenu}" Value="True">
                                <Setter Property="IsSelected" Value="True"/>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <ItemsControl ItemsSource="{Binding MenuList}"
                  ScrollViewer.VerticalScrollBarVisibility="Disabled"
                  ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <RadioButton Content="{Binding MenuText}"
                                 x:Name="menu"
                                 GroupName="MainMenu"
                                 Height="30"
                                 Style="{StaticResource MainMenuTheme}"
                                 Command="{Binding Command}"
                                 />
                    <ListBox ItemsSource="{Binding SubMenuList}"
                             ScrollViewer.VerticalScrollBarVisibility="Disabled"
                             ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                             
                             />

                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</UserControl>

namespace bueatifulApp.Components.MenuWithSubMenuList.View
{
    /// <summary>
    /// MenuWithSubMenuControl.xaml 的交互逻辑
    /// </summary>
    public partial class MenuWithSubMenuControl : UserControl
    {
        public MenuWithSubMenuControl()
        {
            InitializeComponent();
            DataContext = new SideMenuViewModel();
        }
        public Thickness SubMenuPadding
        {
            get { return (Thickness)GetValue(SubMenuPaddingProperty); }
            set { SetValue(SubMenuPaddingProperty,value); }
        }
        public static readonly DependencyProperty SubMenuPaddingProperty = DependencyProperty.Register("SubMenuPadding", typeof(Thickness), typeof(MenuWithSubMenuControl));

        public bool HasIcon
        {
            get { return (bool)GetValue(HasIconProperty); }
            set { SetValue(HasIconProperty, value); }
        }
        public static readonly DependencyProperty HasIconProperty = DependencyProperty.Register("HasIcon", typeof(bool), typeof(MenuWithSubMenuControl));
    }
}
posted @ 2025-04-09 09:34  Sitar  阅读(29)  评论(0)    收藏  举报