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

这篇具有很好参考价值的文章主要介绍了[MAUI]在.NET MAUI中实现可拖拽排序列表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


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

[MAUI]在.NET MAUI中实现可拖拽排序列表,.NET MAUI,产品设计,.net,xamarin

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

创建可拖放控件

新建.NET MAUI项目,命名Tile

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

启用拖动

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

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

启用放置

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

属性 类型 描述
AllowDrop bool 指明手势识别器附加到的元素能否为放置目标。 此属性的默认值为 true。
DragOverCommand ICommand 在拖动源被拖动到放置目标上时执行。
DragOverCommandParameter object 是传递给 DragOverCommand 的参数。
DragLeaveCommand ICommand 在拖动源被拖至放置目标上时执行。
DragLeaveCommandParameter object 是传递给 DragLeaveCommand 的参数。
DropCommand ICommand 在拖动源被放置到放置目标上时执行。
DropCommandParameter object 是传递给 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目录下创建不同大小的磁贴控件,如下图所示。

[MAUI]在.NET MAUI中实现可拖拽排序列表,.NET MAUI,产品设计,.net,xamarin

在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的多态。

[MAUI]在.NET MAUI中实现可拖拽排序列表,.NET MAUI,产品设计,.net,xamarin

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

[MAUI]在.NET MAUI中实现可拖拽排序列表,.NET MAUI,产品设计,.net,xamarin

创建占位控件,用于指示松开手指时,控件将放置的位置区域,在这里绑定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>

最终效果

[MAUI]在.NET MAUI中实现可拖拽排序列表,.NET MAUI,产品设计,.net,xamarin

项目地址

Github:maui-samples

关注我,学习更多.NET MAUI开发知识!文章来源地址https://www.toymoban.com/news/detail-652027.html

到了这里,关于[MAUI]在.NET MAUI中实现可拖拽排序列表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端弹窗可拖拽功能实现

           前端弹窗可拖拽功能主要实现思路就是监听鼠标移动事件,根据鼠标位置实时修改弹窗距离父级窗口(或者屏幕,根据需求设置)的left和right,但是考虑到鼠标拖拽一般都是在div的标题栏处发生,鼠标按下的位置不可能是弹窗的左上角位置,为此需要计算 鼠标按下的

    2024年02月19日
    浏览(46)
  • 实现ElementUI tab标签可拖拽

    通过sortablejs实现 参考:https://blog.csdn.net/wangjiecsdn/article/details/121995534

    2024年01月22日
    浏览(69)
  • 可拖拽编辑的流程图X6

     先上图

    2024年02月11日
    浏览(42)
  • vue 进度条组件(可拖拽可点击)

    在日常的开发当中,随着项目的需求复杂化,自定义组件也越来越常见,而且扩展性也比一些组件库要更加全面,比如视频播放器的进度条。 可自定义设置以下属性: 当前进度value,默认50 是否可拖拽isDrag,默认true 设置最小值min,默认0 设置最大值max,默认100 进度条颜色

    2024年02月16日
    浏览(35)
  • Qt编写可拖拽的自定义控件

    一直想做一个像卡牌游戏一样的,可以拖动卡片,实现改变位置,顺序交换的效果,今天我们一起来尝试一下。 类名为Card h文件 cpp文件 我们完成了一个很简单的200*400的圆角卡片 在主界面中展示看看 widget.h widget.cpp 运行后的效果: 首先要实现控件拖动,需要有2个要素,1:

    2024年02月11日
    浏览(46)
  • vue3实现组件可拖拽 vuedraggable

    npm i -S vuedraggable@next 中文文档,里面有完整代码案例,值得一看 vue.draggable vue3 版本在工作台中的应用场景 - itxst.com

    2024年02月13日
    浏览(39)
  • Vue3封装可拖拽的弹窗

    核心代码(复制就可以使用了) 使用方式

    2024年01月19日
    浏览(41)
  • 【微信小程序-原生开发】列表 - 拖拽排序(官方组件 movable-area 和 movable-view 的用法)

    实现逻辑详见代码的注释 需要根据各项的内容,调整或动态生成 ITEM_HEIGHT 值 因 movable-view 是绝对定位,不方便实现水平居中,所以设定 width: 100%; 占满宽度

    2024年02月11日
    浏览(83)
  • 基于vue的可拖拽设计的报表看板设计器

    gitee上的不错项目,基于vue实现的可拖拽的看板设计器可以自由搭配颜色和图标,开发者可以只关注业务数据接口,前端不擅长的人员可以直接轻松上手。 1.可支持的元素 文字,边框,常见图表,柱形图,折线饼图等等,还有一些列表数据,当然还支持iframe嵌套,当组件满足

    2024年02月11日
    浏览(59)
  • QML可拉伸、可拖拽为独立窗口的UI框架

    本文来源于项目预研,根据项目需求,需要新的客户端软件且使用qml实现。之前没有使用过qml,也是通过这个demo进行学习。以下时项目需求: 1.界面分模块,可调整模块大小 2.模块可通过拖拽为独立窗口 最终效果如下图所示: 首先是分模块可调整大小,可以使用Qt已经封装

    2024年02月09日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包