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

[MAUI]在.NET MAUI中实现可拖拽排序列表

文章目录

    • 创建可拖放控件
    • 创建绑定服务类
      • 拖拽(Drag)
      • 拖拽悬停,经过(DragOver)
      • 释放(Drop)
    • 创建页面元素
    • 最终效果
    • 项目地址

.NET MAUI 中提供了拖放(drag-drop)手势识别器,允许用户通过拖动手势来移动控件。在这篇文章中,我们将学习如何使用拖放手势识别器来实现可拖拽排序列表。在本例中,列表中显示不同大小的磁贴(Tile)并且可以拖拽排序。

在这里插入图片描述

使用.NET MAU实现跨平台支持,本项目可运行于Android、iOS平台。

创建可拖放控件

新建.NET MAUI项目,命名Tile

当手指触碰可拖拽区域超过一定时长(不同平台下时长不一定相同,如在Android中是1s)时,将触发拖动手势。
手指离开屏幕时,将触发放置手势。

启用拖动

为页面视图控件创建拖动手势识别器(DragGestureRecognizer), 它定义了以下属性:

属性类型描述
CanDragbool指明手势识别器附加到的控件能否为拖动源。 此属性的默认值为 true。
CanDragbool指明手势识别器附加到的控件能否为拖动源。 此属性的默认值为 true。
DragStartingCommandICommand在第一次识别拖动手势时执行。
DragStartingCommandParameterobject是传递给 DragStartingCommand 的参数。
DropCompletedCommandICommand在放置拖动源时执行。
DropCompletedCommandParameterobject是传递给 DropCompletedCommand 的参数。

启用放置

为页面视图控件创建放置手势识别器(DropGestureRecognizer), 它定义了以下属性:

属性类型描述
AllowDropbool指明手势识别器附加到的元素能否为放置目标。 此属性的默认值为 true。
DragOverCommandICommand在拖动源被拖动到放置目标上时执行。
DragOverCommandParameterobject是传递给 DragOverCommand 的参数。
DragLeaveCommandICommand在拖动源被拖至放置目标上时执行。
DragLeaveCommandParameterobject是传递给 DragLeaveCommand 的参数。
DropCommandICommand在拖动源被放置到放置目标上时执行。
DropCommandParameterobject是传递给 DropCommand 的参数。

创建可拖拽控件的绑定类,实现IDraggableItem接口,定义拖动相关的属性和命令。

public interface IDraggableItem
{bool IsBeingDraggedOver { get; set; }bool IsBeingDragged { get; set; }Command Dragged { get; set; }Command DraggedOver { get; set; }Command DragLeave { get; set; }Command Dropped { get; set; }object DraggedItem { get; set; }object DropPlaceHolderItem { get; set; }
}

Dragged: 拖拽开始时触发的命令。
DraggedOver: 拖拽控件悬停在当前控件上方时触发的命令。
DragLeave: 拖拽控件离开当前控件时触发的命令。
Dropped: 拖拽控件放置在当前控件上方时触发的命令。

IsBeingDragged 为true时,通知当前控件正在被拖拽。
IsBeingDraggedOver 为true时,通知当前控件正在有拖拽控件悬停在其上方。

DraggedItem: 正在拖拽的控件。
DropPlaceHolderItem: 悬停在其上方时的控件,即当前控件的占位控件。

此时可拖拽控件为磁贴片段(TileSegement), 创建一个类用于描述磁贴可显示的属性,如标题、描述、图标、颜色等。

public class TileSegment 
{public string Title { get; set; }public string Type { get; set; }public string Desc { get; set; }public string Icon { get; set; }public Color Color { get; set; }
}

创建绑定服务类

创建可拖拽控件的绑定服务类TileSegmentService,继承ObservableObject,并实现IDraggableItem接口。

public class TileSegmentService : ObservableObject, ITileSegmentService
{...
}

拖拽(Drag)

拖拽开始时,将IsBeingDragged设置为true,通知当前控件正在被拖拽,同时将DraggedItem设置为当前控件。

private void OnDragged(object item)
{IsBeingDragged=true;DraggedItem=item;
}

拖拽悬停,经过(DragOver)

拖拽控件悬停在当前控件上方时,将IsBeingDraggedOver设置为true,通知当前控件正在有拖拽控件悬停在其上方,同时在服务列表中寻找当前正在被拖拽的服务,将DropPlaceHolderItem设置为当前控件。

private void OnDraggedOver(object item)
{if (!IsBeingDragged && item!=null){IsBeingDraggedOver=true;var itemToMove = Container.TileSegments.First(i => i.IsBeingDragged);if (itemToMove.DraggedItem!=null){DropPlaceHolderItem=itemToMove.DraggedItem;}}}

离开控件上方时,IsBeingDraggedOver设置为false

private void OnDragLeave(object item)
{IsBeingDraggedOver = false;
}

释放(Drop)

拖拽完成时,获取当前正在被拖拽的控件,将其从服务列表中移除,然后将其插入到当前控件的位置,通知当前控件拖拽完成。

private void OnDropped(object item)
{var itemToMove = Container.TileSegments.First(i => i.IsBeingDragged);if (itemToMove == null ||  itemToMove == this)return;Container.TileSegments.Remove(itemToMove);var insertAtIndex = Container.TileSegments.IndexOf(this);Container.TileSegments.Insert(insertAtIndex, itemToMove);itemToMove.IsBeingDragged = false;IsBeingDraggedOver = false;DraggedItem=null;}

完整的TileSegmentService代码如下:

public class TileSegmentService : ObservableObject, ITileSegmentService
{public TileSegmentService(TileSegment tileSegment){Remove = new Command(RemoveAction);TileSegment = tileSegment;Dragged = new Command(OnDragged);DraggedOver = new Command(OnDraggedOver);DragLeave = new Command(OnDragLeave);Dropped = new Command(i => OnDropped(i));}private void OnDragged(object item){IsBeingDragged=true;}private void OnDraggedOver(object item){if (!IsBeingDragged && item!=null){IsBeingDraggedOver=true;var itemToMove = Container.TileSegments.First(i => i.IsBeingDragged);if (itemToMove.DraggedItem!=null){DropPlaceHolderItem=itemToMove.DraggedItem;}}}private object _draggedItem;public object DraggedItem{get { return _draggedItem; }set{_draggedItem = value;OnPropertyChanged();}}private object _dropPlaceHolderItem;public object DropPlaceHolderItem{get { return _dropPlaceHolderItem; }set{_dropPlaceHolderItem = value;OnPropertyChanged();}}private void OnDragLeave(object item){IsBeingDraggedOver = false;DraggedItem = null;}private void OnDropped(object item){var itemToMove = Container.TileSegments.First(i => i.IsBeingDragged);if (itemToMove == null ||  itemToMove == this)return;Container.TileSegments.Remove(itemToMove);var insertAtIndex = Container.TileSegments.IndexOf(this);Container.TileSegments.Insert(insertAtIndex, itemToMove);itemToMove.IsBeingDragged = false;IsBeingDraggedOver = false;DraggedItem=null;}private async void RemoveAction(object obj){if (Container is ITileSegmentServiceContainer){(Container as ITileSegmentServiceContainer).RemoveSegment.Execute(this);}}public IReadOnlyTileSegmentServiceContainer Container { get; set; }private TileSegment tileSegment;public TileSegment TileSegment{get { return tileSegment; }set{tileSegment = value;OnPropertyChanged();}}private bool _isBeingDragged;public bool IsBeingDragged{get { return _isBeingDragged; }set{_isBeingDragged = value;OnPropertyChanged();}}private bool _isBeingDraggedOver;public bool IsBeingDraggedOver{get { return _isBeingDraggedOver; }set{_isBeingDraggedOver = value;OnPropertyChanged();}}public Command Remove { get; set; }public Command Dragged { get; set; }public Command DraggedOver { get; set; }public Command DragLeave { get; set; }public Command Dropped { get; set; }
}

创建页面元素

在Controls目录下创建不同大小的磁贴控件,如下图所示。

在这里插入图片描述

在MainPage中创建CollectionView,用于将磁贴元素以列表形式展示。

<CollectionView Grid.Row="1"x:Name="MainCollectionView"ItemsSource="{Binding TileSegments}"ItemTemplate="{StaticResource TileSegmentDataTemplateSelector}"><CollectionView.ItemsLayout><LinearItemsLayout Orientation="Vertical" /></CollectionView.ItemsLayout>
</CollectionView>

创建MainPageViewModel,创建绑定服务类集合TileSegments,初始化中添加一些不同颜色,大小的磁贴,并将TileSegementService.Container设置为自己(this)。

不同大小的磁贴通过绑定相应的数据,使用不同的数据模板进行展示。请阅读博文 [MAUI程序设计]界面多态与实现,了解如何实现列表Item的多态。

在这里插入图片描述

在MainPage中创建磁贴片段数据模板选择器(TileSegmentDataTemplateSelector),用于根据磁贴片段的大小选择不同的数据模板。

<DataTemplate x:Key="SmallSegment"><controls1:SmallSegmentView  Margin="0,5"ControlTemplate="{StaticResource TileSegmentTemplate}"></controls1:SmallSegmentView>
</DataTemplate>
<DataTemplate x:Key="MediumSegment"><controls1:MediumSegmentView Margin="0,5"ControlTemplate="{StaticResource TileSegmentTemplate}"></controls1:MediumSegmentView>
</DataTemplate>
<DataTemplate x:Key="LargeSegment"><controls1:LargeSegmentView Margin="0,5"ControlTemplate="{StaticResource TileSegmentTemplate}"></controls1:LargeSegmentView>
</DataTemplate>
<controls1:TileSegmentDataTemplateSelector x:Key="TileSegmentDataTemplateSelector"ResourcesContainer="{x:Reference Main}" />

创建磁贴控件模板TileSegmentTemplate,并在此指定DropGestureRecognizer

<ControlTemplate x:Key="TileSegmentTemplate"><ContentView><StackLayout><StackLayout.GestureRecognizers><DropGestureRecognizer AllowDrop="True"DragLeaveCommand="{TemplateBinding BindingContext.DragLeave}"DragLeaveCommandParameter="{TemplateBinding}"DragOverCommand="{TemplateBinding BindingContext.DraggedOver}"DragOverCommandParameter="{TemplateBinding}"DropCommand="{TemplateBinding BindingContext.Dropped}"DropCommandParameter="{TemplateBinding}" /></StackLayout.GestureRecognizers></StackLayout></ContentView>
</ControlTemplate>

创建磁贴控件外观Layout,<ContentPresenter />处将呈现磁贴片段的内容。在Layout指定DragGestureRecognizer。

<Border x:Name="ContentLayout"Margin="0"><Grid><Grid.GestureRecognizers><DragGestureRecognizer CanDrag="True"DragStartingCommand="{TemplateBinding BindingContext.Dragged}"DragStartingCommandParameter="{TemplateBinding}" /></Grid.GestureRecognizers><ContentPresenter /><Button CornerRadius="100"HeightRequest="20"WidthRequest="20"Padding="0"BackgroundColor="Red"TextColor="White"Command="{TemplateBinding BindingContext.Remove}"Text="×"HorizontalOptions="End"VerticalOptions="Start"></Button></Grid>
</Border>

在这里插入图片描述

创建占位控件,用于指示松开手指时,控件将放置的位置区域,在这里绑定DropPlaceHolderItem的高度和宽度。

<Border StrokeThickness="4"StrokeDashArray="2 2"StrokeDashOffset="6"Stroke="black"HorizontalOptions="Center"IsVisible="{TemplateBinding BindingContext.IsBeingDraggedOver}"><Grid HeightRequest="{TemplateBinding BindingContext.DropPlaceHolderItem.Height}"WidthRequest="{TemplateBinding BindingContext.DropPlaceHolderItem.Width}"><Label HorizontalTextAlignment="Center"VerticalOptions="Center"Text="松开手指将放置条目至此处"></Label></Grid>
</Border>

最终效果

在这里插入图片描述

项目地址

Github:maui-samples

关注我,学习更多.NET MAUI开发知识!

相关文章:

[MAUI]在.NET MAUI中实现可拖拽排序列表

文章目录 创建可拖放控件创建绑定服务类拖拽&#xff08;Drag&#xff09;拖拽悬停&#xff0c;经过&#xff08;DragOver&#xff09;释放&#xff08;Drop&#xff09; 创建页面元素最终效果项目地址 .NET MAUI 中提供了拖放(drag-drop)手势识别器&#xff0c;允许用户通过拖…...

Linux 内核内存管理 pfn_to_online_page宏

文章目录 一、Memory Hotplug1.1 简介1.2 热插拔事件通知机制 二、pfn_to_online_page2.1 pfn_to_online_page2.2 pfn_to_section_nr2.3 online_section_nr 参考资料 一、Memory Hotplug 1.1 简介 Linux 内存热插拔&#xff08;Memory Hotplug&#xff09;是指在运行时动态增…...

『C语言初阶』第八章 -结构体

前言 今天小羊又来给铁汁们分享关于C语言的结构体&#xff0c;在C语言中&#xff0c;结构体类型属于一种构造类型&#xff08;其他的构造类型还有&#xff1a;数组类型&#xff0c;联合类型&#xff09;&#xff0c;今天我们主要简单了解一下结构体。 一、结构体是什么&#x…...

chatgpt汇编,两个关于chatgpt的问题

1.用C写一个高级的可自主学习的AI语言模型 编写一个高级的可自主学习的AI语言模型是一个复杂且庞大的工程&#xff0c;需要深入的专业知识和大量的时间精力。下面是一个简化的示例&#xff0c;演示如何使用C实现一个基本的可自主学习的AI语言模型。 首先&#xff0c;你需要定…...

ps丢失d3dcompiler_47.dll怎么办,启动无反应,分享三个解决方法

d3dcompiler_47.dll64位是windows系统中重要的dll文件&#xff0c;缺少了它可能会引起部分软件或者游戏不能运行。 如果系统出现“找不到d3dcompiler_47.dll”或“d3dcompiler_47.dll丢失”等错误信息&#xff0c;那么我们就该着手修复它。 先带了解一下d3dcompiler_47.dll是什…...

第三章nginx详解

nginx&#xff1a;高性能&#xff0c;轻量级的web服务软件。 特点&#xff1a; 1&#xff0c;稳定性高。&#xff08;没有apache稳定&#xff09; 2&#xff0c;系统资源消耗地较低。&#xff08;处理http请求的并发能力非常高&#xff0c;单台物理服务器可以处理30000-5000…...

【二叉树前沿篇】树

【二叉树前沿篇】树 1 树的概念2. 树的相关概念3. 树的表示4. 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是…...

python3 0基础学习----数据结构(基础+练习)

python 0基础学习笔记之数据结构 &#x1f4da; 几种常见数据结构列表 &#xff08;List&#xff09;1. 定义2. 实例&#xff1a;3. 列表中常用方法.append(要添加内容) 向列表末尾添加数据.extend(列表) 将可迭代对象逐个添加到列表中.insert(索引&#xff0c;插入内容) 向指定…...

计算机科学中的“旅行商问题”

题目&#xff1a;旅行商问题&#xff08;Traveling Salesman Problem&#xff09; 当初为何收藏&#xff1a;我收藏了这个题目是因为它是一个经典而富有挑战性的组合优化问题&#xff0c;涉及到计算机科学、算法设计和实际应用领域。我认为这个问题可以展示出算法设计的重要性…...

QT:自定义控件(Connect使用,子控件连接)

自定义控件封装&#xff1a; 1.添加新文件&#xff08;设计师界面类&#xff09;&#xff0c;创建子页面 &#xff0c;放自己想要的控件 2.在主页面中使用子控件 :新建一个widget-![在这里插入图片描述](https://img-blog.csdnimg.cn/95ed8015343e4c56a3914853950eff4c.png#pi…...

目录——车载网络安全

本文主要汇总车载网络安全专栏文章,以方便各位读者阅读。 ISO21434 概述(一) ISO21434 组织网络安全管理(二) ISO21434 项目网络安全管理(三) ISO21434 分布式网络安全(四) SO21434 持续进行的网络安全(五) ISO21434 概念阶段网络安全(六)...

Visual Studio 如何放大代码字体的大小

1.打开Visual Studio&#xff0c;新建一个程序&#xff0c;一段代码&#xff0c;为接下去的操作做好准备。单击菜单栏的【工具】选项。 2.在跳出来菜单中找到【选项】&#xff08;一般在最后一项&#xff09;&#xff0c;然后单击。跳出新的窗口。 3.跳出新的窗口后&#xff…...

Verilog同步FIFO设计

同步FIFO(synchronous)的写时钟和读时钟为同一个时钟&#xff0c;FIFO内部所有逻辑都是同步逻辑&#xff0c;常常用于交互数据缓冲。 异步FIFO&#xff1a;数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous) 典型同步FIFO有三部分组成: &#xff08;1&#xff0…...

Php“牵手”lazada商品详情页数据采集方法,lazadaAPI接口申请指南

lazada详情接口 API 是开放平台提供的一种 API 接口&#xff0c;它可以帮助开发者获取商品的详细信息&#xff0c;包括商品的标题、描述、图片等信息。在电商平台的开发中&#xff0c;详情接口API是非常常用的 API&#xff0c;因此本文将详细介绍详情接口 API 的使用。 一、la…...

Sentinel 规则持久化

文章目录 Sentinel 规则持久化一、修改order-service服务1.引入依赖2.配置nacos地址 第二步修改非常麻烦&#xff0c;可以略过&#xff0c;直接使用已经打好包的来使用二、修改sentinel-dashboard源码1. 解压2. 修改nacos依赖3. 添加nacos支持4. 修改nacos地址5. 配置nacos数据…...

元宇宙时代超高清视音频技术白皮书关于流媒体协议和媒体传输解读

流媒体协议 元宇宙业务场景对流媒体传输的实时性和互动性提出了更高的要求&#xff0c;这就需要在传统的 RTMP、SRT、 HLS 等基础上增加实时互动的支持。实时互动&#xff0c;指在远程条件下沟通、协作&#xff0c;可随时随地接入、实时地传递虚实融合的多维信息&#xff0c;身…...

【计算机设计大赛】国赛一等奖项目分享——基于多端融合的化工安全生产监管可视化系统

文章目录 一、计算机设计大赛国赛一等奖二、项目背景三、项目简介四、系统架构五、系统功能结构六、项目特色&#xff08;1&#xff09;多端融合&#xff08;2&#xff09;数据可视化&#xff08;3&#xff09;计算机视觉&#xff08;目标检测&#xff09; 七、系统界面设计&am…...

深入理解【二叉树】

&#x1f4d9;作者简介&#xff1a; 清水加冰&#xff0c;目前大二在读&#xff0c;正在学习C/C、Python、操作系统、数据库等。 &#x1f4d8;相关专栏&#xff1a;C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 &#x1f44d…...

RequestRespons

文章目录 Request&Respons1 Request和Response的概述2 Request对象2.1 Request继承体系2.2 Request获取请求数据2.2.1 获取请求行数据2.2.2 获取请求头数据2.2.3 获取请求体数据2.2.4 获取请求参数的通用方式 2.3 IDEA快速创建Servlet2.4 请求参数中文乱码问题2.4.1 POST请…...

UniApp 使用命令创建页面的详细指南

系列文章目录 文章目录 系列文章目录前言一、安装Uni-CLI二、创建页面三、页面创建命令四、页面结构五、页面使用总结 前言 UniApp是一款跨平台的前端框架&#xff0c;可以用于开发同时运行在多个平台&#xff08;如微信小程序、H5、App等&#xff09;的应用程序。本文将详细介…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...