前言
.NET 多平臺應用 UI (.NET MAUI) 是一個跨平臺框架,用于使用 C# 和 XAML 創建本機移動和桌面應用。
使用 .NET MAUI,可從單個共享代碼庫開發可在 Android、iOS、macOS 和 Windows 上運行的應用。
.NET MAUI 是一款開放源代碼應用,是 Xamarin.Forms 的進化版,從移動場景擴展到了桌面場景,并從頭重新生成了 UI 控件,以提高性能和可擴展性。 如果以前使用過 Xamarin.Forms 來生成跨平臺用戶界面,那么你會注意到它與 .NET MAUI 有許多相似之處。 但也有一些差異。 通過使用 .NET MAUI,可使用單個項目創建多平臺應用,但如果有必要,可以添加特定于平臺的源代碼和資源。 .NET MAUI 的主要目的之一是使你能夠在單個代碼庫中實現盡可能多的應用邏輯和 UI 布局。
一、問題描述
實現如下效果,菜單根據數據庫取數,自動加載。
二、解決方案
創建數據模型
UserMenu.cs 用戶功能菜單,功能字、導航頁面名(后面使用反射可以實例化窗體)、圖標名。
UserMenu.cs 用戶模塊菜單,模塊下掛在用戶功能菜單。
MenuService.cs 業務邏輯單元,相應事件的處理,菜單數據初始化
三、詳細代碼
3.1 創建用戶菜單模型
二級菜單,功能級別的,UserMenu.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;namespace GlueNet.Mobile.Models
{public class UserMenu{/// <summary>/// 功能名/// </summary>public string Name { get; set; }/// <summary>/// 圖標名/// </summary>public string Icon { get; set; }/// <summary>/// 路由名/// </summary>public string Router { get; set; }/// <summary>/// 命令/// </summary>public ICommand Command { get; set; }}
}
3.2 創建用戶菜單視圖模型
一級菜單,模塊級別的,觀察者模式
需要變更屬性,UserMenuViewModel.cs
using GlueNet.Mobile.Models;
using System.ComponentModel;
using System.Runtime.CompilerServices;namespace GlueNet.Mobile.ViewModels
{/// <summary>/// 實現INotifyPropertyChanged接口,觀察者模式/// 屬性改變通知綁定控件更新/// </summary>public class UserMenuViewModel : INotifyPropertyChanged{/// <summary>/// 模塊名/// </summary>private string _moduleName;/// <summary>/// 功能集合/// </summary>private List<UserMenu> _functions;/// <summary>/// 是否展開/// </summary>private bool _isExpanded;/// <summary>/// 展開/收起文本/// </summary>private string _expandedText;public string ModuleName{get => _moduleName;set{_moduleName = value;OnPropertyChanged();}}public List<UserMenu> Functions{get => _functions;set{_functions = value;OnPropertyChanged();}}public bool IsExpanded{get => _isExpanded;set{_isExpanded = value;OnPropertyChanged();}}public string ExpandedText{get => _expandedText;set{_expandedText = value;OnPropertyChanged();}}public event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}}
}
3.3 創建用戶菜單服務方法
綁定事件、菜單數據初始化。
using GlueNet.Bussiness.Dtos;
using GlueNet.Bussiness;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.ServiceModel.Channels;
using System.Windows.Input;
using GlueNet.Mobile.ViewModels;
using GlueNet.Mobile.Models;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.Xml.Linq;
using System.Diagnostics.Metrics;namespace GlueNet.Mobile.BLL
{public class MenuService : BindableObject{public ObservableCollection<UserMenuViewModel> MenuGroups { get; set; }public ICommand ToggleExpandCommand { get; protected set; }public MenuService(){ToggleExpandCommand = new Command<UserMenuViewModel>(OnToggleExpand);GetMenuData();}private void OnToggleExpand(UserMenuViewModel menuGroup){if (menuGroup != null){menuGroup.IsExpanded = !menuGroup.IsExpanded;menuGroup.ExpandedText = menuGroup.IsExpanded ? "收起" : "展開";OnPropertyChanged(nameof(MenuGroups));}}/// <summary>/// 獲取菜單數據,從MES服務端獲取/// </summary>public void GetMenuData(){MenuGroups = new ObservableCollection<UserMenuViewModel>{new UserMenuViewModel{ModuleName = "質量管理",Functions = new List<UserMenu>{new UserMenu { Name = "質量1", Icon = "icon_quality.png", Router ="MO2001Page", Command = new Command(() => NavigateToPage("MO2001Page")) } ,new UserMenu { Name = "質量2", Icon = "icon_quality.png", Router = "MO2001Page",Command = new Command(() => NavigateToPage("MO2001Page")) },new UserMenu { Name = "質量3", Icon = "icon_quality.png", Router = "MO2001Page" ,Command = new Command(() => NavigateToPage("MO2001Page"))},new UserMenu { Name = "質量4", Icon = "icon_quality.png", Router = "MO2001Page" ,Command = new Command(() => NavigateToPage("MO2001Page"))},new UserMenu { Name = "質量5", Icon = "icon_quality.png", Router = "MO2001Page" ,Command = new Command(() => NavigateToPage("MO2001Page"))},new UserMenu { Name = "質量6", Icon = "icon_quality.png", Router = "MO2001Page" ,Command = new Command(() => NavigateToPage("MO2001Page"))},new UserMenu { Name = "質量7", Icon = "icon_quality.png", Router = "MO2001Page" ,Command = new Command(() => NavigateToPage("MO2001Page"))},new UserMenu { Name = "質量8", Icon = "icon_quality.png", Router = "MO2001Page" ,Command = new Command(() => NavigateToPage("MO2001Page"))},new UserMenu { Name = "質量9", Icon = "icon_quality.png", Router = "MO2001Page" ,Command = new Command(() => NavigateToPage("MO2001Page"))},},IsExpanded = true,ExpandedText = "收起"},new UserMenuViewModel{ModuleName = "采購管理",Functions = new List<UserMenu>{new UserMenu { Name = "采購1", Icon = "icon_supply.png", Router="MO2001Page", Command = new Command(() => NavigateToPage("MO2001Page")) } ,new UserMenu { Name = "采購2", Icon = "icon_supply.png", Router = "MO2001Page",Command = new Command(() => NavigateToPage("MO2001Page")) },new UserMenu { Name = "采購3", Icon = "icon_supply.png", Router = "MO2001Page" ,Command = new Command(() => NavigateToPage("MO2001Page"))},},IsExpanded = true,ExpandedText = "收起"},new UserMenuViewModel{ModuleName = "作業管理",Functions = new List<UserMenu>{new UserMenu { Name= "棧板下線", Icon= "icon_operation.png", Router="MO1001Page",Command = new Command(() => NavigateToPage("MO1001Page"))},new UserMenu { Name = "次件退庫", Icon = "icon_operation.png", Router = "MO1002Page",Command = new Command(() => NavigateToPage("MO1002Page")) }},IsExpanded = true,ExpandedText = "收起"}};}/// <summary>/// 使用反射,根據頁面名稱導航到指定頁面/// </summary>/// <param name="pageName"></param>/// <exception cref="ArgumentException"></exception>private async void NavigateToPage(string pageName){// 獲取對象名Type pageType = Type.GetType($"GlueNet.Mobile.Pages.{pageName}");if (pageType != null){//創建實例Page page = (Page)Activator.CreateInstance(pageType);await Application.Current.MainPage.Navigation.PushAsync(page);}else{throw new ArgumentException($"無法導航頁面: {pageName}");}}}
}
3.4 創建用戶菜單界面
xaml前段界面,需要使用模型綁定,和CollectionView
遍歷,創建MenuView.xaml。
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:local="clr-namespace:GlueNet.Mobile.BLL"x:Class="GlueNet.Mobile.MenuView"><!--綁定上下文--><ContentView.BindingContext><local:MenuService /></ContentView.BindingContext><!--綁定字體資源,圖標已經生成字體庫--><ContentView.Resources><Style x:Key="NavButtonStyle" TargetType="RadioButton"><Setter Property="ControlTemplate"><ControlTemplate><Grid><Grid.RowDefinitions><RowDefinition/><RowDefinition Height="30" /></Grid.RowDefinitions><!--替換為圖標--><Label Text="{TemplateBinding Value}" FontFamily="Iconfont" FontSize="30" HorizontalOptions="Center"VerticalOptions="Center"></Label><!--替換為文字--><Label Text="{TemplateBinding Content}" Grid.Row="1" HorizontalOptions="Center"VerticalOptions="Center" FontSize="13"></Label></Grid></ControlTemplate></Setter></Style></ContentView.Resources><ScrollView><StackLayout><CollectionView ItemsSource="{Binding MenuGroups}"><CollectionView.ItemTemplate><DataTemplate><StackLayout><!-- 模塊名 --><Frame BorderColor="Gray" CornerRadius="5" Padding="5" Margin="5"><Grid ColumnDefinitions="*,60"><Label Text="{Binding ModuleName}" FontSize="Medium" FontAttributes="Bold" VerticalTextAlignment="Center"/><Button Grid.Column="1" Text="{Binding ExpandedText}" Command="{Binding Source={RelativeSource AncestorType={x:Type local:MenuService}}, Path=ToggleExpandCommand}" CommandParameter="{Binding .}" /></Grid></Frame><!-- 功能列表 --><StackLayout IsVisible="{Binding IsExpanded}"><CollectionView ItemsSource="{Binding Functions}" ItemsLayout="VerticalGrid,4"><CollectionView.ItemTemplate><DataTemplate><Grid Padding="5"><Grid.RowDefinitions><RowDefinition Height="*" /><RowDefinition Height="Auto" /></Grid.RowDefinitions><Frame BorderColor="LightGray" CornerRadius="5" Padding="10" Margin="5"><StackLayout Orientation="Vertical" HorizontalOptions="Center" VerticalOptions="Center" ><ImageButton Source="{Binding Icon}" HorizontalOptions="Center" Command="{Binding Command}" /><Label Text="{Binding Name}" FontSize="Medium" VerticalOptions="Center" HorizontalOptions="Center" Margin="10,0,0,0" /></StackLayout></Frame></Grid></DataTemplate></CollectionView.ItemTemplate></CollectionView></StackLayout></StackLayout></DataTemplate></CollectionView.ItemTemplate></CollectionView></StackLayout></ScrollView>
</ContentView>