【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));
}
}