代码之家  ›  专栏  ›  技术社区  ›  BlueRaja - Danny Pflughoeft

在datagridview上移动行时的可视标记

  •  6
  • BlueRaja - Danny Pflughoeft  · 技术社区  · 15 年前

    用户在我的datagridview中上下拖动行。我有拖拽的逻辑,但我想有一个黑色的标记,指示在我放开鼠标后行将被放置在哪里。

    Example from Microsoft Access http://img718.imageshack.us/img718/8171/accessdrag.png
    来自Microsoft Access的示例;我要拖动行而不是列

    有人知道我会怎么做吗?这是内置的,还是我必须画我自己的标记(如果是,我该怎么做)?

    谢谢!

    3 回复  |  直到 15 年前
        1
  •  3
  •   Igby Largeman    15 年前

    几年前我做了一个树视图;不记得具体是怎么做的,但是考虑使用 MouseMove DataGridView的事件。

    当拖动发生时,鼠标移动处理程序应:

    • 获取 鼠标(mouseeventargs包含 坐标,但我认为它们是屏幕坐标,所以你可以使用 DataGridView.PointToClient() 将它们转换为相对的)
    • 确定哪个行在那个x处 位置(有办法吗?如果没有,您可以通过将行+行标题高度相加来计算,但请记住,网格可能已经滚动过)
    • 突出显示该行或将其变暗 边界。一种可以使一个边界变暗的方法是更改 DataGridViewRow.DividerHeight 财产。
    • 当鼠标移到外面 行,恢复到以前的状态 看。

    如果要对鼠标下的行外观进行自定义(而不是仅使用可用属性),可以使用 DataGridView.RowPostPaint 事件。如果实现此事件的处理程序(仅在将行拖到另一行上时使用),则可以使用粗体画笔重新绘制行的上边框或下边框。 MSDN example here.

        2
  •  3
  •   BlueRaja - Danny Pflughoeft    15 年前

    这是我最终的解决办法。此控件:

    • 允许将一行拖到另一行
    • 使用分隔符突出显示插入位置
    • 当用户在拖动时到达控件边缘时自动滚动
    • 支持控件的多个实例
      • 可以将行从一个实例拖到另一个实例
      • 在控件的所有实例中将只选择一行
    • 自定义行突出显示

    你可以用这个代码做任何你想做的事 (无担保等)

    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Linq;
    using System.Windows.Forms;
    
    namespace CAM_Products.General_Controls
    {
        public class DataGridViewWithDraggableRows : DataGridView
        {
            private int? _predictedInsertIndex; //Index to draw divider at.  Null means no divider
            private Timer _autoScrollTimer;
            private int _scrollDirection;
            private static DataGridViewRow _selectedRow;
            private bool _ignoreSelectionChanged;
            private static event EventHandler<EventArgs> OverallSelectionChanged;
            private SolidBrush _dividerBrush;
            private Pen _selectionPen;
    
            #region Designer properties
            /// <summary>
            /// The color of the divider displayed between rows while dragging
            /// </summary>
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            [Category("Appearance")]
            [Description("The color of the divider displayed between rows while dragging")]
            public Color DividerColor
            {
                get { return _dividerBrush.Color; }
                set { _dividerBrush = new SolidBrush(value); }
            }
    
            /// <summary>
            /// The color of the border drawn around the selected row
            /// </summary>
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            [Category("Appearance")]
            [Description("The color of the border drawn around the selected row")]
            public Color SelectionColor
            {
                get { return _selectionPen.Color; }
                set { _selectionPen = new Pen(value); }
            }
    
            /// <summary>
            /// Height (in pixels) of the divider to display
            /// </summary>
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            [Category("Appearance")]
            [Description("Height (in pixels) of the divider to display")]
            [DefaultValue(4)]
            public int DividerHeight { get; set; }
    
            /// <summary>
            /// Width (in pixels) of the border around the selected row
            /// </summary>
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            [Category("Appearance")]
            [Description("Width (in pixels) of the border around the selected row")]
            [DefaultValue(3)]
            public int SelectionWidth { get; set; }
            #endregion
    
            #region Form setup
            public DataGridViewWithDraggableRows()
            {
                InitializeProperties();
                SetupTimer();
            }
    
            private void InitializeProperties()
            {
                #region Code stolen from designer
                this.AllowDrop = true;
                this.AllowUserToAddRows = false;
                this.AllowUserToDeleteRows = false;
                this.AllowUserToOrderColumns = true;
                this.AllowUserToResizeRows = false;
                this.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
                this.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;
                this.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
                this.EnableHeadersVisualStyles = false;
                this.MultiSelect = false;
                this.ReadOnly = true;
                this.RowHeadersVisible = false;
                this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
                this.CellMouseDown += dataGridView1_CellMouseDown;
                this.DragOver += dataGridView1_DragOver;
                this.DragLeave += dataGridView1_DragLeave;
                this.DragEnter += dataGridView1_DragEnter;
                this.Paint += dataGridView1_Paint_Selection;
                this.Paint += dataGridView1_Paint_RowDivider;
                this.DefaultCellStyleChanged += dataGridView1_DefaultcellStyleChanged;
                this.Scroll += dataGridView1_Scroll;
                #endregion
    
                _ignoreSelectionChanged = false;
                OverallSelectionChanged += OnOverallSelectionChanged;
                _dividerBrush = new SolidBrush(Color.Red);
                _selectionPen = new Pen(Color.Blue);
                DividerHeight = 4;
                SelectionWidth = 3;
            }
            #endregion
    
            #region Selection
            /// <summary>
            /// All instances of this class share an event, so that only one row
            /// can be selected throughout all instances.
            /// This method is called when a row is selected on any DataGridView
            /// </summary>
            private void OnOverallSelectionChanged(object sender, EventArgs e)
            {
                if(sender != this && SelectedRows.Count != 0)
                {
                    ClearSelection();
                    Invalidate();
                }
            }
    
            protected override void OnSelectionChanged(EventArgs e)
            {
                if(_ignoreSelectionChanged)
                    return;
    
                if(SelectedRows.Count != 1 || SelectedRows[0] != _selectedRow)
                {
                    _ignoreSelectionChanged = true; //Following lines cause event to be raised again
                    if(_selectedRow == null || _selectedRow.DataGridView != this)
                    {
                        ClearSelection();
                    }
                    else
                    {
                        _selectedRow.Selected = true; //Deny new selection
                        if(OverallSelectionChanged != null)
                            OverallSelectionChanged(this, EventArgs.Empty);
                    }
                    _ignoreSelectionChanged = false;
                }
                else
                {
                    base.OnSelectionChanged(e);
                    if(OverallSelectionChanged != null)
                        OverallSelectionChanged(this, EventArgs.Empty);
                }
            }
    
            public void SelectRow(int rowIndex)
            {
                _selectedRow = Rows[rowIndex];
                _selectedRow.Selected = true;
                Invalidate();
            }
            #endregion
    
            #region Selection highlighting
            private void dataGridView1_Paint_Selection(object sender, PaintEventArgs e)
            {
                if(_selectedRow == null || _selectedRow.DataGridView != this)
                    return;
    
                Rectangle displayRect = GetRowDisplayRectangle(_selectedRow.Index, false);
                if(displayRect.Height == 0)
                    return;
    
                _selectionPen.Width = SelectionWidth;
                int heightAdjust = (int)Math.Ceiling((float)SelectionWidth/2);
                e.Graphics.DrawRectangle(_selectionPen, displayRect.X - 1, displayRect.Y - heightAdjust,
                                         displayRect.Width, displayRect.Height + SelectionWidth - 1);
            }
    
            private void dataGridView1_DefaultcellStyleChanged(object sender, EventArgs e)
            {
                DefaultCellStyle.SelectionBackColor = DefaultCellStyle.BackColor;
                DefaultCellStyle.SelectionForeColor = DefaultCellStyle.ForeColor;
            }
    
            private void dataGridView1_Scroll(object sender, ScrollEventArgs e)
            {
                Invalidate();
            }
            #endregion
    
            #region Drag-and-drop
            protected override void OnDragDrop(DragEventArgs args)
            {
                if(args.Effect == DragDropEffects.None)
                    return;
    
                //Convert to coordinates within client (instead of screen-coordinates)
                Point clientPoint = PointToClient(new Point(args.X, args.Y));
    
                //Get index of row to insert into
                DataGridViewRow dragFromRow = (DataGridViewRow)args.Data.GetData(typeof(DataGridViewRow));
                int newRowIndex = GetNewRowIndex(clientPoint.Y);
    
                //Adjust index if both rows belong to same DataGridView, due to removal of row
                if(dragFromRow.DataGridView == this && dragFromRow.Index < newRowIndex)
                {
                    newRowIndex--;
                }
    
                //Clean up
                RemoveHighlighting();
                _autoScrollTimer.Enabled = false;
    
                //Only go through the trouble if we're actually moving the row
                if(dragFromRow.DataGridView != this || newRowIndex != dragFromRow.Index)
                {
                    //Insert the row
                    MoveDraggedRow(dragFromRow, newRowIndex);
    
                    //Let everyone know the selection has changed
                    SelectRow(newRowIndex);
                }
                base.OnDragDrop(args);
            }
    
            private void dataGridView1_DragLeave(object sender, EventArgs e1)
            {
                RemoveHighlighting();
                _autoScrollTimer.Enabled = false;
            }
    
            private void dataGridView1_DragEnter(object sender, DragEventArgs e)
            {
                e.Effect = (e.Data.GetDataPresent(typeof(DataGridViewRow))
                                ? DragDropEffects.Move
                                : DragDropEffects.None);
            }
    
            private void dataGridView1_DragOver(object sender, DragEventArgs e)
            {
                if(e.Effect == DragDropEffects.None)
                    return;
    
                Point clientPoint = PointToClient(new Point(e.X, e.Y));
    
                //Note: For some reason, HitTest is failing when clientPoint.Y = dataGridView1.Height-1.
                // I have no idea why.
                // clientPoint.Y is always 0 <= clientPoint.Y < dataGridView1.Height
                if(clientPoint.Y < Height - 1)
                {
                    int newRowIndex = GetNewRowIndex(clientPoint.Y);
                    HighlightInsertPosition(newRowIndex);
                    StartAutoscrollTimer(e);
                }
            }
    
            private void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
            {
                if(e.Button == MouseButtons.Left && e.RowIndex >= 0)
                {
                    SelectRow(e.RowIndex);
                    var dragObject = Rows[e.RowIndex];
                    DoDragDrop(dragObject, DragDropEffects.Move);
                    //TODO: Any way to make this *not* happen if they only click?
                }
            }
    
            /// <summary>
            /// Based on the mouse position, determines where the new row would
            /// be inserted if the user were to release the mouse-button right now
            /// </summary>
            /// <param name="clientY">
            /// The y-coordinate of the mouse, given with respectto the control
            /// (not the screen)
            /// </param>
            private int GetNewRowIndex(int clientY)
            {
                int lastRowIndex = Rows.Count - 1;
    
                //DataGridView has no cells
                if(Rows.Count == 0)
                    return 0;
    
                //Dragged above the DataGridView
                if(clientY < GetRowDisplayRectangle(0, true).Top)
                    return 0;
    
                //Dragged below the DataGridView
                int bottom = GetRowDisplayRectangle(lastRowIndex, true).Bottom;
                if(bottom > 0 && clientY >= bottom)
                    return lastRowIndex + 1;
    
                //Dragged onto one of the cells.  Depending on where in cell,
                // insert before or after row.
                var hittest = HitTest(2, clientY); //Don't care about X coordinate
    
                if(hittest.RowIndex == -1)
                {
                    //This should only happen when midway scrolled down the page,
                    //and user drags over header-columns
                    //Grab the index of the current top (displayed) row
                    return FirstDisplayedScrollingRowIndex;
                }
    
                //If we are hovering over the upper-quarter of the row, place above;
                // otherwise below.  Experimenting shows that placing above at 1/4 
                //works better than at 1/2 or always below
                if(clientY < GetRowDisplayRectangle(hittest.RowIndex, false).Top
                   + Rows[hittest.RowIndex].Height/4)
                    return hittest.RowIndex;
                return hittest.RowIndex + 1;
            }
    
            private void MoveDraggedRow(DataGridViewRow dragFromRow, int newRowIndex)
            {
                dragFromRow.DataGridView.Rows.Remove(dragFromRow);
                Rows.Insert(newRowIndex, dragFromRow);
            }
            #endregion
    
            #region Drop-and-drop highlighting
            //Draw the actual row-divider
            private void dataGridView1_Paint_RowDivider(object sender, PaintEventArgs e)
            {
                if(_predictedInsertIndex != null)
                {
                    e.Graphics.FillRectangle(_dividerBrush, GetHighlightRectangle());
                }
            }
    
            private Rectangle GetHighlightRectangle()
            {
                int width = DisplayRectangle.Width - 2;
    
                int relativeY = (_predictedInsertIndex > 0
                                     ? GetRowDisplayRectangle((int)_predictedInsertIndex - 1, false).Bottom
                                     : Columns[0].HeaderCell.Size.Height);
    
                if(relativeY == 0)
                    relativeY = GetRowDisplayRectangle(FirstDisplayedScrollingRowIndex, true).Top;
                int locationX = Location.X + 1;
                int locationY = relativeY - (int)Math.Ceiling((double)DividerHeight/2);
                return new Rectangle(locationX, locationY, width, DividerHeight);
            }
    
            private void HighlightInsertPosition(int rowIndex)
            {
                if(_predictedInsertIndex == rowIndex)
                    return;
    
                Rectangle oldRect = GetHighlightRectangle();
                _predictedInsertIndex = rowIndex;
                Rectangle newRect = GetHighlightRectangle();
    
                Invalidate(oldRect);
                Invalidate(newRect);
            }
    
            private void RemoveHighlighting()
            {
                if(_predictedInsertIndex != null)
                {
                    Rectangle oldRect = GetHighlightRectangle();
                    _predictedInsertIndex = null;
                    Invalidate(oldRect);
                }
                else
                {
                    Invalidate();
                }
            }
            #endregion
    
            #region Autoscroll
            private void SetupTimer()
            {
                _autoScrollTimer = new Timer
                {
                    Interval = 250,
                    Enabled = false
                };
                _autoScrollTimer.Tick += OnAutoscrollTimerTick;
            }
    
            private void StartAutoscrollTimer(DragEventArgs args)
            {
                Point position = PointToClient(new Point(args.X, args.Y));
    
                if(position.Y <= Font.Height/2 &&
                   FirstDisplayedScrollingRowIndex > 0)
                {
                    //Near top, scroll up
                    _scrollDirection = -1;
                    _autoScrollTimer.Enabled = true;
                }
                else if(position.Y >= ClientSize.Height - Font.Height/2 &&
                        FirstDisplayedScrollingRowIndex < Rows.Count - 1)
                {
                    //Near bottom, scroll down
                    _scrollDirection = 1;
                    _autoScrollTimer.Enabled = true;
                }
                else
                {
                    _autoScrollTimer.Enabled = false;
                }
            }
    
            private void OnAutoscrollTimerTick(object sender, EventArgs e)
            {
                //Scroll up/down
                FirstDisplayedScrollingRowIndex += _scrollDirection;
            }
            #endregion
        }
    }
    
        3
  •  1
  •   Mason    15 年前

    我正在处理的应用程序将标记作为一个高度为1、背景色为1的单独面板对象。在实际进行拖放操作之前,面板对象保持隐藏状态。此函数在dragover事件上触发,实现大部分逻辑:

    public static void frameG_dragover(Form current_form, DataGridView FRAMEG, Panel drag_row_indicator, Point mousePos)
        {
            int FRAMEG_Row_Height = FRAMEG.RowTemplate.Height;
            int FRAMEG_Height = FRAMEG.Height;
            int Loc_X = FRAMEG.Location.X + 2;
    
            Point clientPoint = FRAMEG.PointToClient(mousePos);
            int CurRow = FRAMEG.HitTest(clientPoint.X, clientPoint.Y).RowIndex;
            int Loc_Y = 0;
            if (CurRow != -1)
            {
                Loc_Y = FRAMEG.Location.Y + ((FRAMEG.Rows[CurRow].Index + 1) * FRAMEG_Row_Height) - FRAMEG.VerticalScrollingOffset;
            }
            else
            {
                Loc_Y = FRAMEG.Location.Y + (FRAMEG.Rows.Count + 1) * FRAMEG_Row_Height;
            }
    
            int width_c = FRAMEG.Columns[0].Width + FRAMEG.Columns[1].Width + FRAMEG.Columns[2].Width;
    
            if ((Loc_Y > (FRAMEG.Location.Y)) && (Loc_Y < (FRAMEG.Location.Y + FRAMEG_Height - FRAMEG_Row_Height))) //+ FRAMEG_Row_Height
            {
                drag_row_indicator.Location = new System.Drawing.Point(Loc_X, Loc_Y);
                drag_row_indicator.Size = new Size(width_c, 1);
            }
    
            if (!drag_row_indicator.Visible)
                drag_row_indicator.Visible = true;
        }
    

    除此之外,您只需在拖放完成或移出datagridview时再次隐藏面板。

    推荐文章