DataGridView的拖拽换行功能

本文介绍了如何在Windows Forms应用程序中实现DataGridView控件的拖拽换行功能。通过设置AllowDrop、MultiSelect和SelectionMode属性,结合Mouse事件处理,实现在DataGridView中拖动行并调整顺序。拖放过程中使用了BinaryFormatter进行对象序列化和反序列化,以在拖放操作中传递数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
  首先设置DataGridView控件的AllowDrop属性为True,MultiSelect为False,SelectionMode为FullRowSelect 
 */
namespace DragRow
{
    public partial class MainForm : Form
    {
        public BindingList<Employee> EmployeeList;
        
        // 拖动的源数据行索引
        private int indexOfItemUnderMouseToDrag = -1;
        // 拖动的目标数据行索引
        private int indexOfItemUnderMouseToDrop = -1;
        // 拖动中的鼠标所在位置的当前行索引
        private int indexOfItemUnderMouseOver = -1;
        // 不启用拖放的鼠标范围
        private Rectangle dragBoxFromMouseDown = Rectangle.Empty;

        public MainForm()
        {
            InitializeComponent();            
        }
              
        private void MainForm_Load(object sender, EventArgs e)
        {
            EmployeeList = new BindingList<Employee>();
            EmployeeList.ListChanged += new ListChangedEventHandler(EmployeeList_ListChanged);

            List<Employee> empList = Employee.GetEmployeeList();
         
            foreach (var item in empList)
            {
                EmployeeList.Add(item);
            }
            dataGridView.DataSource = EmployeeList;
        }

        //为的是No列始终是重新排列的
        void EmployeeList_ListChanged(object sender, ListChangedEventArgs e)
        {
            int index = 1;
            foreach (var item in this.EmployeeList)
            {
                item.No = index++;
            }
        }

        private void dataGridView_MouseDown(object sender, MouseEventArgs e)
        {
            // 通过鼠标按下的位置获取所在行的信息
            var hitTest = dataGridView.HitTest(e.X, e.Y);
            if (hitTest.Type != DataGridViewHitTestType.Cell)
                return;

            // 记下拖动源数据行的索引及已鼠标按下坐标为中心的不会开始拖动的范围
            indexOfItemUnderMouseToDrag = hitTest.RowIndex;
            if (indexOfItemUnderMouseToDrag > -1)
            {
                Size dragSize = SystemInformation.DragSize;
                dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)), dragSize);
            }
            else
                dragBoxFromMouseDown = Rectangle.Empty;
        }

        private void dataGridView_MouseUp(object sender, MouseEventArgs e)
        {
            // 释放鼠标按键时清空变量为默认值
            dragBoxFromMouseDown = Rectangle.Empty;
        }

        private void dataGridView_MouseMove(object sender, MouseEventArgs e)
        {
            // 不是鼠标左键按下时移动
            if ((e.Button & MouseButtons.Left) != MouseButtons.Left)
                return;
            // 如果鼠标在不启用拖动的范围内
            if (dragBoxFromMouseDown == Rectangle.Empty || dragBoxFromMouseDown.Contains(e.X, e.Y))
                return;
            // 如果源数据行索引值不正确
            if (indexOfItemUnderMouseToDrag < 0)
                return;

            // 开始拖动,第一个参数表示要拖动的数据,可以自定义,一般是源数据行
            var row = dataGridView.Rows[indexOfItemUnderMouseToDrag];
            string strRow = DataSerializer.SerializeObject(row.DataBoundItem as Employee );
            DragDropEffects dropEffect = dataGridView.DoDragDrop(strRow, DragDropEffects.All);

            //拖动过程结束后清除拖动位置行的红线效果
            OnRowDragOver(-1);
        }

        private void dataGridView_DragOver(object sender, DragEventArgs e)
        {
            // 把屏幕坐标转换成控件坐标
            Point p = dataGridView.PointToClient(new Point(e.X, e.Y));

            // 通过鼠标按下的位置获取所在行的信息
            // 如果不是在数据行或者在源数据行上则不能作为拖放的目标
            var hitTest = dataGridView.HitTest(p.X, p.Y);
            if (hitTest.Type != DataGridViewHitTestType.Cell || hitTest.RowIndex == indexOfItemUnderMouseToDrag)
            {
                e.Effect = DragDropEffects.None;
                OnRowDragOver(-1);
                return;
            }

            // 设置为作为拖放移动的目标
            e.Effect = DragDropEffects.Move;
            // 通知目标行重绘
            OnRowDragOver(hitTest.RowIndex);
        }

        private void dataGridView_DragDrop(object sender, DragEventArgs e)
        {
            // 把屏幕坐标转换成控件坐标
            Point p = dataGridView.PointToClient(new Point(e.X, e.Y));

            // 如果当前位置不是数据行
            // 或者刚好是源数据行的下一行(本示例中假定拖放操作为拖放至目标行的上方)
            // 则不进行任何操作
            var hitTest = dataGridView.HitTest(p.X, p.Y);
            if (hitTest.Type != DataGridViewHitTestType.Cell || hitTest.RowIndex == indexOfItemUnderMouseToDrag + 1)
                return;

            indexOfItemUnderMouseToDrop = hitTest.RowIndex;

            // * 执行拖放操作(执行的逻辑按实际需要)
            string strRow = (string)e.Data.GetData(DataFormats.StringFormat);
            Employee aimItem = (Employee)DataSerializer.DeserializeObject(strRow);
            Employee tempModel = new Employee();
            this.CopyModel<Employee>(aimItem, tempModel);
            EmployeeList.RemoveAt(indexOfItemUnderMouseToDrag);               
            
            if (indexOfItemUnderMouseToDrag < indexOfItemUnderMouseToDrop)
                indexOfItemUnderMouseToDrop--;
            EmployeeList.Insert(indexOfItemUnderMouseToDrop, tempModel );            
        }

        private void dataGridView_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
        {
            // 如果当前行是鼠标拖放过程的所在行
            if (e.RowIndex == indexOfItemUnderMouseOver)
                e.Graphics.FillRectangle(Brushes.Red, e.RowBounds.X, e.RowBounds.Y, e.RowBounds.Width, 2);
        }

        private void OnRowDragOver(int rowIndex)
        {
            // 如果和上次导致重绘的行是同一行则无需重绘
            if (indexOfItemUnderMouseOver == rowIndex)
                return;

            int old = indexOfItemUnderMouseOver;
            indexOfItemUnderMouseOver = rowIndex;

            // 去掉原有行的红线
            if (old > -1)
                dataGridView.InvalidateRow(old);

            // 绘制新行的红线
            if (rowIndex > -1)
                dataGridView.InvalidateRow(rowIndex);
        }
        private void CopyModel<T>(T oriEntity, T newEntity)
        {
            if (oriEntity == null || newEntity == null)
                return;
            Type type = typeof(T);
            PropertyInfo[] proInfoArray = type.GetProperties();
            foreach (PropertyInfo proInfo in proInfoArray)
            {
                if (proInfo.CanWrite)
                {
                    object oriObj = type.GetProperty(proInfo.Name).GetValue(oriEntity, null);
                    type.GetProperty(proInfo.Name).SetValue(newEntity, oriObj, null);
                }
            }
        }
    }
}


namespace DragRow
{
    public class DataSerializer
    {
        /// <summary>
        /// Binary Serialize
        /// </summary>
        public static string SerializeObject(object obj)
        {
            StringBuilder sb = new StringBuilder();
 
            if (obj == null)
                return null;
            MemoryStream ms = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(ms, obj);
            ms.Position = 0;
            byte[] bytes = new byte[ms.Length];
            ms.Read(bytes, 0, bytes.Length);
            ms.Close();
            string str = Convert.ToBase64String(bytes);
            return str;
        }
 
        /// <summary>
        /// Binary Deserialize
        /// </summary>
        public static object DeserializeObject(string str)
        {
            byte[] bytes = Convert.FromBase64String(str);
            object obj = null;
            if (bytes == null)
                return obj;
            MemoryStream ms = new MemoryStream(bytes);
            ms.Position = 0;
            BinaryFormatter formatter = new BinaryFormatter();
            obj = formatter.Deserialize(ms);
            ms.Close();
            return obj;
        }
    }
}

namespace DragRow
{
    [Serializable]
    public class Employee
    {
        public int No { get; set; }
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }

        public Employee()
        { }

        public Employee(int id, string name, int age)
        {
            this.Id = id;
            this.Name = name;
            this.Age = age;
        }

        public static List<Employee> GetEmployeeList()
        {
            List<Employee> EmpList = new List<Employee>
            {
                new Employee(1,"Cui",26),
                new Employee(2,"Liu",54),
                new Employee(3,"Zhang",67),
                new Employee(4,"Li",14),
                new Employee(5,"Cao",32),
                new Employee(6,"Luo",23),
                new Employee(7,"Wang",43),
                new Employee(8,"Qiao",34)
            }.ToList();
            return EmpList;
        }
    }

    
}



                
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值