[MAUI]实现动态拖拽排序网格

MAUI 框架安卓入门开发01 界面设计 1.导航栏设计1.1 由于需要水平布局的设计方式,所以外围需要使用一个HorizontalStackLayout 进行包裹着所有的子控件,来达到水平布局的效果。页面大概是这样子,按钮全部由RadioButton 进行重绘 阅读详情


上一章我们使用拖放(drag-drop)手势识别实现了可拖拽排序列表,对于列表中的条目,完整的拖拽排序过程是:
手指触碰条目 -> 拖拽条目 -> 拖拽悬停在另一个条目上方 -> 松开手指 -> 移动条目至此处。

其是在松开手指之后才向列表提交条目位置变更的命令。今天我们换一个写法,将拖拽条目放置在另一个条目上方时,即可将条目位置变更。即实时拖拽排序。

在这里插入图片描述

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

创建页面元素

新建.NET MAUI项目,命名Tile

本章的实例中使用网格布局的CollectionView控件作为Tile的容器。

CollectionView 的其他布局方式请参考官方文档 指定 CollectionView 布局

创建GridTilesPage.xaml

在页面中创建CollectionView,

<CollectionView Grid.Row="1"
                x:Name="MainCollectionView"
                ItemsSource="{Binding TileSegments}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <ContentView HeightRequest="110" WidthRequest="110" HorizontalOptions="Center" VerticalOptions="Center">
                <StackLayout>
                    <StackLayout.GestureRecognizers>
                        <DropGestureRecognizer AllowDrop="True"
                                                DragLeaveCommand="{Binding DragLeave}"
                                                DragLeaveCommandParameter="{Binding}"
                                                DragOverCommand="{Binding DraggedOver}"
                                                DragOverCommandParameter="{Binding}"
                                                DropCommand="{Binding Dropped}"
                                                DropCommandParameter="{Binding}" />
                    </StackLayout.GestureRecognizers>

                    <Border x:Name="ContentLayout"
                            StrokeThickness="0"
                            Margin="0">
                        <Grid>
                            <Grid.GestureRecognizers>
                                <DragGestureRecognizer CanDrag="True"
                                                        DragStartingCommand="{Binding Dragged}"
                                                        DragStartingCommandParameter="{Binding}" />
                            </Grid.GestureRecognizers>

                            <controls1:TileSegmentView HeightRequest="100"
                                                        WidthRequest="100"
                                                        Margin="5,5">

                            </controls1:TileSegmentView>
                            <Button CornerRadius="100"
                                    HeightRequest="20"
                                    WidthRequest="20"
                                    Padding="0"
                                    Margin="2,2"
                                    BackgroundColor="Red"
                                    TextColor="White"
                                    Command="{Binding Remove}"
                                    Text="×"
                                    HorizontalOptions="End"
                                    VerticalOptions="Start"></Button>
                        </Grid>
                    </Border>
                </StackLayout>
            </ContentView>

        </DataTemplate>

    </CollectionView.ItemTemplate>
    <CollectionView.ItemsLayout>
        <GridItemsLayout Orientation="Vertical"
                            Span="3" />
    </CollectionView.ItemsLayout>
</CollectionView>

呈现效果如下:

在这里插入图片描述

DropGestureRecognizer中设置了拖拽悬停、离开、放置时的命令,

创建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; }
}

创建可绑定对象

创建GridTilesPageViewModel,创建绑定服务类集合TileSegments。

private ObservableCollection<ITileSegmentService> _tileSegments;

public ObservableCollection<ITileSegmentService> TileSegments
{
    get { return _tileSegments; }
    set
    {
        _tileSegments = value;
        OnPropertyChanged();
    }
}

       

构造函数中初始化一些不同颜色的磁贴,并将TileSegementService.Container设置为自己(this)。

public GridTilesPageViewModel()
{
    TileSegments = new ObservableCollection<ITileSegmentService>();
    CreateSegmentAction("TileSegment", "App1", "Some description here", Colors.LightPink);
    CreateSegmentAction("TileSegment", "App2", "Some description here", Colors.LightGreen);

    ...
}
private ITileSegmentService CreateTileSegmentService(object obj, string title, string desc, Color color)
{
    var type = obj as string;
    var tileSegment = new TileSegment()
    {
        Title = title,
        Type = type,
        Desc = desc,
        Icon = "dotnet_bot.svg",
        Color = color,
    };
    var newModel = new GridTileSegmentService(tileSegment); 
    if (newModel != null)
    {
        newModel.Container = this;
    }
    return newModel;
}

创建绑定服务类

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

创建ICommand属性:Dragged, DraggedOver, DragLeave, Dropped。

订阅PropertyChanged事件以便在属性更改时触发相关操作

public class GridTileSegmentService : ObservableObject, ITileSegmentService
{
    public GridTileSegmentService(TileSegment tileSegment)
    {
        TileSegment = tileSegment;
        Dragged = new Command(OnDragged);
        DraggedOver = new Command(OnDraggedOver);
        DragLeave = new Command(OnDragLeave);
        Dropped = new Command(i => OnDropped(i));
        this.PropertyChanged+=GridTileSegmentService_PropertyChanged;
    }
    ...
}

拖拽(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)
    {

        var itemToMove = Container.TileSegments.First(i => i.IsBeingDragged);
        if (itemToMove.DraggedItem!=null)
        {
            DropPlaceHolderItem=itemToMove.DraggedItem;

        }
        IsBeingDraggedOver=true;

    }
}

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

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

通过订阅PropertyChanged, 在GridTileSegmentService_PropertyChanged方法中响应IsBeingDraggedOver属性的值变更。

当IsBeingDraggedOver为True时代表有拖拽中控件悬停在其上方,DropPlaceHolderItem即为悬停在其上方的控件对象。

此时我们应该将悬停在其上方的控件对象插入到自身的前方,通过获取两者在集合的角标并调用Move()方法。


private void GridTileSegmentService_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName==nameof(this.IsBeingDraggedOver))
    {

        if (this.IsBeingDraggedOver && DropPlaceHolderItem!=null)
        {
            var newIndex = Container.TileSegments.IndexOf(this);
            var oldIndex = Container.TileSegments.IndexOf(DropPlaceHolderItem as ITileSegmentService);
            Container.TileSegments.Move(oldIndex, newIndex);
        }
    }

}

效果如下:

在这里插入图片描述

释放(Drop)

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

private void OnDropped(object item)
{
    var itemToMove = Container.TileSegments.First(i => i.IsBeingDragged);

    if (itemToMove == null)
        return;

    itemToMove.IsBeingDragged = false;
    IsBeingDraggedOver = false;
    DraggedItem=null;
    DropPlaceHolderItem = null;
}

完整的TileSegmentService代码如下:

public class GridTileSegmentService : ObservableObject, ITileSegmentService
{

    public GridTileSegmentService(
        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));
        this.PropertyChanged+=GridTileSegmentService_PropertyChanged;
    }

    private void GridTileSegmentService_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName==nameof(this.IsBeingDraggedOver))
        {

            if (this.IsBeingDraggedOver && DropPlaceHolderItem!=null)
            {
                var newIndex = Container.TileSegments.IndexOf(this);
                var oldIndex = Container.TileSegments.IndexOf(DropPlaceHolderItem as ITileSegmentService);
                Container.TileSegments.Move(oldIndex, newIndex);
            }
        }

    }

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


    }

    private void OnDraggedOver(object item)
    {
        if (!IsBeingDragged && item!=null)
        {

            var itemToMove = Container.TileSegments.First(i => i.IsBeingDragged);
            if (itemToMove.DraggedItem!=null)
            {
                DropPlaceHolderItem=itemToMove.DraggedItem;

            }
            IsBeingDraggedOver=true;

        }
    }


    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;
        DropPlaceHolderItem = null;
    }

    private void OnDropped(object item)
    {
        var itemToMove = Container.TileSegments.First(i => i.IsBeingDragged);

        if (itemToMove == null)
            return;


        itemToMove.IsBeingDragged = false;
        IsBeingDraggedOver = false;
        DraggedItem=null;
        DropPlaceHolderItem = 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
        {
            if (value!=_isBeingDraggedOver)
            {
                _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; }
}

运行程序,此时我们可以看到拖拽控件悬停在其它控件上方时,其它控件会自动调整位置。

限流(Throttle)和防抖(Debounce)

在特定平台的列表控件中更新项目集合时,引发的动画效果会导致列表中的控件位置错乱。

当以比较快的速度,拖拽Tile经过较多的位置时,后面的Tile会短暂地替代原先的位置,导致拖拽中的Tile不在期望的Tile上方,而拖拽中的Tile与错误的Tile产生了交叠从而触发DraggedOver事件,导致错乱。

在这里插入图片描述

在某些机型上甚至会引发错乱的持续循环

一个办法是禁用动画,如在iOS中配置

listView.On<iOS>().SetRowAnimationsEnabled(false);

动效问题最终要解决。由于快速拖拽Tile经过较多的位置频繁触发Move操作,通过限制事件的触发频率,引入限流(Throttle)和防抖(Debounce)机制可以有效地解决这个问题。限流和防抖的作用如下图:

在这里插入图片描述

代码引用自 ThrottleDebounce

在GridTileSegmentService中创建静态限流器对象变量throttledAction。以及全局锁对象throttledLocker。

public static RateLimitedAction throttledAction = Debouncer.Debounce(null, TimeSpan.FromMilliseconds(500), leading: false, trailing: true);

public static object throttledLocker = new object();

改写GridTileSegmentService_PropertyChanged如下:

private void GridTileSegmentService_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName==nameof(this.IsBeingDraggedOver))
    {

        if (this.IsBeingDraggedOver && DropPlaceHolderItem!=null)
        {
            lock (throttledLocker)
            {
                var newIndex = Container.TileSegments.IndexOf(this);
                var oldIndex = Container.TileSegments.IndexOf(DropPlaceHolderItem as ITileSegmentService);

                var originalAction = () =>
                {
                    Container.TileSegments.Move(oldIndex, newIndex);
                };
                throttledAction.Update(originalAction);
                throttledAction.Invoke();
            }
        }
    }

}

此时,在500毫秒内,只会执行一次Move操作。问题解决!

在这里插入图片描述

因为有500毫秒的延迟,Tile响应上感觉没有那么“灵动”,这算是一种牺牲。在不同的平台上可以调整这个时间以达到一种平衡,不知道屏幕前的你有没有更好的方式解决呢?

在这里插入图片描述

项目地址

Github:maui-samples

【MAUI】 xaml CollectionView对绑定的ItemsSource进行过滤 在Maui中,使用CollectionView对ItemsSource进行过滤通常涉及到数据绑定和使用数据转换器(如Behaviors或者Converter)。以下是一个简单的示例,展示如何使用Converter来过滤ItemsSource。 阅读详情

相关推荐

C#工业软件界面开发:控件库选型、核心组件详解与实战集成指南

在工业软件(如SCADA、MES、上位机)开发中,数据可视化是核心需求,它涉及将实时数据、设备状态和工艺流程以直观图形界面呈现。其原理在于通过数据绑定、图形渲染和状态管理,将后台数据流映射为前端的图表、仪表和动画。这项技术的价值在于提升监控效率、辅助决策并保障系统稳定运行,广泛应用于设备监控、生产管理和故障预警等场景。本文聚焦于C#技术栈,深入探讨如何为WinForms或WPF项目选择合适的工业控件库,涵盖从商用套件(如DevExpress、Telerik)到专业可视化库(如SciChart)的对比,并详解

weixin_33875564的博客 305

基于grid布局实现页面元素的动态拖拽替换

本人看见网上的都是基于flex布局的动态拖拽,就想尝试写一下grid布局的拖拽实现一下,于是有了本篇文章,本篇主要都是基于元素实现,文章主要时提供了一个思路,需要时请根据自己的实际需求更改或者封装。觉得不错记得点个赞:

qq_40482342的博客 1096

【MAUI】CollectionView之 垂直网格

App主页或者导航页面中动态按钮的垂直网格布局在 XAML 中,CollectionView 可以通过将其 ItemsLayout 属性设置为 VerticalGrid,在垂直网格中显示其项:

德仔 541

maui界面布局

xaml 布局控件,Grid 网格布局

转眼之间 一路有你 3226

芯片测试中的DC与AC模式详解:如何选择最适合你的测试方案?

本文深入解析了芯片测试中DC(直流)与AC(交流)模式的核心差异与互补性。DC测试关注静态电气特性,如固定型故障;而AC测试则聚焦于芯片在额定工作速度下的动态时序行为。文章从VLSI设计、电路模块、成本约束、可测试性设计和应用场景五大维度,提供了如何根据工艺节点、工作频率及具体需求,在testMode配置、时钟复位策略和输入输出接口设计中,平衡两种测试模式以制定最优方案的决策框架。

bb456的博客 1086

问题:dpkg-deb: error: paste subprocess was killed by signal (Broken pipe)

 1. 问题:Ubuntu安装软件的过程中出现依赖项的问题 “E: Unmet dependencies. Try 'apt-get -f install' with no packages (or specify a solution).” 按照指示执行如下两条命令都没有解决问题。 sudo apt-get -f install sudo apt-get --fix-broken ...

gloria_littlechi的博客 2万+

【打造动态布局新体验】—— 探索Sharpnado.CollectionView(.Maui): 开源界的新星

【打造动态布局新体验】—— 探索Sharpnado.CollectionView(.Maui): 开源界的新星 在移动应用开发的浩瀚星空下,有一颗璀璨的新星正以其独特的光芒闪耀——那就是Sharpnado.CollectionView(.Maui)。这个项目,曾以“HorizontalListView”之名惊艳开发者社区,如今已蜕变升级为更强大、更灵活的解决方案,旨在为跨平台应用的UI设计带来革命...

gitblog_00013的博客 1204

LMK04821时钟芯片SPI配置避坑指南:从寄存器读写到频率校准全流程

本文详细解析了LMK04821时钟芯片的SPI配置全流程,涵盖寄存器读写、频率校准及JESD204B系统设计中的关键技巧。通过实战案例和避坑指南,帮助工程师高效解决时钟输出异常、同步失败等常见问题,提升高速数据采集系统的时钟稳定性。

weixin_29172963的博客 159

Android 实现菜单拖拽排序

本文主角是ItemTouchHelper。它是RecyclerView对于item交互处理的一个「辅助类」,主要用于拖拽以及滑动处理。以接口实现的方式,达到配置简单、逻辑解耦、职责分明的效果,并且支持所有的布局方式。

金戈鐡馬 2772

CTF实战:Flask Session流量解密与内存马路由追踪

本文通过一道CTF实战题目,详细解析了Flask Session流量解密与内存马路由追踪的全过程。文章从破解前端加密密码入手,逐步揭示如何利用泄露的SECRET_KEY解密Session,发现命令执行痕迹,并最终追踪到攻击者植入的内存马路由。内容涵盖Wireshark流量分析、flask-unsign工具使用等实战技巧,为CTF选手和安全研究人员提供了一套完整的Flask应用安全分析思路。

weixin_28309571的博客 306

【推荐100个unity插件】动画重定向,快速在不同角色间共享动画,共用非人形Humanoid(泛型generic)动画——Retarget Pro

Retarget Pro是一款高效的Unity动画重定向工具,可快速在不同角色间共享动画。主要特点包括:1分钟快速设置,支持编辑器和运行时重定向;适用于人形、动物、武器等各类角色;提供基础重定向、IK重定向和FPS专用重定向三种功能模块;通过Rig组件和骨架资产实现骨骼链匹配,支持批量处理。插件包含示例项目和预设资源,操作简便且效果优于Unity原生Humanoid系统,特别适合处理手指等精细动画。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 546

MAUI 框架入门学习03 布局控件介绍

MAUI 是一种标记性语言,每一个标签成对出现的,即有开始标签就要有结束标签。它的写法跟JavaScript 编程语言大同小异 1. .xaml 布局控件 ContentPage 显示单个视图的页面。 ScrollView 表示滚动控件,即页面内容超出范围时,可以进行滚动 VerticalStackLayout 纵向栈垂直布局控件, HoriaontalStackLayout 水平方向布局控件 StackLayout 具备了VerticalStackLayout, HoriaontalS

build2024 4510

YOLO11 改进系列 | 引入2023年 MLCA 的 C3k2_MLCA 模块,近零参数开销的本地-全局通道注意力,适合检测与分割特征增强

本文提出将MLCA(混合局部通道注意力)模块嵌入YOLOv11的C3k2框架中,形成C3k2_MLCA改进结构。该模块通过5×5局部池化和1×1全局池化双路融合,结合ECA式Conv1d生成通道注意力,在几乎不增加参数量的前提下(仅增加66个参数),实现了局部空间信息与全局通道统计的联合建模。相比传统SE和ECA注意力,MLCA保留了空间分布信息,更适合实例分割等需要精细定位的任务。实验表明改进后模型参数量仅增加0.002%,计算量基本不变,是一种高效的注意力增强方案。

qq_44224801的博客 93

vue3拖拽布局+动态组件+自适应布局

/在package.json中dependencies下添加下面插件库,并执行命令npm install。Vue Grid Layout -️ 适用Vue.js的栅格布局系统。可拖动和可调整大小栅格布局的Vue组件。

蒋小姐的博客 9121

【Windows Server 2019】存储服务器的配置和管理——iSCSI的安装和配置(下)

博文介绍了如何使用Windows 10 和 ESXi 验证iSCSI服务器部署成功以及如何管理iSCSI服务 。

VMware, Windows, Linux, Cisco, Container, Kubernetes, Kali 8000

FANUC机器人_CRX协作机器人的开机和示教器的初始化设置基本步骤(二)

FANUC机器人_CRX协作机器人的开机和示教器的初始化设置基本步骤

Robot_PLC_HMI_Automation 2732

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

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

jevonsflash的专栏 2010

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

[MAUI程序设计]界面多态与实现

首先用控件模板定义博客条目的外观,“博客条目”是包含博客标题,内容,以及发布时间等信息的卡片,视觉上呈现圆角矩形的白色不透明卡片效果。博客条目控件是一个基于ContentView控件在页面的资源中,添加如下两个ControlTemplate模板,分别用于手机设备和桌面设备。BlogCardViewPhone用于博客条目在手机设备中的呈现,条目菜单侧滑栏方式展开,我们配置SwipeView控件,作为卡片,用一个Frame框架包裹其内容。

Gefangenes的博客 1575

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

动态添加diV,实现了拖拽功能

New Document function creatediv() { var obj = document.createElement("div"); classA.call(obj); bd.appendChild(obj); } function classA(){ this.style.width="

west8623的专栏 2233

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

前端技术搭建(动态图片)拖拽拼图!!(内含实现原理)

动态拖拽拼图,是将以往的二维移动方块的方式,变成了三维移动方式,目标是将一张图片切割打乱,然后,重组为原图的游戏。通过gif动态图,我开发了一个动态图拖拽拼图网页游戏_哔哩哔哩_bilibili首先实现游戏界面搭建,引入相关文件。我将游戏分为三个部分

redrun博客 1216

vue 拖动 datatransfer 问题_Vue的动态拖放组件Vue.Draggable,实现页面元素动态拖放...

介绍在我们日常开发中可能会面临很多交互上的问题,特别是在Web应用盛行的今天,在之前有段时间的文章中介绍过javascript的拖拽库sortablejs,Vue.Draggable就是基于Sortable.js的允许拖放和视图模型数组同步的组件,避免了我们常说的重复造轮子,有了它省了我们很多事情。它基于Sortable.js并提供Sortable.js的所有功能!开源地址Github:https...

weixin_39766910的博客 556

.NET MAUI布局技巧:灵活使用Grid布局

在.NET MAUI的开发过程中,布局是创建用户界面不可或缺的部分。尤其是当你尝试让某个控件占据布局中的全部剩余空间时,可能会遇到一些挑战。今天,我们来讨论一下如何在.NET MAUI中使用Grid布局来解决这个常见问题。

m0_62153576的博客 729

MAUI劝退:使用grid布局显示表格

MAUI劝退:使用grid布局显示表格

编程之道在明明德在亲民在止于至善 1418

MAUI自定义绘图

于是此类型即可通过实现 Draw 方法,被框架层调用到,从而在 Draw 方法里面执行绘图。例如和官方的例子一样,将此类型命名为 GraphicsDrawable 如以下代码。MAUI 框架里提供了 GraphicsView 元素用来对接 Microsoft.Maui.Graphics 的绘图功能。在新建的项目里面,新建一个类型,让这个类型继承。

转眼之间 一路有你 687

从桌面端到移动端,.NET MAUI为什么对WPF开发人员更简单?

本文将为大家介绍.NET MAUI与WPF之间的共性,及如何帮助开发人员更简单的完成工作!

需要界面开发、IDE工具研发中文教程资料的小伙伴,记得私信我~ 2905

MAUI中的CollectionView高级用法:性能优化与定制

CollectionView(集合视图)是.NET MAUI(Multi-platform App UI)中功能强大的列表控件,提供灵活的布局和高效的数据展示能力。本文将深入探讨CollectionView的高级用法,重点分析性能优化策略与定制化实现,帮助开发者构建流畅、个性化的跨平台应用界面。 ## CollectionView基础架构与工作原理 CollectionView基于现代UI架构...

gitblog_00907的博客 1275
上一篇: [MAUI]在.NET MAUI中实现可拖拽排序列表
下一篇: [MAUI]深入了解.NET MAUI Blazor与Vue的混合开发
林晓lx
林晓lx 2017年优快云移动跨平台大赛优秀奖获得者 2017年优快云移动跨平台大赛优秀奖获得者
博客等级 码龄15年 3761粉丝 123原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林晓lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值