.NET UserControl
(から派生ScrollableControl
)は、水平および垂直スクロールバーを表示する機能を備えている必要があります。
呼び出し元は、これらの水平および垂直スクロールバーの可視性と範囲を設定できます。
UserControl.AutoScroll = true;
UserControl.AutoScrollMinSize = new Size(1000, 4000); //1000x4000 scroll area
注:(
UserControl
ie )は、ウィンドウスタイルをScrollableControl
指定するWindows標準メカニズムを使用して、スクロールバーを表示します。つまり、個別のWindowsまたは.NETスクロールコントロールを作成せず、ウィンドウの右/下に配置します。Windowsには、一方または両方のスクロールバーを表示するための標準メカニズムがあります。WS_HSCROLL
WS_VSCROLL
ユーザーがコントロールをスクロールすると、またはメッセージUserControl
が送信されます。これらのメッセージに応答して、ScrollableControlがクライアント領域を無効にすることを望みます。これはネイティブWin32で発生することです。WM_HSCROLL
WM_VSCROLL
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);
}
...
}
ScrollableControlは、クライアントの四角形全体でInvalidateRectをトリガーするのではなく、クライアント領域の既存のコンテンツを「サルベージ」しようとします。たとえば、ユーザーが上にスクロールすると、現在のクライアントコンテンツがによって押し下げられ、ScrollWindowEx
新しくカバーされていない領域のみが無効になり、WM_PAINT
:がトリガーされます。
上の図では、チェッカーボード領域は無効なコンテンツであり、次のWM_PAINT中にペイントする必要があります。
私の場合、これは良くありません。コントロールの上部には「ヘッダー」(リストビューの列ヘッダーなど)が含まれています。このコンテンツをさらに下にスクロールするのは正しくありません。
そしてそれは視覚的な腐敗を引き起こします。
ScrollableControlでを使用せずScrollWindowEx
、代わりにクライアント領域全体を無効にします。
OnScroll
保護されたメソッドをオーバーライドしてみました:
protected override void OnScroll(ScrollEventArgs se)
{
base.OnScroll(se);
this.Invalidate();
}
しかし、それはダブルドローを引き起こします。
注:ダブルバッファリングを使用して問題を隠すことはできますが、それは実際の解決策ではありません
- リモートデスクトップ/ターミナルセッションではダブルバッファリングを使用しないでください
- CPUリソースの無駄です
- それは私が尋ねている質問ではありません
Control
代わりにUserControl
(つまり、継承チェーンの前に)を使用して、ScrollableControl
HScrollまたはVScroll .NETコントロールを手動で追加することを検討しましたが、これも望ましくありません。
- Windowsは、スクロールバーの位置の標準的な外観をすでに提供しています(複製するのは簡単ではありません)
- これは、 ScrollWindowExではなくInvalidateRectのみを実行したい場合に、最初から再現する必要のある多くの機能です。
を確認して投稿できるので、内部のコードは、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#を変更して、子孫がプライベートメンバーにアクセスできるようにしない限り、私はそれを行うことができません。
アップデート2
の実装をコピーアンドペーストし、1つの問題ScrollableControl
を修正すると、継承チェーン全体をコピーアンドペーストする必要があることに気付きました。UserControl
...
ScrollableControl2 : Control, IArrangedElement, IComponent, IDisposable
ContainerControl2 : ScrollableControl2, IContainerControl
UserControl2 : ContainerControl2
私は、オブジェクト指向のデザインに反対するのではなく、それ を使って作業することを本当に望んでいます。