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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/951829.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【json】

JSON JSON是一种轻量级的,按照指定的格式去组织和封装数据的数据交互格式。 本质上是一个带有特定格式的字符串(py打印json时认定为str类型) 在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言中的数据传递和交互,类似于计算机普通话 python与json关系及相互转换…

51单片机——中断(重点)

学习51单片机的重点及难点主要有中断、定时器、串口等内容&#xff0c;这部分内容一定要认真掌握&#xff0c;这部分没有学好就不能说学会了51单片机 1、中断系统 1.1 概念 中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的&#xff0c;中断功能的存在&#…

易支付二次元网站源码及部署教程

易支付二次元网站源码及部署教程 引言 在当今数字化时代&#xff0c;二次元文化逐渐成为年轻人生活中不可或缺的一部分。为了满足这一庞大用户群体的需求&#xff0c;搭建一个二次元主题网站显得尤为重要。本文将为您详细介绍易支付二次元网站源码的特点及其部署教程&#xf…

开源生成式物理引擎Genesis,可模拟世界万物

这是生成大模型时代 —— 它们能生成文本、图像、音频、视频、3D 对象…… 而如果将所有这些组合到一起&#xff0c;我们可能会得到一个世界&#xff01; 现在&#xff0c;不管是 LeCun 正在探索的世界模型&#xff0c;还是李飞飞想要攻克的空间智能&#xff0c;又或是其他研究…

【fly-iot飞凡物联】(19):开源飞凡物联项目重启,使用go重写后端代码,感兴趣的小伙伴可以一起参加,使用apache协议开源,招募感兴趣的小伙伴!!

目录 前言fly-iot飞凡物联&#xff0c;感兴趣的小伙伴可以一起参加&#xff0c;使用apache协议开源使用go重写后端代码 前言 fly-iot飞凡物联专栏&#xff1a; https://blog.csdn.net/freewebsys/category_12219758.html fly-iot飞凡物联&#xff0c;感兴趣的小伙伴可以一起参…

用于与多个数据库聊天的智能 SQL 代理问答和 RAG 系统(3) —— 基于 LangChain 框架的文档检索与问答功能以及RAG Tool的使用

介绍基于 LangChain 框架的文档检索与问答功能&#xff0c;目标是通过查询存储的向量数据库&#xff08;VectorDB&#xff09;&#xff0c;为用户的问题检索相关内容&#xff0c;并生成自然语言的答案。以下是代码逻辑的详细解析&#xff1a; 代码结构与功能 初始化环境与加载…

消息中间件类型介绍

消息中间件是一种在分布式系统中用于实现消息传递的软件架构模式。它能够在不同的系统或应用之间异步地传输数据&#xff0c;实现系统的解耦、提高系统的可扩展性和可靠性。以下是几种常见的消息中间件类型及其介绍&#xff1a; 1.RabbitMQ 特点&#xff1a; • 基于AMQP&#…

uniapp使用scss mixin抽离css常用的公共样式

1、编写通用scss样式文件 // 通用 Flex Mixin mixin flex($direction: row, $justify: flex-start, $align: stretch, $wrap: nowrap) {display: flex;flex-direction: $direction;justify-content: $justify;align-items: $align;flex-wrap: $wrap; }// 水平居中 mixin flex-…

Matlab Steger算法提取条纹中心线(亚像素位置)

文章目录 一、简介二、实现代码三、实现效果参考文献一、简介 Steger 算法是一种常用的图像边缘检测算法,可以用于提取图像中的中心线或边缘信息。它的理论假设是:条纹的亮度是按照高斯分布呈现的,即中心亮两侧渐暗。 其计算过程如下所述: 1、首先,我们需要计算每个点Hess…

PySide6 Qt for Python Qt Quick参考网址

Qt QML BOOK&#xff1a; 《Qt for Python》 -Building an Application https://www.qt.io/product/qt6/qml-book/ch19-python-build-app#signals-and-slots Qt for Python&#xff1a;与C版本的差异即BUG处理&#xff08;常见的DLL文件确实的问题等&#xff09; Qt for Pyt…

【大数据】Apache Superset:可视化开源架构

Apache Superset是什么 Apache Superset 是一个开源的现代化数据可视化和数据探索平台&#xff0c;主要用于帮助用户以交互式的方式分析和展示数据。有不少丰富的可视化组件&#xff0c;可以将数据从多种数据源&#xff08;如 SQL 数据库、数据仓库、NoSQL 数据库等&#xff0…

ELK实战(最详细)

一、什么是ELK ELK是三个产品的简称&#xff1a;ElasticSearch(简称ES) 、Logstash 、Kibana 。其中&#xff1a; ElasticSearch&#xff1a;是一个开源分布式搜索引擎Logstash &#xff1a;是一个数据收集引擎&#xff0c;支持日志搜集、分析、过滤&#xff0c;支持大量数据…

汽车物资拍卖系统架构与功能分析

2015工作至今&#xff0c;10年资深全栈工程师&#xff0c;CTO&#xff0c;擅长带团队、攻克各种技术难题、研发各类软件产品&#xff0c;我的代码态度&#xff1a;代码虐我千百遍&#xff0c;我待代码如初恋&#xff0c;我的工作态度&#xff1a;极致&#xff0c;责任&#xff…

利用 Python 爬虫从义乌购根据关键词获取商品列表

在当今数字化商业时代&#xff0c;数据是企业获取竞争优势的关键。对于从事国际贸易的商家而言&#xff0c;能够及时、准确地获取商品信息至关重要。义乌购作为知名的国际贸易批发平台&#xff0c;汇集了海量的商品资源。通过 Python 爬虫技术&#xff0c;我们可以高效地从义乌…

HDFS编程 - 使用HDFS Java API进行文件操作

文章目录 前言一、创建hdfs-demo项目1. 在idea上创建maven项目2. 导入hadoop相关依赖 二、常用 HDFS Java API1. 简介2. 获取文件系统实例3. 创建目录4. 创建文件4.1 创建文件并写入数据4.2 创建新空白文件 5. 查看文件内容6. 查看目录下的文件或目录信息6.1 查看指定目录下的文…

直流无刷电机控制(FOC):电流模式

目录 概述 1 系统框架结构 1.1 硬件模块介绍 1.2 硬件实物图 1.3 引脚接口定义 2 代码实现 2.1 软件架构 2.2 电流检测函数 3 电流环功能实现 3.1 代码实现 3.2 测试代码实现 4 测试 概述 本文主要介绍基于DengFOC的库函数&#xff0c;实现直流无刷电机控制&#x…

51单片机——串口通信(重点)

1、通信 通信的方式可以分为多种&#xff0c;按照数据传送方式可分为串行通信和并行通信&#xff1b; 按照通信的数据同步方式&#xff0c;可分为异步通信和同步通信&#xff1b; 按照数据的传输方向又可分为单工、半双工和全双工通信 1.1 通信速率 衡量通信性能的一个非常…

如何在 Linux、MacOS 以及 Windows 中打开控制面板

控制面板不仅仅是一系列图标和菜单的集合&#xff1b;它是通往优化个人计算体验的大门。通过它&#xff0c;用户可以轻松调整从外观到性能的各种参数&#xff0c;确保他们的电脑能够完美地适应自己的需求。无论是想要提升系统安全性、管理硬件设备&#xff0c;还是简单地改变桌…

浅谈弱电系统RVVP和RVSP电缆的区别(

1、RVVP 1.1RVVP电缆定义&#xff1f; RVVP电缆抗干扰软电缆、屏蔽电缆、信号电缆、控制电缆&#xff08;名字很多&#xff09;&#xff0c;学名&#xff1a;铜芯-聚氯乙烯绝缘-屏蔽聚氯乙烯护套-软电缆。 1.2RVVP执行标准 主要执行标准为JB/T8734.5-2016&#xff0c;部…

Python的pandas库基础知识(超详细教学)

目录 一、配置环境 二、序列和数据表 2.1 初始化 2.2 获取数值 2.3 获取索引 2.4 索引取内容 2.5 索引改变取值 2.6 字典生成序列 2.7 计算取值出现次数 2.8 数据表 2.9 数据表添加新变量 2.10 获取列名 2.11 根据列名获取数据 2.12 输出固定行 2.13 输出多行…