当前位置: 首页 > article >正文

【Maui】动态菜单实现(绑定数据视图)

前言

.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>


http://www.kler.cn/a/488022.html

相关文章:

  • 2024年华为OD机试真题-判断一组不等式是否满足约束并输出最大差-Python-OD统一考试(E卷)
  • Vue进阶(贰幺贰)npm run build多环境编译
  • 新车月交付突破2万辆!小鹏汽车“激活”智驾之困待解
  • 网络安全-XSS跨站脚本攻击(基础篇)
  • EXCEL: (二) 常用图表
  • 如何配置Cursor的显示主题模式
  • Docker部署Naocs-- 超细教程
  • 【JVM-2】JVM图形化监控工具大全:从入门到精通
  • 青少年编程与数学 02-006 前端开发框架VUE 18课题、逻辑复用
  • qemu模拟磁盘
  • 【Linux】Linux开发:GDB调试器与Git版本控制工具指南
  • STM32中的MCO
  • brpc之IOBuf
  • 【redis】centos7下安装redis7
  • 网站自动签到
  • 【MySQL基础篇】十四、MySQL的C语言API使用
  • #渗透测试#网络安全# 一文了解什么是跨域CROS!!!
  • (纯小白教程)Liunx系统安装Anaconda
  • LLM - Llama 3 的 Pre/Post Training 阶段 Loss 以及 logits 和 logps 概念
  • 《零基础Go语言算法实战》【题目 2-2】使用函数交换两个变量的值
  • Python网络爬虫:从入门到实战
  • 《Spring Framework实战》15:4.1.4.6.方法注入
  • C#里使用libxl里演示输出日期和读取日期数据的例子
  • 前端工具汇总
  • 使用virtualenv创建虚拟环境
  • JavaScript学习记录13