Как сделать так, чтобы DataGridView прокручивал по одному элементу за раз, используя колесо мыши? - PullRequest
7 голосов
/ 25 сентября 2008

Мы хотели бы переопределить поведение по умолчанию 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);
    }

Ответы [ 4 ]

4 голосов
/ 25 сентября 2008

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

Итак:

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

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

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

1 голос
/ 26 сентября 2008

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

Один из способов сделать это - создать подкласс 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}");
    }
}
1 голос
/ 25 сентября 2008

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

1 голос
/ 25 сентября 2008

Я бы создал подкласс 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 на число, выходящее за пределы диапазона вашей сетки и т. Д. Но это работает довольно хорошо!

Richard

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...