.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
:
На приведенной выше диаграмме область шахматной доски - это контент, который недействителен и должен быть раскрашен во время следующего WM_PAINT.
В моем случае это бесполезно ; верхняя часть моего элемента управления содержит «заголовок» (например, заголовки столбцов listview). Прокрутка этого содержимого дальше вниз неверна:
и вызывает визуальное искажение.
Я хочу, чтобы 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 #, чтобы разрешить потомкам доступ к закрытым членам: я не могу этого сделать.
Я понял, что копирующая вставка реализации ScrollableControl
устраняет одну проблему означает, что мне также придется копировать и вставлять всю цепочку наследования до UserControl
...
ScrollableControl2 : Control, IArrangedElement, IComponent, IDisposable
ContainerControl2 : ScrollableControl2, IContainerControl
UserControl2 : ContainerControl2
. Я бы предпочел работать с объектно-ориентированным дизайном, а не против него.