自制WPF侧边栏菜单导航
这是询问DeepSeek后,写的Wpf侧边栏菜单导航。
先用Nuget安装CommunityToolkit.Mvvm实现mvvm模式编程,安装Microsoft.Xaml.Behaviors.Wpf实现事件转命令。
先建Models文件夹,增加MenuItem.cs
namespace SidebarMenuDemo.Models
{
class MenuItem
{
required public string Title { get; init; }
required public string ImagePath { get; init; }
required public Type ViewType { get; init; }
}
}
增加NamedColor.cs模型
using System.Windows.Media;
namespace SidebarMenuDemo.Models
{
class NamedColor
{
required public string Name { get; set; }
required public Color Color { get; set; }
}
}
创建ViewModels文件夹,为MainWindow.xaml创建MainWindowViewModel.cs。
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace SidebarMenuDemo.ViewModels
{
partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private object? _currentView;
public ObservableCollection<Models.MenuItem> MenuItems { get; private set; } = [];
[RelayCommand]
private void SelectionChanged(Models.MenuItem? item)
{
if (item is null)
return;
if(item.ViewType is not null)
{
CurrentView = Activator.CreateInstance(item.ViewType);
}
}
private void InitializeMenu()
{
MenuItems.Add(new Models.MenuItem { Title = "命名颜色表", ImagePath = "/Images/Flower.png", ViewType = typeof(Views.NamedColorView) });
MenuItems.Add(new Models.MenuItem { Title = "关于", ImagePath = "/Images/about.png", ViewType = typeof(Views.AboutView) });
}
public MainWindowViewModel()
{
InitializeMenu();
SelectionChanged(MenuItems.First<Models.MenuItem>());
}
}
}
把SelectionChanged事件的参数SelectionChangedEventArgs转换成MenuItem需要一个转换器SelectionChangedToMenuItem,
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;
namespace SidebarMenuDemo.Converter
{
class SelectionChangedToMenuItem: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value switch
{
SelectionChangedEventArgs e => e.AddedItems.OfType<Models.MenuItem>().First(),
null => throw new ArgumentNullException(nameof(value)),
_ => throw new ArgumentException("Unknown Type of a value")
};
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
下面是MainWindow.xaml的内容
<Window x:Class="SidebarMenuDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SidebarMenuDemo"
xmlns:viewmodel="clr-namespace:SidebarMenuDemo.ViewModels"
xmlns:converter="clr-namespace:SidebarMenuDemo.Converter"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<converter:SelectionChangedToMenuItem x:Key="SelectionChangedToMenuItem"/>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Bd" Background="Transparent" BorderBrush="Transparent" BorderThickness="1"
CornerRadius="4" Padding="8,6" SnapsToDevicePixels="True">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<!--鼠标悬停效果-->
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#44FFA500"/>
<Setter TargetName="Bd" Property="BorderBrush" Value="#22FFA500"/>
</Trigger>
<!--选中效果-->
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#88FFA500"/>
<Setter TargetName="Bd" Property="BorderBrush" Value="#66FFA500"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="Margin" Value="4"/>
</Style>
</Window.Resources>
<Grid>
<Grid.DataContext>
<viewmodel:MainWindowViewModel/>
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!--左侧菜单-->
<ListBox Margin="4" Background="Teal" ItemsSource="{Binding MenuItems}"
SelectionMode="Single">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
EventArgsConverter="{StaticResource SelectionChangedToMenuItem}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}" Width="32" Margin="4"/>
<TextBlock Text="{Binding Title}" FontSize="18" Foreground="White" VerticalAlignment="Center" Margin="4"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--右侧内容视图-->
<ContentControl Grid.Column="1" Content="{Binding CurrentView}" Margin="4"/>
</Grid>
</Window>
添加Views文件夹,添加AboutView.xaml文件
<UserControl x:Class="SidebarMenuDemo.Views.AboutView"
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:SidebarMenuDemo.Views"
xmlns:viewmodel="clr-namespace:SidebarMenuDemo.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="600">
<Grid>
<Grid.Background>
<ImageBrush ImageSource="/Images/蓝底树叶.jpg"/>
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="SidebarMenuDemo 1.0" FontSize="22" Foreground="White" Margin="4"
Grid.Row="0"/>
<TextBlock Text="学习WPF,自制菜单导航功能。" FontSize="18" Grid.Row="1" Foreground="White"
Margin="4"/>
</Grid>
</UserControl>
再添加一个NamedColorView.xaml文件
<UserControl x:Class="SidebarMenuDemo.Views.NamedColorView"
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:SidebarMenuDemo.Views"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:viewModel="clr-namespace:SidebarMenuDemo.ViewModels"
xmlns:converter="clr-namespace:SidebarMenuDemo.Converter"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="600">
<UserControl.DataContext>
<viewModel:NamedColorViewModel/>
</UserControl.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding FillCollectionCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid Background="#FF0A1921">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Margin="4" Text="{Binding NamedColors.Count,StringFormat=命名颜色数:{0}}" FontSize="18" Grid.Row="0"
Foreground="White"/>
<ListBox Grid.Row="1" Margin="4" ItemsSource="{Binding NamedColors}"
Background="LemonChiffon">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Grid.Column="0" FontSize="16" VerticalAlignment="Center"/>
<Rectangle Height="30" Width="300" Grid.Column="1">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Color}"/>
</Rectangle.Fill>
</Rectangle>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
在ViewModels文件夹下为NamedColor.xaml增加一个NamedColorViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows.Media;
namespace SidebarMenuDemo.ViewModels
{
partial class NamedColorViewModel : ObservableObject
{
public ObservableCollection<Models.NamedColor> NamedColors { get; set; } = [];
private async IAsyncEnumerable<Models.NamedColor> GetColors()
{
foreach (PropertyInfo propertyInfo in typeof(Colors).GetRuntimeProperties())
{
yield return new Models.NamedColor
{
Color = (Color)propertyInfo.GetValue(null)!,
Name = propertyInfo.Name
};
await Task.Delay(1);
}
}
[RelayCommand]
private async Task FillCollection()
{
NamedColors.Clear();
await foreach (var item in GetColors())
{
NamedColors.Add(item);
}
}
public NamedColorViewModel()
{
}
}
}
程序效果
DeepSeek按照我的意思设计的,还不错吧。