.NET ROOM.NET ROOMトップへCOMRADE総合トップへ
「6:コントロール実行時の動作を制御する」へ

コントロールをスクロール可能にする

 カスタムペイントのコントロールを実装する場合には、コントロールをスクロール可能にしたい場合が多いでしょう。
ここではスクロールに関して、簡単に説明します。

 比較的簡単な方法をはじめに紹介します。
これは、ユーザーコントロールなどのScrollableControlから派生している
クラスをベースにして自作コントロールを作る方法です。

 ScrollableControlは、Controlクラスにいくつかのプロパティやメソッドが追加されたものですが、
以降でその中のスクロールに関連する重要なプロパティを紹介します。


■AutoScroll■
 このプロパティをTrueに設定すると、文字通りAutoScrollが有効になります。
自動スクロールはおおまかに以下の2つの条件のどちらかが満たされた場合に、
自動的にスクロールバーを表示します(以降では、スクロールバーが表示されている状態のことを
「自動スクロールが有効である」ということにします)。

条件1: 当該のコントロールをコンテナとした場合に、
表示領域よりも子コントロールが大きい場合

条件2: 後述する、AutoScrollMinSizeよりも現在のSizeが
水平/垂直のいずれかの方向に小さい場合


■AutoScrollMargin■
 AutoScrollのマージンを設定/取得するプロパティです。


■AutoScrollMinSize■
 前述したように、このプロパティの値よりも現在のコントロールのSizeが
水平/垂直のいずれかの方向に小さい場合に自動的にスクロールバーが表示されます。


■AutoScrollPosition■
 自動スクロールが有効である場合の現在の位置を示します。


■AutoScrollPosition■
 自動スクロールが有効である場合の現在の位置を示します。


■VScroll/HScroll■
 水平/垂直のスクロールバーの表示/非表示を設定するプロパティです。

 ただし、残念ながら現状の.NET Framework 1.1では、
このVScroll/HScrollプロパティは有効に機能していないようです。
VScroll/HScrollプロパティをFalseにしても、スクロールバーは表示されたままになり、
表示されているスクロールバーは動きますがAutoScrollPositionが変更されません。

 この不具合を確認するためのサンプルを付録のScroll\ScrollTestAppディレクトリに収録しておきます。

AutoScrollによるスクロール@


■サンプル:AutoScroll/AutoScrollImproved■

 サンプルAutoScrollでは、AutoScrollの「条件2」によって実現する自動スクロールを利用した、
スクロールの実装を行なっています。
このサンプルをこのまま実行するとスクロールした際に描画が崩れてしまいます(図4)。

図4:サンプルAutoScrollをスクロールさせた際に起きる問題



 これは、AutoScrollを設定しても実際のコントロールのLocationが変化しないために起こります。
そのため、リスト4のように描画をAutoScrollPositionでOffsetするという
工夫が必要になります(サンプルAutoScrollImproved)。
この変更を加えることで、スクロールしても描画が崩れなくなります(図5)。

リスト4:AutoScrollを使ったスクロールの描画の改善
internal float DrawHeight {
 get {
  float auto = AutoScrollMinSize.Height,
      client = ClientSize.Height;
  if (AutoScroll)
   return (auto > client) ? auto : client;
  else
   return client;
 }
}

internal float DrawWidth {
 get {
  float auto = AutoScrollMinSize.Width,
      client = ClientSize.Width;
  if (AutoScroll)
   return (auto > client) ? auto : client;
  else
   return client;
 }
} internal Size DrawOffset {
 get {
  return (AutoScroll) ? new Size(AutoScrollPosition) :
   Size.Empty;
 }
}

const float LINEFREQ = 50F;
private void DrawAster(Graphics g) {
 float width = DrawWidth;
 float height = DrawHeight;
 float x_delta = width / LINEFREQ;
 float y_delta = height / LINEFREQ;
 Size offset = DrawOffset;
 for (int i = 0; i < LINEFREQ; i++) {
  g.DrawLine(blue,
   new PointF(0, y_delta * i) + offset,
   new PointF(width, height - (y_delta * i)) + offset);
  g.DrawLine(red,
   new PointF(x_delta * (i + 1), 0) + offset,
   new PointF(width - (x_delta * (i + 1)), height)
   + offset);
 }
}


図5:図4の問題の改善


AutoScrollによるスクロールA


■サンプル:PanelScroll■

 続いて、AutoScrollの「条件1」によって実現する自動スクロールを利用した、
スクロールの実装について解説します。
サンプルPanelScrollでは、描画用のコントロールをコンテナのPanelScrollの中に
子コントロールとして追加して、スクロールを出現させています。
このパターンの場合は、内側の子コントロールの座標を実際に変更するため、
描画ルーチンに手を加える必要がありません(図6)。

図6:Panelを使用したスクロール可能なコントロール


VScrollBar/HScrollBarによるスクロール


■サンプル:VHScrollBars■

 最後に、AutoScrollを使用せずに、コントロールのVScrollBarとHScrollBarを
貼ることでスクロールを実装してみます。
このサンプルVHScrollBarsの実行結果は図7のようになりますが、このサンプルでは、
VScrollBarとHScrollBar以外にも、角を表現するためにControlクラスのオブジェクトを追加しています。

図7:VScrollBarとHScrollBarを使ったスクロール可能なコントロール



 また、VScrollBarとHScrollBarと内部の描画用子コントロールは直接にはリンクしていないため、
以下のように、Scrollイベントで移動する必要があります。

   private void vscroll_Scroll(object sender, ScrollEventArgs e) {
    int y_delta = vscroll.Value - e.NewValue;
    ctrl.Location += new Size(0, y_delta);
   }
   private void hscroll_Scroll(object sender, ScrollEventArgs e) {
    int x_delta = hscroll.Value - e.NewValue;
    ctrl.Location += new Size(x_delta, 0);
   }

 スクロールボックスのサイズはLargeChangeとMaximumの比率で決まります。
適切に表示するためには、図8のように設定するとよいでしょう。


図8:VScrollBarとHScrollBarの各プロパティの設定の目安



前へ | ページトップへ | 次へ