[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开发知识!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql驱动包下载

第一步&#xff1a;下载地址 MySQL :: Download Connector/J 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a;解压 第五步&#xff1a;找到驱动包&#xff0c;放入项目使用即可

管理类联考——逻辑——真题篇——按知识分类——汇总篇——三、综合推理

文章目录 题-综合推理-分类1-排序真题&#xff08;2016-54-55&#xff09;-难度最高*****-综合推理-分类1-排序-画表排除法真题&#xff08;2016-54&#xff09;真题&#xff08;2016-55&#xff09;真题&#xff08;2019-36&#xff09;-综合推理-分类1-排序真题&#xff08;2…

【AIGC】 国内版聊天GPT

国内版聊天GPT 引言一、国内平台二、简单体验2.1 提问2.2 角色扮演2.3 总结画图 引言 ChatGPT是OpenAI发开的聊天程序&#xff0c;功能强大&#xff0c;可快速获取信息&#xff0c;节省用户时间和精力&#xff0c;提供个性化的服务。目前国产ChatGPT&#xff0c;比如文心一言&a…

OJ练习第151题——克隆图

克隆图 力扣链接&#xff1a;133. 克隆图 题目描述 给你无向 连通 图中一个节点的引用&#xff0c;请你返回该图的 深拷贝&#xff08;克隆&#xff09;。 示例 分析 对于一张图而言&#xff0c;它的深拷贝即构建一张与原图结构&#xff0c;值均一样的图&#xff0c;但是…

Electron基础篇

人生有些事,错过一时,就错过一世。 官网&#xff1a;简介 | Electron Electron-大多用来写桌面端软件 Electron介绍 Electront的核心组成是Chromium、Node.js以及内置的Native API&#xff0c;其中Chromium为Electron提供强大的UI能力&#xff0c;可以在不考虑兼容的情况下利…

使用神卓互联内网穿透搭建远程访问公司ERP系统

神卓互联是一款企业级内网穿透软件&#xff0c;可以将内网中的服务映射到公网上&#xff0c;实现内网服务的访问。通过神卓互联&#xff0c;您可以远程访问ERP系统。在使用神卓互联进行内网穿透时&#xff0c;您只需要在生成的公网地址后面加上ERP系统的端口号&#xff0c;即可…

NVIDIA vGPU License许可服务器高可用全套部署秘籍

第1章 前言 近期遇到比较多的场景使用vGPU&#xff0c;比如Citrix 3D场景、Horizon 3D场景&#xff0c;还有AI等&#xff0c;都需要使用显卡设计研发等&#xff0c;此时许可服务器尤为重要&#xff0c;许可断掉会出现掉帧等情况&#xff0c;我们此次教大家部署HA许可服务器。 …

【.net】本地调试运行只能用localhost的问题

【.net】本地调试运行只能用localhost的问题 解决方案 找到到项目目录下 隐藏文件夹 .vs /项目名称/config/applicationhost.config <bindings><binding protocol"http" bindingInformation"*:1738:localhost" /></bindings> 再加一条你…

职业学院物联网实训室建设方案

一、概述 1.1专业背景 物联网&#xff08;Internet of Things&#xff09;被称为继计算机、互联网之后世界信息产业第三次浪潮&#xff0c;它并非一个全新的技术领域&#xff0c;而是现代信息技术发展到一定阶段后出现的一种聚合性应用与技术提升&#xff0c;是随着传感网、通…

麦肯锡发布《2023科技趋势展望报告》,生成式AI、下一代软件开发成为趋势,软件测试如何贴合趋势?

近日&#xff0c;麦肯锡公司发布了《2023科技趋势展望报告》。报告列出了15个趋势&#xff0c;并把他们分为5大类&#xff0c;人工智能革命、构建数字未来、计算和连接的前沿、尖端工程技术和可持续发展。 类别一&#xff1a;人工智能革命 生成式AI 生成型人工智能标志着人工智…

CSRF

文章目录 CSRF(get)CSRF(post)CSRF Token CSRF(get) 根据提示的用户信息登录 点击修改个人信息 开启bp代理&#xff0c;点击submit 拦截到请求数据包 浏览器关闭代理 刷新页面 CSRF(post) 使用BP生成CSRF POC post请求伪造&#xff0c;可以通过钓鱼网站&#xff0c;诱导用户去…

css 实现文字横向循环滚动

实现效果 思路 ## 直接上代码,html部分 //我这里是用的uniapp <view class"weather_info_wrap"><view class"weather_info">当前多云&#xff0c;今晚8点转晴&#xff0c;明天有雨&#xff0c;温度32摄氏度。</view><view class&qu…

解决IDEA tomcat控制台只有server日志

解决IDEA tomcat控制台只有server日志 确认tomcatxxx/conf/logging.properties文件是否存在&#xff0c;存在就会有。前提是在run configuration配置了打印多个日志

uniapp封装组件,选中后右上角显示对号√样式(通过css实现)

效果&#xff1a; 一、组件封装 1、在项目根目录下创建components文件夹&#xff0c;自定义组件名称&#xff0c;我定义的是xc-button 2、封装组件代码 <template><view class"handle-btn"><view :class"handleIdCode 1 ? select : unSelec…

【电商领域】Axure在线购物商城小程序原型图,品牌自营垂直电商APP原型

作品概况 页面数量&#xff1a;共 60 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;网上商城、品牌自营商城、商城模块插件 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本作品为品牌自营网上商城…

无涯教程-Perl - warn函数

描述 此函数将LIST的值打印到STDERR。基本上与die函数相同,除了不对出口进行任何调用并且在eval语句内不引发异常。这对于引发错误而不导致脚本过早终止很有用。 如果变量$包含一个值(来自先前的eval调用),并且LIST为空,则$的值将以。\t.caught打印。附加到末尾。如果$和LIST…

MySQL数据库概述

MySQL数据库概述 1 SQL SQL语句大小写不敏感。 SQL语句末尾应该使用分号结束。 1.1 SQL语句及相关操作示例 DDL&#xff1a;数据定义语言&#xff0c;负责数据库定义、数据库对象定义&#xff0c;由CREATE、ALTER与DROP三个语法所组成DML&#xff1a;数据操作语言&#xff…

关于小程序收集用户手机号行为的规范

手机号在日常生活中被广泛使用&#xff0c;是重要的用户个人信息&#xff0c;小程序开发者应在用户明确同意的前提下&#xff0c;依法合规地处理用户的手机号信息。 而部分开发者在处理用户手机号过程中&#xff0c;存在不规范收集行为&#xff0c;影响了用户的正常使用体验&a…

k8s简介、虚拟机快速搭建k8s集群、集群管理方式及K8S工作原理和组件介绍

文章目录 1、k8s简介1.1、部署方式的变迁1.2、定义1.3、Kubernetes提供的功能 2、虚拟机快速搭建k8s集群2.1、虚拟机配置&#xff08;centos7 2G内存2个处理器&#xff09;2.2、基础环境准备2.3、docker安装&#xff08;易踩坑&#xff09;2.4、安装k8s组件2.5、master节点部署…

提高批量爬虫工作效率

大家好&#xff01;作为一名专业的爬虫程序员&#xff0c;我今天要和大家分享一些关于提高批量爬虫工作效率的实用技巧。无论你是要批量采集图片、文本还是视频数据&#xff0c;这些经验都能帮助你在大规模数据采集中事半功倍。废话不多说&#xff0c;让我们开始吧&#xff01;…