使用MAUI制作的联系人Demo

749 阅读4分钟

MAUI

简介

什么是MAUI

.NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移动和桌面应用。 使用 .NET MAUI,可从单个共享代码库开发可在 Android、iOS、macOS 和 Windows 上运行的应用。

image.png

环境准备

确保你的Vs安装了这个组件

image.png

创建项目

选择MAUI应用创建,设置你的项目名字

image.png

image.png

启动项目

选择你的启动方式:

image.png

Windows启动

如果要在Windows中启动将会出现以下提示:为Windows启用开发人员模式,你需要点击链接然后启用开发人员模式。

image.png

image.png

Android启动

选择启动然后接受许可协议,然后创建一个模拟器

image.png

需要在Windows功能中启用以下功能加速安卓模拟器。确保在Android设备管理器中创建的虚拟设备是基于x86-64或基于x86的系统映像。如果使用基于ARM的系统映像,虚拟设备将无法加速并且运行缓慢。

image.png

image.png

创建完模拟器选择启动,第一次部署项目比较慢,后面部署会对比上一次部署的差异,然后编译差异的内容而不是所有内容,然后部署到模拟器中

image.png

项目结构

Visual Studio 创建 .NET MAUI 项目时,将生成四个重要的代码文件。 可以在 Visual Studio 的“解决方案资源管理器”窗格中看到这些文件:

image.png

MauiProgram.cs

这是启动应用的代码文件。 此文件中的代码充当应用的跨平台入口点,用于配置和启动应用。 模板启动代码指向 App.xaml 文件定义的 App 类。更细节的一点是其实这个文件不是最开始的入口,最开始的入口实在Platforms文件夹中实际平台的启动应用代码文件,比如安卓:

image.png 可以看到实际代码是指到MauiProgram.cs这个文件的,虽然但是一般只需要关注MauiProgram就行了,如果不是特殊情况下

public class MainApplication : MauiApplication
{
    public MainApplication(IntPtr handle, JniHandleOwnership ownership)
        : base(handle, ownership)
    {
    }

    protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

App.xaml 和 App.xaml.cs

为了简单起见,这两个文件称为单个文件。 所有 XAML 文件通常都包含两个文件,即 .xaml 文件本身,以及一个相应的代码文件,该文件是“解决方案资源管理器”中的 .xaml 文件的子项。 .xaml 文件包含 XAML 标记,代码文件包含用户创建的用于与 XAML 标记交互的代码。

App.xaml 文件包含应用范围的 XAML 资源,例如颜色、样式或模板。 App.xaml.cs 文件通常包含用于实例化 Shell 应用程序的代码。 在此项目中,它指向 AppShell 类。

AppShell.xaml 和 AppShell.xaml.cs

此文件定义 AppShell 类,该类用于定义应用的视觉层次结构。

MainPage.xaml 和 MainPage.xaml.cs

这是应用显示的启动页。 MainPage.xaml 文件定义页面的 UI(用户界面)。 MainPage.xaml.cs 包含 XAML 的代码隐藏,如按钮单击事件的代码。

代码编写

创建模型

创建Mods文件夹,创建以下两个类Contact.csContactReposiroty.cs

namespace Contacts.Maui.Mods
{
    class Contact
    {
        public int ContactId { get; set; }
        public string Name { get; set; }
        public string  Email { get; set; }
        public string Phone { get; set; }
        public string Address { get; set; }

        public Contact(int contactId, string name, string email, string phone,string adddress)
        {
            Name = name;
            Email = email;
            ContactId = contactId;
            Phone = phone;
            Address = adddress;
        }
        public Contact() { }
    }
}
namespace Contacts.Maui.Mods
{
    class ContactRepository
    {
        public static List<Contact> _contacts = new List<Contact>()
        {
            new Contact(1,"John Doe","JohnDoe@gmail.com","416-800-8000","New York"),
            new Contact(2,"Jane Doe","JaneDoe@gmail.com","","New York"),
            new Contact(3,"Tom Hanks","TomHanks@gmail.com","","New York"),
            new Contact(4,"Frank Liu","FrankLiu@gmail.com","","New York"),
        };

        public static List<Contact> GetContacts() => _contacts;

        public static Contact GetContactById(int contactId)
        {
            return _contacts.FirstOrDefault(x => x.ContactId == contactId);
        }

        public static void UpdateContact(int contactId,Contact contact)
        {
            if(contact.ContactId!= contact.ContactId)
            {
                return;
            }
            var oldContact = GetContactById(contactId);
            if(oldContact != null)
            {
                oldContact = contact;
            }
        }

        public static void CreateContact(Contact contact)
        {
            var maxId = _contacts.Max(x => x.ContactId);
            contact.ContactId = maxId+1;
            _contacts.Add(contact);
        }

        public static void DeleteContact(int contactId) 
        {
            var contact = _contacts.FirstOrDefault( x => x.ContactId == contactId);
            if (contact == null)
            {
                return;
            }
            _contacts.Remove(contact);
        }

        public static List<Contact> SearchContacts(string filterText)
        {
            var contacts = _contacts.Where(x =>!string.IsNullOrWhiteSpace(x.Name) && x.Name.StartsWith(filterText, StringComparison.OrdinalIgnoreCase)).ToList();
            if(contacts == null || contacts.Count <= 0)
            {
                contacts = _contacts.Where(x => !string.IsNullOrWhiteSpace(x.Email) && x.Email.StartsWith(filterText, StringComparison.OrdinalIgnoreCase)).ToList();
            }
            else
            {
                return contacts;
            }
            if (contacts == null || contacts.Count <= 0)
            {
                contacts = _contacts.Where(x => !string.IsNullOrWhiteSpace(x.Phone) && x.Phone.StartsWith(filterText, StringComparison.OrdinalIgnoreCase)).ToList();
            }
            else
            {
                return contacts;
            }
            if (contacts == null || contacts.Count <= 0)
            {
                contacts = _contacts.Where(x => !string.IsNullOrWhiteSpace(x.Address) && x.Address.StartsWith(filterText, StringComparison.OrdinalIgnoreCase)).ToList();
            }
            else
            {
                return contacts;
            }
            return contacts;
        }


    }
}

创建页面

这个Demo需要用到一个工具包CommunityToolkit.Maui,通过nuget管理安装

安装完以后需要在MauiProgram.cs中引用 builder.UseMauiCommunityToolkit();

image.png

新建页面之前新建文件夹Views存放视图页面

创建组件视图,先新建一个Controls文件夹

创建一个组件ContactControl.xaml,选择ContentView

image.png

ContactControl.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"
             x:Class="Contacts.Maui.Views.Controls.ContactControl"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit">
    <!--垂直布局
    Spacing每个组件的间隔
    -->
    <VerticalStackLayout Spacing="10" Margin="20,20,20,0">
        <Frame Padding="10,0,10,0">
            <HorizontalStackLayout>
                <Label Text="Name" VerticalOptions="Center" WidthRequest="60"></Label>
                <Entry x:Name="entryName" VerticalOptions="Center">
                    <Entry.Behaviors>
                        <!--toolkit工具包的文本验证Flags触发方式-->
                        <toolkit:TextValidationBehavior 
                        x:Name="nameValidator"
                        Flags="ValidateOnAttaching,ValidateOnValueChanged"
                        MinimumLength="1"/>
                    </Entry.Behaviors>
                </Entry>
            </HorizontalStackLayout>
        </Frame>

        <Frame Padding="10,0,10,0">
            <HorizontalStackLayout>
                <Label Text="Email" VerticalOptions="Center" WidthRequest="60"></Label>
                <Entry x:Name="entryEmail"  VerticalOptions="Center">
                    <Entry.Behaviors>
                        <!--多重验证-->
                        <toolkit:MultiValidationBehavior 
                        x:Name="emailValidator"
                        Flags="ValidateOnAttaching,ValidateOnValueChanged">
                            <toolkit:TextValidationBehavior 
                            Flags="ValidateOnAttaching,ValidateOnValueChanged"
                            MinimumLength="1"
                            toolkit:MultiValidationBehavior.Error="Email is required."/>
                            <toolkit:EmailValidationBehavior 
                            Flags="ValidateOnAttaching,ValidateOnValueChanged"
                            MinimumLength="1"
                            toolkit:MultiValidationBehavior.Error="Email format is invalid."/>
                        </toolkit:MultiValidationBehavior>
                    </Entry.Behaviors>
                </Entry>
            </HorizontalStackLayout>
        </Frame>
        <Frame Padding="10,0,10,0">
            <HorizontalStackLayout>
                <Label Text="Phone" VerticalOptions="Center" WidthRequest="60"></Label>
                <Entry x:Name="entryPhone"  VerticalOptions="Center"></Entry>
            </HorizontalStackLayout>
        </Frame>

        <Frame Padding="10,0,10,0">
            <HorizontalStackLayout>
                <Label Text="Address" VerticalOptions="Center" WidthRequest="60"></Label>
                <Entry x:Name="entryAddress"  VerticalOptions="Center"></Entry>
            </HorizontalStackLayout>
        </Frame>
        <Button x:Name="btnSave" Text="Save" Clicked="btnSave_Clicked"></Button>
        <Button x:Name="btnCancel" Text="Cancel" Clicked="btnCancel_Clicked"></Button>

    </VerticalStackLayout>
</ContentView>

ContactControl.xmal.cs:

namespace Contacts.Maui.Views.Controls;

public partial class ContactControl : ContentView
{
    public ContactControl()
    {
        InitializeComponent();
    }
    //类似于vue中的defindEmits
    public event EventHandler<string> OnError;
    public event EventHandler<EventArgs> OnSave;
    public event EventHandler<EventArgs> OnCancel;
    public string Name { get { return entryName.Text; } set { entryName.Text = value; } }
    public string Email { get { return entryEmail.Text; } set { entryEmail.Text = value; } }
    public string Address { get { return entryAddress.Text; } set { entryAddress.Text = value; } }
    public string Phone { get { return entryPhone.Text; } set { entryPhone.Text = value; } }

    private void btnSave_Clicked(object sender, EventArgs e)
    {
        if (nameValidator.IsNotValid)
        {
            OnError?.Invoke(sender,"Name is required.");
            return;
        }
        if (emailValidator.IsNotValid)
        {
            var message = "";
            foreach (var error in emailValidator.Errors)
            {
                message += error + "\n";
            }
            OnError?.Invoke(sender,message);
            return;
        }
        OnSave?.Invoke(sender,e);
    }

    private void btnCancel_Clicked(object sender, EventArgs e)
    {
        OnCancel?.Invoke(sender,e);
    }
}

新建AddContactPage,EditContactPage,ContactsPage页面,选择ContentPage

image.png

最终视图文件:

image.png

最终效果:

image.png

toolkit地址:github.com/CommunityTo…

仓库地址:gitee.com/zzh16430/ma…