Как заставить DataGridView прокручивать один элемент за раз с помощью колеса мыши?

StackOverflow https://stackoverflow.com/questions/135105

  •  02-07-2019
  •  | 
  •  

Вопрос

Мы хотели бы переопределить поведение DataGridView по умолчанию при использовании колеса мыши с этим элементом управления.По умолчанию DataGridView прокручивает количество строк, равное значению параметра SystemInformation.MouseWheelScrollLines.Нам хотелось бы прокручивать только один элемент за раз.

(Мы отображаем изображения в DataGridView, которые имеют довольно большой размер.Из-за такой прокрутки три строки (типичная системная настройка) — это слишком много, что часто приводит к тому, что пользователь прокручивает элементы, которые он даже не видит.)

Я уже попробовал пару вещей и пока не добился особого успеха.Вот некоторые проблемы, с которыми я столкнулся:

  1. Вы можете подписаться на события MouseWheel, но нет возможности пометить событие как обработанное и заняться своими делами.

  2. Вы можете переопределить OnMouseWheel, но он никогда не вызывается.

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

У кого-нибудь есть хорошее предложение?

Вот окончательный код, использующий замечательный ответ:

    /// <summary>
    /// Handle the mouse wheel manually due to the fact that we display
    /// images, which don't work well when you scroll by more than one
    /// item at a time.
    /// </summary>
    /// 
    /// <param name="sender">
    /// sender
    /// </param>
    /// <param name="e">
    /// the mouse event
    /// </param>
    private void mImageDataGrid_MouseWheel(object sender, MouseEventArgs e)
    {
        // Hack alert!  Through reflection, we know that the passed
        // in event argument is actually a handled mouse event argument,
        // allowing us to handle this event ourselves.
        // See http://tinyurl.com/54o7lc for more info.
        HandledMouseEventArgs handledE = (HandledMouseEventArgs) e;
        handledE.Handled = true;

        // Do the scrolling manually.  Move just one row at a time.
        int rowIndex = mImageDataGrid.FirstDisplayedScrollingRowIndex;
        mImageDataGrid.FirstDisplayedScrollingRowIndex =
            e.Delta < 0 ?
                Math.Min(rowIndex + 1, mImageDataGrid.RowCount - 1):
                Math.Max(rowIndex - 1, 0);
    }
Это было полезно?

Решение

Я просто немного поискал и протестировал сам.я использовал Отражатель чтобы исследовать и обнаружил пару вещей.А MouseWheel мероприятие обеспечивает MouseEventArgs параметр, но OnMouseWheel() переопределить в DataGridView бросает это на HandledMouseEventArgs.Это также работает при обработке MouseWheel событие. OnMouseWheel() действительно вызывается, и он находится в DataGridView's переопределить, что он использует SystemInformation.MouseWheelScrollLines.

Так:

  1. Вы действительно могли бы справиться с MouseWheel мероприятие, кастинг MouseEventArgs к HandledMouseEventArgs и установить Handled = true, тогда делай, что хочешь.

  2. Подкласс DataGridView, переопределить OnMouseWheel() самостоятельно и попытаться воссоздать весь код, который я прочитал здесь в Отражатель кроме замены SystemInformation.MouseWheelScrollLines с 1.

Последнее было бы огромной проблемой, поскольку оно использует ряд частных переменных (включая ссылки на ScrollBars), и вам придется заменить некоторые из них своими собственными и получить/установить другие с помощью Reflection.

Другие советы

Я бы подклассифицировал DataGridView в свой собственный пользовательский элемент управления (вы знаете, добавьте новый файл Windows Forms -> Custom Control и измените базовый класс с Control на DataGridView).

public partial class MyDataGridView : DataGridView

Затем переопределите метод WndProc и замените его примерно так:

protected override void WndProc(ref Message m)
{
    if (m.Msg == 0x20a)
    {
        int wheelDelta = ((int)m.WParam) >> 16;

        // 120 = UP 1 tick
        // -120 = DOWN 1 tick

        this.FirstDisplayedScrollingRowIndex -= (wheelDelta / 120);
    }
    else
    {
        base.WndProc(ref m);
    }
}

Конечно, вы должны убедиться, что вы не установили для FirstDisplayedScrollingRowIndex число, выходящее за пределы диапазона вашей сетки и т. д.Но это работает очень хорошо!

Ричард

Переопределение OnMouseWheel без вызова base.OnMouseWheel должно работать.Некоторые мыши с колесиком имеют специальные настройки, которые вам, возможно, придется настроить самостоятельно, чтобы они работали правильно.Посмотреть этот пост http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=126295&SiteID=1

ОБНОВЛЯТЬ: Поскольку теперь я узнал, что DataGridView имеет MouseWheel event, я добавил второе, более простое переопределение.

Один из способов добиться этого — создать подкласс DataGridView и переопределить WndProc добавить специальную обработку WM_MOUSEWHEEL сообщение.

Этот пример улавливает движение колесика мыши и заменяет его вызовом SendKeys.Send.

(Это немного отличается от простой прокрутки, поскольку при этом также выбирается следующая/предыдущая строка DataGridView.Но это работает.)

public class MyDataGridView : DataGridView
{
    private const uint WM_MOUSEWHEEL = 0x20a;

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_MOUSEWHEEL)
        {
            var wheelDelta = ((int)m.WParam) >> 16;

            if (wheelDelta < 0)
            {
                SendKeys.Send("{DOWN}");
            }

            if (wheelDelta > 0)
            {
                SendKeys.Send("{UP}");
            }

            return;
        }

        base.WndProc(ref m);
    }
}

2-й дубль (с теми же оговорками, что упоминались выше):

public class MyDataGridView : DataGridView
{
    protected override void OnMouseWheel(MouseEventArgs e)
    {
        if (e.Delta < 0)
            SendKeys.Send("{DOWN}");
        else
            SendKeys.Send("{UP}");
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top