Как запретить UserControl (в девичестве ScrollableControl) вызывать ScrollWindow?

.NET UserControl (который происходит от ScrollableControl ) должен иметь возможность отображать горизонтальные и вертикальные полосы прокрутки.

Вызывающий может установить видимость и диапазон, этих горизонтальных и вертикальных полос прокрутки:

UserControl.AutoScroll = true;
UserControl.AutoScrollMinSize = new Size(1000, 4000); //1000x4000 scroll area

Примечание: UserControl (то есть ScrollableControl ) использует стандартный механизм Windows для указания WS_HSCROLL и ] WS_VSCROLL стили окна для отображения полос прокрутки. То есть: они не создают отдельные элементы управления прокруткой Windows или .NET, размещая их в правой / нижней части окна. В Windows есть стандартный механизм отображения одной или обеих полос прокрутки.

Если пользователь прокручивает элемент управления, UserControl отправляется WM_HSCROLL или WM_VSCROLL сообщение. В ответ на эти сообщения я хочу, чтобы ScrollableControl аннулировал клиентскую область, что и произошло бы в собственном Win32:

switch (uMsg) 
{ 
   case WM_VSCROLL:
       ...
       GetScrollInfo(...);
       ...
       SetScrollInfo(...);
       ...

       InvalidateRect(g_hWnd, 
              null, //erase entire client area
              true, //background needs erasing too (trigger WM_ERASEBKGND));
       break;
 }

мне нужно сделать недействительной всю клиентскую область. Проблема в том, что UserControl (т.е. ScrollableControl ) вызывает функцию API ScrollWindow :

protected void SetDisplayRectLocation(int x, int y)
{
    ...
    if ((nXAmount != 0) || ((nYAmount != 0) && base.IsHandleCreated))
    {
        ...
        SafeNativeMethods.ScrollWindowEx(new HandleRef(this, base.Handle), nXAmount, nYAmount, null, ref rectClip, NativeMethods.NullHandleRef, ref prcUpdate, 7);
    }
    ...
}

вместо того, чтобы запускать InvalidateRect на всем клиентский прямоугольник, ScrollableControl пытается « спасти » существующее содержимое в клиентской области. Например, пользователь прокручивает вверх , текущее содержимое клиента сдвигается вниз с помощью ScrollWindowEx , а затем только вновь открытая область становится недействительной, вызывая WM_PAINT :

enter image description here

На приведенной выше диаграмме область шахматной доски - это контент, который недействителен и должен быть раскрашен во время следующего WM_PAINT.

В моем случае это бесполезно ; верхняя часть моего элемента управления содержит «заголовок» (например, заголовки столбцов listview). Прокрутка этого содержимого дальше вниз неверна:

enter image description here

и вызывает визуальное искажение.

Я хочу, чтобы ScrollableControl был , а не , использовал ScrollWindowEx , а вместо этого просто аннулировал всю клиентскую область.

Я попытался переопределить защищенный метод OnScroll :

protected override void OnScroll(ScrollEventArgs se)
{
   base.OnScroll(se);

   this.Invalidate();
}

Но это вызывает двойное рисование.

Примечание: я мог бы использовать двойную буферизацию, чтобы замаскировать проблему, но это не реальное решение

  • двойную буферизацию не следует использовать в сеансе удаленного рабочего стола / терминала
  • это неэффективно для ресурсов процессора
  • это не тот вопрос, который я задаю

. Я рассматривал возможность использования Control вместо UserControl (т.е.

protected override void SetDisplayRectLocation(int x, int y)
{
    ...
    Rectangle displayRect = this.displayRect;
    ...
    this.displayRect.X = x;
    this.displayRect.Y = y;
    if ((nXAmount != 0) || ((nYAmount != 0) && base.IsHandleCreated))
    {
        ...
        SafeNativeMethods.ScrollWindowEx(new HandleRef(this, base.Handle), nXAmount, nYAmount, null, ref rectClip, NativeMethods.NullHandleRef, ref prcUpdate, 7);
    }
    ...
}

Проблема в том, что SetDisplayRectLocation читает и записывает частную переменную-член ( displayRect ). Если Microsoft не изменит C #, чтобы разрешить потомкам доступ к закрытым членам: я не могу этого сделать.


Обновление 2

Я понял, что копирующая вставка реализации ScrollableControl устраняет одну проблему означает, что мне также придется копировать и вставлять всю цепочку наследования до UserControl

...
   ScrollableControl2 : Control, IArrangedElement, IComponent, IDisposable
      ContainerControl2 : ScrollableControl2, IContainerControl
         UserControl2 : ContainerControl2

. Я бы предпочел работать с объектно-ориентированным дизайном, а не против него.

13
задан Ian Boyd 27 April 2011 в 19:47
поделиться