.NET UserControl
(который происходит от _ 2_) должен отображать горизонтальное и вертикальное полосы прокрутки.
Вызывающий может установить видимость и диапазон этих горизонтальных и вертикальных полос прокрутки:
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
) вызывает ScrollWindow
Функция API:
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.
В моем случае это нехорошо; верхняя часть моего элемента управления содержит «заголовок» (например, заголовки столбцов списка). Прокрутка этого содержимого дальше вниз неверна:
и это вызывает визуальное искажение.
я хочу, чтобы ScrollableControl не использовал ScrollWindowEx
, а вместо этого просто аннулировал всю клиентскую область.
Я пробовал переопределить OnScroll
защищенный метод:
protected override void OnScroll(ScrollEventArgs se)
{
base.OnScroll(se);
this.Invalidate();
}
Но это вызывает двойную ничью.
Примечание. я мог бы использовать двойную буферизацию, чтобы замаскировать проблему, но это не настоящее решение.
- двойная буферизация не должна использоваться в сеансе удаленного рабочего стола / терминала
- это расточительное использование ресурсов ЦП
- это не тот вопрос, который я задаю
Я подумал об использовании Control
вместо UserControl
(т.е. до ScrollableControl
в цепочке наследования) и вручную добавить элемент управления HScroll или VScroll .NET, но это тоже нежелательно:
- Windows уже обеспечивает стандартный вид положения полос прокрутки (дублировать нетривиально)
- это много функций, которые нужно воспроизводить с нуля, когда мне нужно только InvalidateRect, а не ScrollWindowEx
Поскольку я могу видеть и размещать внутренний код для ScrollableControl
, я знаю, что нет свойства для отключения использования ScrollWindow
, но есть ли свойство для отключения использования ScrollWindow
?
Обновлять:
Я попытался переопределить метод нарушения и использовать отражатель для кражи всего кода:
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
, исправление одной проблемы означает, что мне также придется копировать-n-вставить всю цепочку наследования до UserControl
...
ScrollableControl2 : Control, IArrangedElement, IComponent, IDisposable
ContainerControl2 : ScrollableControl2, IContainerControl
UserControl2 : ContainerControl2
я бы предпочел работать с объектно-ориентированным дизайном, а не против него.
SetDisplayLocation
, не вызываяbase
, и скопировать все кишки, кроме одной строки. Обратной стороной этого решения является а) я бы предпочел работать с .NETUserControl
(если возможно), а не против него, б) копируя текущую реализацию, я потеряю любые будущие улучшения .NET дляScrollableControl
и c) хотя мне все равно: это незаконно - person Ian Boyd   schedule 25.04.2011SetDisplayLocation
метод использует закрытыйdisplayRect
член - нет возможности повторно реализовать метод без доступа к этому закрытому члену. - person Ian Boyd   schedule 26.04.2011