Визуальный маркер, когда движущиеся строки на DataGridView

Пользователи перетаскивают строки вверх и вниз в моем DataGridView. У меня есть вниз стандартная логика перетаскивания, но я хотел бы там быть темным маркером, указывающим, куда строка будет помещена после того, как я отпустил мыши.

Пример от Microsoft Access http://img718.imageshack.us/img718/8171/accessdrag.png
Пример от Microsoft Access; я хочу перетащить строки вместо столбцов

Кто-либо знает, как я пошел бы о выполнении этого? Действительно ли это встроено, или я должен был бы потянуть свой собственный маркер (если так, как я делаю это)?

Спасибо!

6
задан BlueRaja - Danny Pflughoeft 2 April 2010 в 18:21
поделиться

3 ответа

Я делал это для древовидного просмотра пару лет назад; не помню точно как, но рассмотрите возможность использования события MouseMove DataGridView.

Пока происходит перетаскивание, ваш обработчик MouseMove должен:

  • получить относительные координаты мыши (MouseventArgs). мыши (MouseEventArgs содержит координаты, но я думаю, что это экранные координаты, поэтому вы можете использовать DataGridView.PointToClient() для преобразования их в относительные)
  • определить, какая строка находится в данной X (есть ли метод для этого? Если нет, вы можете вычислить его, сложив высоту ряда + высоту заголовка ряда, но помните, что сетка могла быть прокручена)
  • выделите этот ряд или затемните его границу. Одним из способов затемнения границы может быть изменение свойства DataGridViewRow.DividerHeight.
  • когда мышь переместится за пределы этой строки, восстановите ее прежний вид.

Если вы хотите сделать что-то свое с внешним видом строки под мышью (вместо того, чтобы просто использовать доступные свойства), вы можете использовать событие DataGridView.RowPostPaint. Если вы реализуете обработчик этого события, который используется только при перетаскивании строки поверх другой строки, вы можете перекрасить верхнюю или нижнюю границу строки более жирной кистью. Пример из MSDN здесь.

3
ответ дан 17 December 2019 в 00:07
поделиться

Приложение, над которым я работаю, обрабатывает маркер как отдельный объект Panel с высотой 1 и BackColor равным 1. Объект Panel остается скрытым до тех пор, пока фактически не начнется перетаскивание. Эта функция, запускаемая по событию 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.

1
ответ дан 17 December 2019 в 00:07
поделиться

Вот и было мое возможное решение. Этот элемент управления:

  • Позволяет перетаскивать одну строку в другую
  • Подсвечивает позицию вставки с помощью разделителя
  • Автоматическая прокрутка, когда пользователь достигает края элемента управления во время перетаскивания
  • Поддерживает несколько экземпляров элемента управления
    • Может перетаскивать строки из одного экземпляра в другой
    • Во всех экземплярах элемента управления будет выбрана только одна строка
  • Пользовательское выделение строк

Вы можете делать все, что хотите, с помощью этого кода (без гарантии и т. д.)

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
ответ дан 17 December 2019 в 00:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: