【问题标题】:How to move a ListViewItem part of a Group in place of another ListViewItem using the same ListView?如何使用相同的 ListView 移动组的 ListViewItem 部分来代替另一个 ListViewItem?
【发布时间】:2022-06-17 03:49:05
【问题描述】:

下面我试图移动item4 代替item5,我期望的操作是项目4 位于item5 之上,而项目5 位于item4 之下:

下面我试图将 item4 移动到 item5 的位置,我期望的操作是 item4 位于 item5item5item4 之上:

我也不能移动一个项目来代替另一个,有人可以帮忙吗?

我在这里留下完整的代码,包括设计:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp3
{
    public partial class Form1 : Form
    {
        private ListView listView;
        private ListViewItem currentVieItem;
        public Form1()
        {
            InitializeComponent();
            
            createListview();
        }

        public void createListview()
        {
            listView = new ListView();
            listView.AllowDrop = true;

            listView.ItemDrag += new ItemDragEventHandler(OnItemDrag);
            listView.DragOver += new DragEventHandler(OnDragOver);
            listView.DragDrop += new DragEventHandler(OnDragDrop);

            // MY CODE HERE
            ColumnHeader columnHeader = new ColumnHeader();
            listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            columnHeader});
            listView.HideSelection = false;
            int indexGroup = 1;
            ListViewGroup group = new ListViewGroup();
            for (int i = 0; i < 100; i++)
            {
                if (i % 5 == 0)
                {
                    string nmGroup = $"Group {indexGroup}";
                    group = new ListViewGroup() { Header = nmGroup};
                    listView.Groups.Add(group);
                    indexGroup++;
                }
                listView.Items.Add(new ListViewItem() { Text = $"Item {i}", Group = group });
            }
            listView.Location = new System.Drawing.Point(12, 12);
            listView.Name = "listView1";
            listView.Size = new System.Drawing.Size(436, 494);
            listView.TabIndex = 0;
            listView.UseCompatibleStateImageBehavior = false;
            listView.View = System.Windows.Forms.View.Details;
            // 
            // columnHeader1
            // 
            columnHeader.Text = "Items";
            columnHeader.Width = 382;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(937, 600);
            this.Controls.Add(listView);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
        }

        public void OnDragOver(object sender, DragEventArgs e)
        {
            var pos = listView.PointToClient(new Point(e.X, e.Y));
            var hit = listView.HitTest(pos);
            this.currentVieItem = hit.Item;
            this.Text = hit.Item?.Index.ToString();

            if (e.Data.GetDataPresent(typeof(ListView.SelectedListViewItemCollection)))
            {
                e.Effect = e.AllowedEffect;
            }
        }

        public void OnDragDrop(object sender, DragEventArgs e)
        {
            if (currentVieItem == null) return;

            int index = currentVieItem.Index;
            this.Text = index.ToString();

            if (e.Data.GetDataPresent(typeof(ListView.SelectedListViewItemCollection)))
            {
                if (e.Effect == DragDropEffects.Move)
                {
                    foreach (ListViewItem current in (ListView.SelectedListViewItemCollection)e.Data.GetData(typeof(ListView.SelectedListViewItemCollection)))
                    {
                        current.Remove();
                        current.Group = currentVieItem.Group;
                        listView.Items.Insert(index, current);
                        index++;
                    }
                }                   
            }
        }

        public void OnItemDrag(object sender, ItemDragEventArgs e)
        {
            listView.DoDragDrop(listView.SelectedItems, DragDropEffects.Move);
        }
    }
}

【问题讨论】:

    标签: c# .net winforms listview


    【解决方案1】:

    虽然ListViewItem.Group.Items.Insert() 方法按预期工作(项目插入到指定位置),但组本身不会根据分配给新项目的索引(组索引)对其项目进行排序。
    现有的项目顺序优先,所以最后添加新项目。

    当您更改项目的顺序时,如果您根据自己的条件为 ListView 分配自定义 ListViewItemSorter,则可以重新定义此行为。

    您可以删除currentViewItem 引用,这不是必需的。查看代码。
    DragOver 事件处理程序也不是必需的,但它最终可用于显示 InsertionMark

    保持ListView的初始化,改变handlers的名字:

    // [...]
    listView.ItemDrag += OnLVItemDrag;
    listView.DragEnter += OnLVDragEnter;
    listView.DragDrop += OnLVDragDrop;
    // [...]
    

    替换事件处理程序中的代码:

    public void OnLVItemDrag(object sender, ItemDragEventArgs e)
    {
        var lv = sender as ListView;
        lv.DoDragDrop(lv.SelectedItems, DragDropEffects.Move);
    }
    
    private void OnLVDragEnter(object sender, DragEventArgs e) => e.Effect = e.AllowedEffect;
    
    public void OnLVDragDrop(object sender, DragEventArgs e)
    {
        var data = e.Data.GetData(typeof(ListView.SelectedListViewItemCollection)) as ListView.SelectedListViewItemCollection;
        if (data is null) return;
    
        var lv = sender as ListView;
        var nearestItem = lv.HitTest(lv.PointToClient(new Point(e.X, e.Y))).Item;
        if (nearestItem is null) return;
    
        var groupIndex = nearestItem.Group.Items.IndexOf(nearestItem);
    
        if (e.Effect == DragDropEffects.Move) {
            foreach (ListViewItem item in data) {
                nearestItem.Group.Items.Remove(item);
                nearestItem.Group.Items.Insert(groupIndex, item);
                if (nearestItem.Group != item.Group) groupIndex++;
            }
            lv.ListViewItemSorter = new ListViewSorter(sortByIndex: true, useGroupIndex: true);
        }
        // else{} Handle other operations
    }
    

    自定义ListViewItemSorter对象

    此自定义对象可以根据 SubItem 的 Text 属性(默认)或 Items 的索引或组索引对 ListView 进行排序。

    using System.Collections;
    
    class ListViewSorter : IComparer {
        int columnIdx = 0;
        bool indexSort = false;
        bool sortGroups = false;
    
        public ListViewSorter() { }
        public ListViewSorter(int column) => columnIdx = column;
    
        public ListViewSorter(bool sortByIndex, bool useGroupIndex) { 
            sortGroups = useGroupIndex;
            indexSort = sortByIndex;
        }
    
        public int Compare(object lvi1, object lvi2)
        {
            var item1 = lvi1 as ListViewItem;
            var item2 = lvi2 as ListViewItem;
            if (indexSort) {
                if (sortGroups && item1.Group != null && item2.Group != null) {
                    int idx1 = item1.Group.Items.IndexOf(item1);
                    int idx2 = item2.Group.Items.IndexOf(item2);
                    return idx1 - idx2;
                }
                else {
                    return item1.Index - item2.Index;
                }
            }
            else {
                return string.Compare(
                    item1.SubItems[columnIdx].Text, 
                    item2.SubItems[columnIdx].Text);
            }
        }
    }
    

    这就是它的工作原理:

    【讨论】:

    • 似乎不适用于 .net6 ,奇怪的是“nearestItem.Group.Items.Insert(groupIndex, item)” 行使项目仅添加到集合中但未更新到视图中。随着nearestItem.Group.Items.Count 的增加,每次拖放后我都会看到项目未触及。否则, var data = e.Data.GetData(typeof(ListView.SelectedListViewItemCollection)) as ListView.SelectedListViewItemCollection 也始终为空。奇怪!
    • 错误:在同一组内移动项目向下不起作用。您需要在DragDrop 事件中删除相同组的项目,然后再插入它/它们if (nearestItem.Group == item.Group) nearestItem.Group.Items.Remove(item);。因此,如果是这样,请不要groupIndex++;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-18
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 2012-09-10
    相关资源
    最近更新 更多