ウェブ
イメージ
表示順:
Relevance
Relevance
Date
ウェブ
 
 
 
イメージ
 
 
 
 

レガシー Windows API(概要)について

ここで紹介しているAPIは、Windows95、Windows NT3.5時代のAPI群であり、最新のAPIは含まれていないことに注しして欲しい。なお、当時の解説書は現在よりもより詳細に解説しているため、参考になるはずである。

  • レイアウトを変換する際、崩れているページがあります。
  • 概要およびSampleコードはありますが、APIの解説はありません。

ご要望があれば旧版のWin32 API(HELP形式)をお譲りします。問い合わせフォームより申し込みください。

Windows API スクロール バーの概要

Microsoft(R) Windows(TM) 用アプリケーションのウィンドウには、 ドキュメントやビットマップなど、 ウィンドウのクライアント領域よりも大きいデータ オブジェクトを表示できます。「スクロール バー」があれば、 ユーザーは、 クライアント領域のデータ オブジェクトをスクロールして、 ウィンドウの境界内に収まらないオブジェクトを見ることができます。クライアント領域の内容がウィンドウの境界内に収まらないようなウィンドウには、 スクロール バーを付加してください。

次に示すトピックでは、 Win32のスクロール バーについて説明しています。

スクロール バーの構成要素

標準スクロール バーとスクロール バー コントロール

スクロール バーのつまみの位置とスクロール範囲

スクロール バー要求

スクロール バーのキーボード インターフェイス

クライアント領域のスクロール

スクロール バーの表示状態

スクロール バーの色とメトリック

スクロール バーの作成

テキストのスクロール

テキストのスクロールの例

ビットマップのスクロール

ビットマップのスクロールの例

標準スクロール バー用キーボード インターフェイスの作成

スクロール バー関数

スクロール バー メッセージ

スクロール バーの向きによって、 ユーザーがスクロール バーを操作したときにスクロールが発生する方向が決まります。水平スクロール バーの場合、 ユーザーはウィンドウの内容を左右にスクロールできます。垂直スクロール バーの場合、 ユーザーはウィンドウの内容を上下にスクロールできます。

 

 

スクロール バーの構成要素

スクロール バーは、 細長い長方形 (シャフト)、 その両端の矢印ボタン、 矢印ボタンの間の「スクロール バーのつまみ」(スクロール ボックスまたはサムとも呼ばれます) で構成されます。スクロール バーのつまみは、 ウィンドウのクライアント領域内のデータ オブジェクトの全体の長さまたは幅を表します。スクロール バーのつまみは、 オブジェクトのクライアント領域に見えている部分を表します。ユーザーがデータ オブジェクトをスクロールして別の部分を表示させると、 スクロール バーのつまみの位置は変化します。ウィンドウやデータ オブジェクトのサイズが変更すると、 つまみのサイズも変更します。

ユーザーは、 矢印ボタンのいずれかをクリックするか、 シャフト内をクリックするか、 つまみをドラッグすることによって、 ウィンドウの内容をスクロールできます。ユーザーが矢印ボタンをクリックすると、 アプリケーションは、 ウィンドウの内容を1単位 (通常は1行または1列) だけスクロールします。ユーザーがシャフトをクリックすると、 アプリケーションは、 ウィンドウの内容をウィンドウの大きさだけスクロールします。ユーザーがつまみをドラッグしたときのスクロール量は、 ユーザーがつまみをドラッグした距離と、 スクロール バーの「スクロール範囲」によって異なります。スクロール範囲について詳しくは、 スクロール バーのつまみの位置とスクロール範囲を参照してください。

 

 

標準スクロール バーとスクロール バー コントロール

スクロール バーは、 標準スクロール バーまたはスクロール バー コントロールとして使います。「標準スクロール バー」は、 ウィンドウの非クライアント領域にあり、 ウィンドウとともに作成され、 ウィンドウを表示するときに表示されます。標準スクロール バーの目的は、 ユーザーがクライアント領域の内容全体を見るためにスクロール要求を生成できるようにすることだけです。ウィンドウに標準スクロール バーを付加するには、 WS_HSCROLLWS_VSCROLLを指定してウィンドウを作成します。WS_HSCROLLスタイルを指定すると、 クライアント領域のいちばん下に水平スクロール バーが作成されます。WS_VSCROLLスタイルを指定すると、 クライアント領域の右端に垂直スクロール バーが作成されます。SM_CXHSCROLLシステム メトリック値とSM_CYHSCROLLシステム メトリック値は、 標準水平スクロール バーの幅と高さを定義しています。SM_CXVSCROLL値とSM_CYVSCROLL値は、 標準垂直スクロール バーの幅と高さを定義しています。

「スクロール バー コントロール」とは、 SCROLLBARウィンドウ クラスに属するコントロール ウィンドウです。スクロール バー コントロールは、 機能的には標準スクロール バーに似ていますが、 独立したウィンドウになっています。スクロール バー コントロールは独立したウィンドウであるため、 入力フォーカスを直接受け取ることができ、 スクロール バーのつまみを点滅させることによって入力フォーカスを示します。また、 スクロール バー コントロールには、 標準スクロール バーとは異なり、 ユーザーが直接スクロールするための組み込みのキーボード インターフェイスがあります。スクロール バー コントロールは、 1つのウィンドウで必要なだけ使えます。スクロール バー コントロールを作成するときは、 スクロール バーのサイズと位置を指定しなければなりません。しかし、 スクロール バー コントロールの付いたウィンドウをサイズ変更可能にするときは、 ウィンドウのサイズを変更するたびにスクロール バーのサイズも変更しなければなりません。

標準スクロール バーの利点は、 Windowsがスクロール バーを作成し、 自動的にサイズと位置を設定してくれることです。しかし、 標準スクロール バーでは制限が多すぎる場合もあります。たとえば、 クライアント領域を4分割して、 各部分の内容を独立したスクロール バーのセットで制御するとします。標準スクロール バーはウィンドウ1つに付き1セットしか作成できないため、 標準スクロール バーは使えません。スクロール バー コントロールは必要なだけ作成できるため、 このような場合はスクロール バー コントロールを使ってください。

また、 ウィンドウの内容のスクロール以外の目的にもスクロール バー コントロールを使うことができます。たとえば、 Windowsコントロール パネルの[マウス]アプリケーションには、 ユーザーがマウスのトラッキング速度やダブルクリック速度を設定するためのスクロール バー コントロールがあります。

スクロール バー コントロールには、 スクロール バー コントロールの向きや位置を制御するための多くのスタイルがあります。スタイルは、 CreateWindowEx関数を呼び出してスクロール バー コントロールを作成するときに指定します。一部のスタイルは、 デフォルトの幅や高さを使ってスクロール バー コントロールを作成します。しかし、 スクロール バーのx座標やy座標などの寸法は必ず指定してください。スクロール バー コントロールのスタイルを次に示します。

 

スタイル 説明

SBS_BOTTOMALIGN CreateWindowEx関数のxy nWidthnHeightの各パラメータで指定した長方形の下端に水平スクロール バーを置きます。SBS_HORZスタイルも指定しなければなりません。スクロール バーの高さには、 標準水平スクロール バーの高さが使われます。

SBS_HORZ 水平スクロール バーを作成します。SBS_BOTTOMALIGNスタイルやSBS_TOPALIGNスタイルを指定しなければ、 CreateWindowExで指定した高さ、 幅、 位置が使われます。

SBS_LEFTALIGN CreateWindowEx関数のxy nWidthnHeightの各パラメータで指定した長方形の左端に水平スクロール バーを置きます。SBS_VERTスタイルも指定しなければなりません。スクロール バーの幅には、 標準垂直スクロール バーの幅が使われます。

SBS_RIGHTALIGN CreateWindowEx関数のxy nWidthnHeightの各パラメータで指定した長方形の右端に水平スクロール バーを置きます。SBS_VERTスタイルも指定しなければなりません。スクロール バーの幅には、 標準垂直スクロール バーの幅が使われます。

SBS_TOPALIGN CreateWindowEx関数のxy nWidthnHeightの各パラメータで指定した長方形の上端に水平スクロール バーを置きます。SBS_HORZスタイルも指定しなければなりません。スクロール バーの高さには、 標準水平スクロール バーの高さが使われます。

SBS_VERT 垂直スクロール バーを作成します。SBS_RIGHTALIGNスタイルやSBS_LEFTALIGNスタイルを指定しなければ、 CreateWindowEx関数で指定した高さ、 幅、 位置が使われます。

 

スクロール バーのつまみの位置とスクロール範囲

スクロール バーのつまみの位置は、 整数で表現されます。位置は、 (スクロール バーが水平か垂直かに応じて) スクロール バーの左端または上端に相対的です。位置は、 スクロール範囲の最小値と最大値の間でなければなりません。たとえば、 範囲が0から100までのスクロール バーの場合、 位置50が中央で、 ほかの位置も値に比例します。初期範囲は、 スクロール バーによって異なります。標準スクロール バーの初期範囲は0から100までです。スクロール バー コントロールの場合、 コントロールを作成するときに範囲を明示的に指定しなければ、 範囲は空になります (最小値と最大値が0になります)。範囲を変更するには、 SetScrollRange関数を使って、 新しい最小値と最大値を設定します。GetScrollRange関数は、 現在の最小値と最大値を取得します。

スクロール範囲を使いやすい整数に調整するには、 SetScrollRange関数を使います。この関数によって、 スクロール バーのつまみの位置をスクロール データに対応する値に変換するのが簡単になります。たとえば、 テキストを一度に16行しか表示できないウィンドウに260行を表示する場合、 垂直スクロール バーの範囲には1から244を設定します。スクロール バーが位置1にあるときは、 1行目はウィンドウのいちばん上に表示されます。つまみが位置244にあるときは、 最後の行 (260行目) がウィンドウのいちばん下に表示されます。最小値より小さい位置や最大値より大きい位置をSetScrollPos関数に指定すると、 代わりにスクロール範囲の最小値または最大値が使われます。

スクロール バーの範囲とデータの関係を適切に保つには、 データやウィンドウのサイズが変化するたびに範囲を調整しなければなりません。

ユーザーがスクロール バーのつまみを移動すると、 スクロール バーは、 つまみの位置をスクロール範囲内の整数として報告します。位置が範囲の最小値ならば、 つまみは、 垂直スクロール バーの上端か、 水平スクロール バーの左端にあります。位置が最大値ならば、 つまみは、 垂直スクロール バーの下端か、 水平スクロール バーの右端にあります。

スクロール バーのつまみの移動は、 アプリケーションが行わなければなりません。ユーザーがスクロール バーでスクロールを要求しても、 スクロール バーは、 つまみの位置を更新しません。その代わり、 スクロール バーは、 親ウィンドウに要求を渡します。要求を受け取った親ウィンドウは、 データをスクロールし、 SetScrollPos関数を使ってつまみの位置を更新しなければなりません。つまみの位置はアプリケーションが制御するため、 アプリケーションは、 スクロール対象のデータに最適な増分でつまみを移動できます。

 

 

 

スクロール バー要求

ユーザーは、 スクロール バーのさまざまな部分をクリックすることによってスクロールを要求します。Windowsは、 WM_HSCROLLメッセージやWM_VSCROLLメッセージによってこの要求をウィンドウに送ります。水平スクロール バーは、 WM_HSCROLLメッセージを送ります。垂直スクロール バーは、 WM_VSCROLLメッセージを送ります。各メッセージには、 ユーザーの動作に対応する通知コード、 スクロール バーのハンドル (スクロール バー コントロールの場合のみ) が付属します。また、 スクロール バーのつまみの位置が報告される場合もあります。

次の図は、 ユーザーがスクロール バーのさまざまな部分をクリックしたときに生成される通知コードを示しています。

スクロール バー通知コードは、 ユーザーの操作を示します。アプリケーションは、 WM_HSCROLLメッセージやWM_VSCROLLメッセージの通知コードを調べて、 適切なスクロール操作を実行します。通知コードとそれを生成するユーザーの操作、 アプリケーションの応答を次の表に示します。どの場合でも、 1「単位」は、 データに応じてアプリケーションが適切に定義できます。たとえば、 テキストを垂直にスクロールする場合は、 単位として1行がよく使われます。

 

通知コード 操作 アプリケーションの応答

SB_LINEUP ユーザーが上スクロール矢印をクリックしたとき スクロール バーのつまみの位置を減らします。データを上方向に1単位だけスクロールします。

SB_LINEDOWN ユーザーが下スクロール矢印をクリックしたとき スクロール バーのつまみの位置を増やします。データを下方向に1単位だけスクロールします。

SB_LINELEFT ユーザーが左スクロール矢印をクリックしたとき スクロール バーのつまみの位置を減らします。データを左方向に1単位だけスクロールします。

SB_LINERIGHT ユーザーが右スクロール矢印をクリックしたとき スクロール バーのつまみの位置を増やします。データを右方向に1単位だけスクロールします。

SB_PAGEUP ユーザーがスクロール バーのつまみの上のスクロール バー シャフトをクリックしたとき スクロール位置をウィンドウ内のデータ単位数だけ減らします。それと同じ単位数だけデータを上方向にスクロールします。

SB_PAGEDOWN ユーザーがスクロール バーのつまみの下のスクロール バー シャフトをクリックしたとき スクロール位置をウィンドウ内のデータ単位数だけ増やします。それと同じ単位数だけデータを下方向にスクロールします。

SB_PAGELEFT ユーザーがスクロール バーのつまみの右のスクロール バー シャフトをクリックしたとき スクロール位置をウィンドウ内のデータ単位数だけ減らします。それと同じ単位数だけデータを左方向にスクロールします。

SB_PAGERIGHT ユーザーがスクロール バーのつまみの右のスクロール バー シャフトをクリックしたとき スクロール位置をウィンドウ内のデータ単位数だけ増やします。それと同じ単位数だけデータを右方向にスクロールします。

SB_THUMBPOSITION ユーザーがスクロール バーのつまみをドラッグしてから離したとき メッセージが示す位置につまみを設定します。つまみを移動したのと同じ単位数だけデータをスクロールします。

SB_THUMBTRACK ユーザーがスクロール バーのつまみをドラッグしたとき データを高速に描画できる場合は、 メッセージが示す位置につまみを設定し、 つまみを移動したのと同じ単位数だけデータをスクロールします。データを高速に描画できないときは、 SB_THUMBPOSITIONコードのメッセージを受け取ってから、 つまみを移動してデータをスクロールします。

SB_ENDSCROLL ユーザーが矢印ボタンやスクロール バー シャフトを押したままにしてからマウスを離したとき 応答は不要です。

 

ユーザーがスクロール バーのつまみをクリックしたりドラッグすると、 スクロール バーは、 SB_THUMBPOSITION通知コードやSB_THUMBTRACK通知コードを生成します。アプリケーションは、 SB_THUMBTRACK通知コードかSB_THUMBPOSITION通知コードのどちらかを処理しなければなりません。

ユーザーがスクロール バーのつまみをクリックしてからマウス ボタンを離すと、 SB_THUMBPOSITION通知コードが発生します。このコードを処理するアプリケーションは、 ユーザーがつまみを必要な位置までドラッグしてマウス ボタンを離してから、 スクロール操作を実行します。

SB_THUMBTRACK通知コードは、 ユーザーがスクロール バーのつまみをドラッグしているときに発生します。SB_THUMBTRACKメッセージを処理するアプリケーションは、 ユーザーがつまみをドラッグするのに応じてウィンドウの内容をスクロールしてください。しかし、 スクロール バーは短時間に大量のSB_THUMBTRACK通知コードを生成するため、 アプリケーションは、 ウィンドウの内容を高速に描画できるときだけこのコードを処理してください。

 

 

スクロール バーのキーボード インターフェイス

スクロール バー コントロールには、 ユーザーがキーボードを使ってスクロールを要求できるようにするための組み込みキーボード インターフェイスがあります。標準スクロール バーにはキーボード インターフェイスはありません。ユーザーが方向キーを押すと、 キーボード フォーカスを持つスクロール バー コントロールは、 WM_HSCROLLメッセージやWM_VSCROLLメッセージを親ウィンドウに送ります。また、 各メッセージには、 ユーザーが押した方向キーに対応する通知コードが付属します。方向キーとそれに対応する通知コードを次に示します。

 

方向キー 通知コード

↑キー SB_LINEUPまたはSB_LINELEFT

↓キー SB_LINEDOWNまたはSB_LINERIGHT

← SB_LINEUPまたはSB_LINELEFT

→ SB_LINEDOWNまたはSB_LINERIGHT

PgUp SB_PAGEUPまたはSB_PAGELEFT

PgDn SB_PAGEDOWNまたはSB_PAGERIGHT

Home SB_TOP

End SB_BOTTOM

 

スクロール バー コントロールのキーボード インターフェイスは、 SB_TOP通知コードやSB_BOTTOM通知コードを送ります。SB_TOPコードは、 ユーザーがスクロール範囲の上端を指定したことを示します。アプリケーションは、 ウィンドウの内容を下にスクロールして、 データ オブジェクトのいちばん上の部分が見えるようにします。SB_BOTTOMコードは、 ユーザーがスクロール範囲の下端を指定したことを示します。アプリケーションは、 SB_BOTTOMを処理するとき、 ウィンドウの内容を上にスクロールして、 データ オブジェクトのいちばん下が見えるようにしなければなりません。

標準スクロール バーでもキーボード インターフェイスが必要ならば、 ウィンドウ プロシージャでWM_KEYDOWNメッセージを独自に処理し、 メッセージが示す仮想キー コードに基づいて適切なスクロール動作を実行することによって、 同様のインターフェイスを実現できます。標準スクロール バーでキーボード インターフェイスを実現する方法については、 標準スクロール バー用キーボード インターフェイスの作成を参照してください。

 

 

クライアント領域のスクロール

クライアント領域の内容を最も簡単にスクロールする方法は、 クライアント領域を消去してから再描画する方法です。SB_PAGEUP SB_PAGEDOWNSB_TOPの各通知メッセージでは完全に新しい内容が必要になることが多いため、 この方法を使います。

しかし、 SB_LINEUPSB_LINEDOWNなどの通知メッセージでは、 スクロール後も内容の一部はそのまま残るため、 内容をすべて消去する必要はありません。ScrollWindow関数やScrollWindowEx関数は、 クライアント領域の内容の一部を指定された量だけそのまま移動し、 残りの部分に新しい情報を描画する準備をします。ScrollWindowScrollWindowExは、 BitBlt関数を使って、 データ オブジェクトの特定の部分をクライアント領域内の新しい位置に移動します。以前の内容が残らなかった部分は無効化され、 次のWM_PAINTメッセージで消去されて描画されます。

ScrollWindow関数やScrollWindowEx関数では、 クライアント領域の一部をスクロール操作から除外できます。これによって、 子ウィンドウなどの位置が変わらない項目が移動しないようにできます。これらの関数はクライアント領域内の新しい情報を描画すべき部分を自動的に無効化するため、 アプリケーションは、 クリッピング リージョンを独自に計算する必要はありません。クリッピングについて詳しくは、 クリッピングの概要を参照してください。

通常、 アプリケーションは、 スクロール バーが示す方向と反対の方向にウィンドウの内容をスクロールします。たとえば、 つまみの下のシャフト部分をユーザーがクリックすると、 アプリケーションは、 ウィンドウ内のオブジェクトを上方向にスクロールして、 現在見えている部分の下の部分が見えるようにします。

 

 

スクロール バーの表示状態

最小値と最大値に同じ値を指定すると、 SetScrollRange関数は、 標準スクロール バーを非表示にして使用不能にします。クライアント領域の内容にスクロール バーが不要ならば、 このようにしてスクロール バーを一時的に非表示にします。スクロール バーが非表示のときはスクロール バーに要求する必要はありません。最小値と最大値に異なる値を指定すると、 SetScrollRangeは、 スクロール バーを表示して使用可能にします。また、 ShowScrollBar関数を使ってスクロール バーを表示または非表示することもできます。この関数は、 スクロール バーの範囲やつまみの位置には影響を与えません。

スクロール バーの矢印を使用不能にするには、 EnableScrollBar関数を使います。この関数は使用不能状態の矢印を表示し、 アプリケーションはユーザー入力に応答しなくなります。

 

 

 

スクロール バーの色とメトリック

システム定義の色の値のCOLOR_SCROLLBARは、 スクロール バーのシャフトの色を示します。シャフトの色を調べるには、 GetSysColor関数を使います。また、 シャフトの色を設定するには、 SetSysColors関数を使います。しかし、 この色の変更はシステムのすべてのスクロール バーに影響することに注意してください。

Windowsが標準スクロール バーで使うビットマップの寸法を取得するには、 GetSystemMetrics関数を呼び出します。スクロール バーに関するシステム メトリック値を次に示します。

 

システム メトリック 説明

SM_CXHSCROLL 水平スクロール バーの矢印ビットマップの幅

SM_CYHSCROLL 水平スクロール バーの矢印ビットマップの高さ

SM_CXVSCROLL 垂直スクロール バーの矢印ビットマップの幅

SM_CYVSCROLL 垂直スクロール バーの矢印ビットマップの高さ

SM_CXHTHUMB 水平スクロール バーのつまみの幅

SM_CYVTHUMB 垂直スクロール バーのつまみの高さ

 

 

 

 

スクロール バーの使用

次以降のトピックでは、 以下に示すタスクの実行方法を説明しています。[>>]ボタンを使って順番に読んでください。または、 各トピックをクリックすると、 そのトピックの内容を表示できます。

 

・ スクロール バーの作成

・ テキストのスクロール

・ ビットマップのスクロール

・ 標準スクロール バー用キーボード インターフェイスの作成

 

 

スクロール バーの作成

オーバーラップ ウィンドウやポップアップ ウィンドウ、 子ウィンドウを作成するとき、 標準スクロール バーを付加するには、 WS_HSCROLLスタイルやWS_VSCROLLスタイルを指定してCreateWindowEx関数を呼び出します。これによって、 水平スクロール バーや垂直スクロール バーがウィンドウに付加されます。次のコード例は、 標準の水平スクロール バーと垂直スクロール バーを持つウィンドウを作成します。

 

hwnd = CreateWindowEx(
0L, /* no extended styles */
"MyAppClass", /* window class */
"Scroll Bar Application", /* text for window title bar */
WS_OVERLAPPEDWINDOW | /* window styles */
WS_HSCROLL |
WS_VSCROLL,
CW_USEDEFAULT, /* default horizontal position */
CW_USEDEFAULT, /* default vertical position */
CW_USEDEFAULT, /* default width */
CW_USEDEFAULT, /* default height */
(HWND) NULL, /* no parent for overlapped windows */
(HMENU) NULL, /* window class menu */
hinst, /* instance owning this window */
(LPVOID) NULL /* pointer not needed */
);

上記のスクロール バーのスクロール バー メッセージを処理するには、 メイン ウィンドウ プロシージャに適切なコードを追加してください。

スクロール バー コントロールを作成するには、 SCROLLBARウィンドウ クラスを指定してCreateWindowEx関数を呼び出します。この関数は、 ウィンドウ スタイルにSBS_HORZSBS_VERTのどちらが指定されているかに応じて、 水平または垂直のスクロール バーを作成します。親ウィンドウに相対的なスクロール バーのサイズと位置を指定することもできます。次のコード例は、 水平スクロール バー コントロールを作成して、 ウィンドウの右上隅に置きます。

 

hwndScroll = CreateWindowEx(
0L, /* no extended styles */
"SCROLLBAR", /* scroll bar control class */
(LPSTR) NULL, /* text for window title bar */
WS_CHILD | SBS_HORZ, /* scroll bar styles */
0, /* horizontal position */
0, /* vertical position */
200, /* width of the scroll bar */
CW_USEDEFAULT, /* default height */
hwnd, /* handle of main window */
(HMENU) NULL, /* no menu for a scroll bar */
hinst, /* instance owning this window */
(LPVOID) NULL /* pointer not needed */
);

 

テキストのスクロール

ここでは、 アプリケーションのメイン ウィンドウ プロシージャを変更して、 ユーザーがテキストをスクロールできるようにする方法について説明します。テキストのスクロールの例では、 テキスト文字列を作成して表示し、 スクロール バーが生成するWM_HSCROLLメッセージやWM_VSCROLLメッセージを処理することによって、 ユーザーがテキストを垂直や水平にスクロールできるようにします。

 

WM_CREATEメッセージの処理

通常、 スクロール単位は、 WM_CREATEメッセージを処理するときに設定します。スクロール単位は、 ウィンドウのディスプレイ コンテキスト (DC) に関連付けられているフォントの寸法から決定するのが便利です。特定のDCのフォントの寸法を取得するには、 GetTextMetrics関数を使います。

テキストのスクロールの例では、 垂直方向のスクロール単位は、 文字セルの高さに外部レディングを加算した値です。また、 水平方向のスクロール単位は、 文字セルの平均幅です。このため、 画面フォントが固定幅でなければ、 水平スクロール位置は実際の文字には対応しません。

 

WM_SIZEメッセージの処理の際のスクロールの調整

クライアント領域の寸法と表示される行数を反映するようにスクロール範囲やスクロール位置を調整するのは、 WM_SIZEメッセージを処理するときに行うのが便利です。

SetScrollRange関数は、 スクロール バーの最小位置と最大位置を設定します。SetScrollPos関数は、 スクロール位置を範囲英するようにスクロール バーのつまみを調整します。

 

WM_HSCROLLメッセージとWM_VSCROLLメッセージの処理

ユーザーがスクロール バーをクリックしたりスクロール バーのつまみをドラッグすると、 スクロール バーは、 WM_HSCROLLメッセージやWM_VSCROLLメッセージをウィンドウ プロシージャに送ります。WM_VSCROLLやWM_HSCROLLのwParamの下位ワードは、 スクロール操作の方向や量を示す通知メッセージです。

WM_HSCROLLメッセージやWM_VSCROLLメッセージを処理するときは、 スクロール バー通知メッセージを調べて、 スクロール量を計算します。現在位置にスクロール量を適用したら、 ScrollWindow関数を使ってウィンドウを新しい位置までスクロールし、 SetScrollPos関数を使ってスクロール バーのつまみの位置を調整します。

ウィンドウをスクロールすると、 クライアント領域の一部が無効になります。無効なリージョンが更新されるようにするため、 UpdateWindow関数を使ってWM_PAINTメッセージを生成します。

 

WM_PAINTメッセージの処理

ウィンドウの無効部分にテキストを描画するのは、 WM_PAINTメッセージを処理するときに行うのが便利です。コード例では、 現在のスクロール位置と無効リージョンの寸法を使って、 無効リージョン内に表示する行の範囲を調べています。

 

 

テキストのスクロールの例

次の例は、 水平スクロール バーや垂直スクロール バーからの入力に応じてテキストをスクロールする方法を示しています。

 

HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;

/* These variables are required to display text. */

static int xClient; /* width of client area */
static int yClient; /* height of client area */
static int xClientMax; /* maximum width of client area */

static int xChar; /* horizontal scrolling unit */
static int yChar; /* vertical scrolling unit */
static int xUpper; /* average width of uppercase letters */

static int xPos; /* current horizontal scrolling position */
static int yPos; /* current vertical scrolling position */

static int xMax; /* maximum horiz. scrolling position */
static int yMax; /* maximum vert. scrolling position */

int xInc; /* horizontal scrolling increment */
int yInc; /* vertical scrolling increment */

int i; /* loop counter */
int x, y; /* horiz. and vert. printing coords */

int FirstLine; /* first line in the invalidated area */
int LastLine; /* last line in the invalidated area */

/* Create an array of lines to display. */

#define LINES 27
static char *abc[] = { "anteater", "bear", "cougar", "dingo",
"elephant", "frog", "gazelle", "hyena", "iguana", "jackal",
"kangaroo", "llama", "moose", "newt", "octopus", "penguin",
"quail", "rat", "squid", "tortoise", "urus", "vole",
"walrus", "xylophone", "yak", "zebra",
"This line contains many words, but no character. Go figure." };

switch (uMsg) {

case WM_CREATE :

/* Get the handle of the client area's device context. */

hdc = GetDC (hwnd);

/* Extract font dimensions from the text metrics. */

GetTextMetrics (hdc, &tm);
xChar = tm.tmAveCharWidth;
xUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * xChar/2;
yChar = tm.tmHeight + tm.tmExternalLeading;

/* Free the device context. */

ReleaseDC (hwnd, hdc);

/*
* Set an arbitrary maximum width for client area.
* (xClientMax is the sum of the widths of 48 average
* lowercase letters and 12 uppercase letters.)
*/

xClientMax = 48 * xChar + 12 * xUpper;

return 0;

case WM_SIZE:

/* Retrieve the dimensions of the client area. */

yClient = HIWORD (lParam);
xClient = LOWORD (lParam);

/*
* Determine the maximum vertical scrolling position.
* The two is added for extra space below the lines
* of text.
*/

yMax = max (0, LINES + 2 - yClient/yChar);

/*
* Make sure the current vertical scrolling position
* does not exceed the maximum.
*/

yPos = min (yPos, yMax);

/*
* Adjust the vertical scrolling range and scroll box
* position to reflect the new yMax and yPos values.
*/

SetScrollRange (hwnd, SB_VERT, 0, yMax, TRUE);
SetScrollPos (hwnd, SB_VERT, yPos, TRUE);

/*
* Determine the maximum horizontal scrolling position.
* The two is added for extra space to the right of the
* lines of text.
*/

xMax = max (0, 2 + (xClientMax - xClient)/xChar);

/*
* Make sure the current horizontal scrolling position
* does not exceed the maximum.
*/

xPos = min (xPos, xMax);

/*
* Adjust the horizontal scrolling range and scroll box
* position to reflect the new xMax and xPos values.
*/

SetScrollRange (hwnd, SB_HORZ, 0, xMax, TRUE);
SetScrollPos (hwnd, SB_HORZ, xPos, TRUE);

return 0;

case WM_PAINT:

/* Prepare the window for painting. */

hdc = BeginPaint(hwnd, &ps);

/*
* Use the current vertical scrolling position and
* coordinates of the invalid rectangle to determine
* the range of new lines that should be drawn in the
* client area.
*/

FirstLine = max (0, yPos + ps.rcPaint.top/yChar - 1);
LastLine = min (LINES, yPos + ps.rcPaint.bottom/yChar);

/* Display these lines. */

for (i = FirstLine;i < LastLine;i++) {
x = xChar * (1 - xPos);
y = yChar * (1 - yPos + i);

TextOut (hdc, x, y, abc[i], lstrlen(abc[i]));
}

/* Indicate that painting is finished. */

EndPaint(hwnd, &ps);
break;

case WM_HSCROLL:
switch(LOWORD (wParam)) {

/* User clicked shaft left of the scroll box. */

case SB_PAGEUP:
xInc = -8;
break;

/* User clicked shaft right of the scroll box. */

case SB_PAGEDOWN:
xInc = 8;
break;

/* User clicked the left arrow. */

case SB_LINEUP:
xInc = -1;
break;

/* User clicked the right arrow. */

case SB_LINEDOWN:
xInc = 1;
break;

/* User dragged the scroll box. */

case SB_THUMBTRACK:
xInc = HIWORD(wParam) - xPos;
break;

default:
xInc = 0;

}

/*
* If applying the horizontal scrolling increment does not
* take the scrolling position out of the scrolling range,
* increment the scrolling position, adjust the position
* of the scroll box, and update the window.
*/

if (xInc = max (-xPos, min (xInc, xMax - xPos))) {
xPos += xInc;
ScrollWindow (hwnd, -xChar * xInc, 0,
(CONST RECT *) NULL, (CONST RECT *) NULL);
SetScrollPos (hwnd, SB_HORZ, xPos, TRUE);
UpdateWindow (hwnd);
}

return 0;


case WM_VSCROLL:
switch(LOWORD (wParam)) {

/* User clicked the shaft above the scroll box. */

case SB_PAGEUP:
yInc = min(-1, -yClient / yChar);
break;

/* User clicked the shaft below the scroll box. */

case SB_PAGEDOWN:
yInc = max(1, yClient / yChar);
break;

/* User clicked the top arrow. */

case SB_LINEUP:
yInc = -1;
break;

/* User clicked the bottom arrow. */

case SB_LINEDOWN:
yInc = 1;
break;

/* User dragged the scroll box. */

case SB_THUMBTRACK:
yInc = HIWORD(wParam) - yPos;
break;

default:
yInc = 0;

}

/*
* If applying the vertical scrolling increment does not
* take the scrolling position out of the scrolling range,
* increment the scrolling position, adjust the position
* of the scroll box, and update the window. UpdateWindow
* sends the WM_PAINT message.
*/

if (yInc = max(-yPos, min(yInc, yMax - yPos))) {
yPos += yInc;
ScrollWindow(hwnd, 0, -yChar * yInc,
(CONST RECT *) NULL, (CONST RECT *) NULL);
SetScrollPos(hwnd, SB_VERT, yPos, TRUE);
UpdateWindow(hwnd);
}

return 0;

 

 

ビットマップのスクロール

ここでは、 アプリケーションのメイン ウィンドウ プロシージャを変更して、 ユーザーがビットマップをスクロールできるようにする方法について説明します。

・ WM_CREATE メッセージの処理

・ WM_SIZE メッセージの処理

・ WM_HSCROLL メッセージと WM_VSCROLL メッセージの処理

ビットマップのスクロールの例では、 画面の内容をビットマップにコピーしてそのビットマップをクライアント領域に表示するメニュー項目を実現しています。

また、 例では、 スクロール バーが生成するWM_HSCROLLメッセージやWM_VSCROLLメッセージを処理して、 ユーザーがビットマップを垂直や水平にスクロールできるようにしています。ただし、 テキストのスクロールの例とは異なり、 ビットマップのスクロールの例では、 BitBlt関数を使ってクライアント領域の無効部分を描画しています。

 

WM_CREATEメッセージの処理

WM_CREATEメッセージを処理するとき、 スクロールに必要な変数を初期化します。それから、 CreateCompatibleDC関数を使って互換デバイス コンテキスト (DC) を作成し、 CreateBitmap関数を使ってビットマップを作成して、 SelectObject関数でそのビットマップをデバイス コンテキストで選択します。互換DCは、 メモリDCとも呼ばれます。

また、 ディスプレイ デバイスに関するデバイス固有情報を取得します。この例のように互換DCを画面用に作成したときは、 GetDeviceCaps関数を使ってこの情報を取得できます。情報には、 ピクセル当たりのカラー ビット数、 カラー プレーン数、 DCの高さと幅などがあります。

 

WM_SIZEメッセージの処理

WM_SIZEメッセージを処理するときは、 クライアント領域と表示されるビットマップの寸法を反映するように、 スクロール範囲とスクロール位置を調整しなければなりません。

SetScrollRange関数は、 スクロール バーの最小位置と最大位置を設定します。SetScrollPos関数は、 スクロール位置を範囲英するようにスクロール バーのつまみを調整します。

 

WM_HSCROLLメッセージとWM_VSCROLLメッセージの処理

WM_HSCROLLメッセージやWM_VSCROLLメッセージを処理するときは、 スクロール バー通知メッセージを調べて、 ユーザーのスクロール操作を反映するようにスクロール位置を新しい値に設定します。スクロール位置がスクロール範囲内ならば、 ScrollWindow関数を使ってウィンドウを新しい位置にスクロールします。それから、 SetScrollPos関数を使って、 スクロール バーのつまみの位置を調整します。

ウィンドウをスクロールすると、 クライアント領域の一部が無効になります。無効なリージョンが更新されるようにするため、 UpdateWindow関数を使ってWM_PAINTメッセージを生成します。アプリケーションは、 WM_PAINTメッセージを処理するとき、 クライアントの下部の無効領域を再描画しなければなりません。この例では、 クライアント領域をスクロールしたりサイズ変更するとき、 BitBlt関数を使って、 ビットマップの適切な部分をクライアント領域の無効部分にコピーします。

 

ビットマップのスクロールの例

次の例は、 ユーザーが画面の内容をビットマップにキャプチャして、 クライアント領域内でビットマップをスクロールできるようにします。

 

HDC hdc;
PAINTSTRUCT ps;

/* These variables are required by BitBlt. */

static HDC hdcWin; /* window DC */
static HDC hdcScreen; /* DC for entire screen */
static HDC hdcScreenCompat; /* memory DC for screen */
static HBITMAP hbmpCompat; /* bitmap handle for old DC */
static BITMAP bmp; /* bitmap data structure */
static BOOL fBlt; /* TRUE if BitBlt occurred */
static BOOL fScroll; /* TRUE if scrolling occurred */
static BOOL fSize; /* TRUE if fBlt & WM_SIZE */

/* These variables are required for horizontal scrolling. */

static int xMinScroll; /* minimum horizontal scroll value */
static int xCurrentScroll; /* current horizontal scroll value */
static int xMaxScroll; /* maximum horizontal scroll value */

/* These variables are required for vertical scrolling. */

static int yMinScroll; /* minimum vertical scroll value */
static int yCurrentScroll; /* current vertical scroll value */
static int yMaxScroll; /* maximum vertical scroll value */

switch (uMsg) {
case WM_CREATE:

/*
* Create a normal DC and a memory DC for the entire
* screen. The normal DC provides a snapshot of the
* screen contents. The memory DC keeps a copy of this
* snapshot in the associated bitmap.
*/

hdcScreen = CreateDC("DISPLAY", (LPCSTR) NULL,
(LPCSTR) NULL, (CONST DEVMODE *) NULL);
hdcScreenCompat = CreateCompatibleDC(hdcScreen);

/*
* Retrieve the metrics for the bitmap associated with the
* regular device context.
*/

bmp.bmBitsPixel =
(BYTE) GetDeviceCaps(hdcScreen, BITSPIXEL);
bmp.bmPlanes = (BYTE) GetDeviceCaps(hdcScreen, PLANES);
bmp.bmWidth = GetDeviceCaps(hdcScreen, HORZRES);
bmp.bmHeight = GetDeviceCaps(hdcScreen, VERTRES);

/* The width must be byte-aligned. */

bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15)/8;

/* Create a bitmap for the compatible DC. */

hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight,
bmp.bmPlanes, bmp.bmBitsPixel, (CONST VOID *) NULL);

/* Select the bitmap for the compatible DC. */

SelectObject(hdcScreenCompat, hbmpCompat);

/* Initialize the flags. */

fBlt = FALSE;
fScroll = FALSE;
fSize = FALSE;

/* Initialize the horizontal scrolling variables. */

xMinScroll = 0;
xCurrentScroll = 0;
xMaxScroll = 0;

/* Initialize the vertical scrolling variables. */

yMinScroll = 0;
yCurrentScroll = 0;
yMaxScroll = 0;

break;

case WM_SIZE: {

int xNewSize;
int yNewSize;

xNewSize = LOWORD(lParam);
yNewSize = HIWORD(lParam);

if (fBlt)
fSize = TRUE;

/*
* The horizontal scrolling range is defined by
* (bitmap_width) - (client_width).
*/

xMaxScroll = max(bmp.bmWidth-xNewSize, 0);
SetScrollRange(hwnd, SB_HORZ, xMinScroll, xMaxScroll,
FALSE);

/*
* The current horizontal scroll value remains within the
* horizontal scrolling range.
*/

xCurrentScroll = min(xCurrentScroll, xMaxScroll);
SetScrollPos(hwnd, SB_HORZ, xCurrentScroll, TRUE);


/*
* The vertical scrolling range is defined by
* (bitmap_height) - (client_height).
*/

yMaxScroll = max(bmp.bmHeight - yNewSize, 0);
SetScrollRange(hwnd, SB_VERT,
yMinScroll, yMaxScroll, FALSE);

/*
* The current vertical scroll value remains within the
* vertical scrolling range.
*/

yCurrentScroll = min(yCurrentScroll, yMaxScroll);
SetScrollPos(hwnd, SB_VERT, yCurrentScroll, TRUE);

}
break;

case WM_PAINT: {

PRECT prect;

hdc = BeginPaint(hwnd, &ps);

/*
* If the window has been resized and the user has
* captured the screen, use the following call to
* BitBlt to paint the window's client area.
*/

if (fSize) {
BitBlt(ps.hdc,
0, 0,
bmp.bmWidth, bmp.bmHeight,
hdcScreenCompat,
xCurrentScroll, yCurrentScroll,
SRCCOPY);

fSize = FALSE;
}

/*
* If scrolling has occurred, use the following call to
* BitBlt to paint the invalid rectangle.
*
* The coordinates of this rectangle are specified in the
* RECT structure to which prect points.
*
* Note that it is necessary to increment the seventh
* argument (prect->left) by xCurrentScroll and the
* eighth argument (prect->top) by yCurrentScroll in
* order to map the correct pixels from the source bitmap.
*/

if (fScroll) {
prect = &ps.rcPaint;

BitBlt(ps.hdc,
prect->left, prect->top,
(prect->right - prect->left),
(prect->bottom - prect->top),
hdcScreenCompat,
prect->left + xCurrentScroll,
prect->top + yCurrentScroll,
SRCCOPY);

fScroll = FALSE;
}

EndPaint(hwnd, &ps);
}
break;

case WM_HSCROLL: {

int xDelta; /* xDelta = new_pos - current_pos */
int xNewPos; /* new position */
int yDelta = 0;

switch (LOWORD(wParam)) {

/* User clicked the shaft left of the scroll box. */

case SB_PAGEUP:
xNewPos = xCurrentScroll - 50;
break;

/* User clicked the shaft right of the scroll box. */

case SB_PAGEDOWN:
xNewPos = xCurrentScroll + 50;
break;

/* User clicked the left arrow. */

case SB_LINEUP:
xNewPos = xCurrentScroll - 5;
break;

/* User clicked the right arrow. */

case SB_LINEDOWN:
xNewPos = xCurrentScroll + 5;
break;

/* User dragged the scroll box. */

case SB_THUMBPOSITION:
xNewPos = HIWORD(wParam);
break;

default:
xNewPos = xCurrentScroll;
}

/* New position must be between 0 and the screen width. */

xNewPos = max(0, xNewPos);
xNewPos = min(xMaxScroll, xNewPos);

/* If the current position does not change, do not scroll.*/

if (xNewPos == xCurrentScroll)
break;

/* Set the scroll flag to TRUE. */

fScroll = TRUE;

/* Determine the amount scrolled (in pixels). */

xDelta = xNewPos - xCurrentScroll;

/* Reset the current scroll position. */

xCurrentScroll = xNewPos;

/*
* Scroll the window. (The system repaints most of the
* client area when ScrollWindow is called; however, it is
* necessary to call UpdateWindow in order to repaint the
* rectangle of pixels that were invalidated.)
*/

ScrollWindow(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
(CONST RECT *) NULL);
UpdateWindow(hwnd);

/* Reset the scroll bar. */

SetScrollPos(hwnd, SB_HORZ, xCurrentScroll, TRUE);
}
break;

case WM_VSCROLL: {

int xDelta = 0;
int yDelta; /* yDelta = new_pos - current_pos */
int yNewPos; /* new position */

switch (LOWORD(wParam)) {

/* User clicked the shaft above the scroll box. */

case SB_PAGEUP:
yNewPos = yCurrentScroll - 50;
break;

/* User clicked the shaft below the scroll box. */

case SB_PAGEDOWN:
yNewPos = yCurrentScroll + 50;
break;

/* User clicked the top arrow. */

case SB_LINEUP:
yNewPos = yCurrentScroll - 5;
break;

/* User clicked the bottom arrow. */

case SB_LINEDOWN:
yNewPos = yCurrentScroll + 5;
break;

/* User dragged the scroll box. */

case SB_THUMBPOSITION:
yNewPos = HIWORD(wParam);
break;

default:
yNewPos = yCurrentScroll;
}

/* New position must be between 0 and the screen height. */

yNewPos = max(0, yNewPos);
yNewPos = min(yMaxScroll, yNewPos);

/* If the current position does not change, do not scroll.*/

if (yNewPos == yCurrentScroll)
break;

/* Set the scroll flag to TRUE. */

fScroll = TRUE;

/* Determine the amount scrolled (in pixels). */

yDelta = yNewPos - yCurrentScroll;

/* Reset the current scroll position. */

yCurrentScroll = yNewPos;

/*
* Scroll the window. (The system repaints most of the
* client area when ScrollWindow is called; however, it is
* necessary to call UpdateWindow in order to repaint the
* rectangle of pixels that were invalidated.)
*/

ScrollWindow(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
(CONST RECT *) NULL);
UpdateWindow(hwnd);

/* Reset the scroll bar. */

SetScrollPos(hwnd, SB_VERT, yCurrentScroll, TRUE);
}
break;

case WM_COMMAND: /* uMsg: command from app. menu */
switch(wParam) {
case IDM_STC:

/*
* Copy the contents of the current screen
* into the compatible DC.
*/

BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth,
bmp.bmHeight, hdcScreen, 0, 0, SRCCOPY);

/*
* Copy the compatible DC to the client area.
*/

hdcWin = GetDC(hwnd);

BitBlt(hdcWin, 0, 0, bmp.bmWidth, bmp.bmHeight,
hdcScreenCompat, 0, 0, SRCCOPY);

ReleaseDC(hwnd, hdcWin);

fBlt = TRUE;
break;

default:
return (DefWindowProc(hwnd, uMsg,
wParam, lParam));
}
break;

 

 

標準スクロール バー用キーボード インターフェイスの作成

スクロール バー コントロールにはキーボード インターフェイスが組み込まれていますが、 標準スクロール バーにはありません。標準スクロール バーでキーボード インターフェイスを実現するには、 ウィンドウ プロシージャはWM_KEYDOWNメッセージを処理して、 wParamパラメータが示す仮想キー コードを調べなければなりません。仮想キー コードが方向キーに対応していれば、 ウィンドウ プロシージャは、 wParamパラメータに適切なスクロール バー通知コードを設定したWM_HSCROLLメッセージかWM_VSCROLLメッセージを自分自身に送ります。たとえば、 ユーザーがキーを押したとき、 ウィンドウ プロシージャは、 wParamVK_UPが設定されたWM_KEYDOWNメッセージを受け取ります。すると、 ウィンドウ プロシージャは、 wParamの下位ワードにSB_LINEUP通知コードを設定したWM_VSCROLLメッセージを自分自身に送ります。

次の例は、 標準スクロール バーでキーボード インターフェイスを実現する方法を示しています。

 

WORD wScrollNotify = 0xFFFF;

.
.
.

case WM_KEYDOWN:
switch (wParam) {
case VK_UP:
wScrollNotify = SB_LINEUP;
break;

case VK_PRIOR:
wScrollNotify = SB_PAGEUP;
break;

case VK_NEXT:
wScrollNotify = SB_PAGEDOWN;
break;

case VK_DOWN:
wScrollNotify = SB_LINEDOWN;
break;

case VK_HOME:
wScrollNotify = SB_TOP;
break;

case VK_END:
wScrollNotify = SB_BOTTOM;
break;
}

if (wScrollNotify != -1)
SendMessage(hwnd, WM_VSCROLL,
MAKELONG(wScrollNotify, 0), 0L);


break;
.
.
.

 

 

スクロール バー関数

スクロール バーに関する関数を次に示します。

EnableScrollBar

GetScrollPos

GetScrollRange

ScrollDC

ScrollWindow

ScrollWindowEx

SetScrollPos

SetScrollRange

ShowScrollBar

 

 

 

スクロール バー メッセージ

スクロール バーに関するメッセージを次に示します。

SBM_ENABLE_ARROWS

SBM_GETPOS

SBM_GETRANGE

SBM_SETPOS

SBM_SETRANGE

SBM_SETRANGEREDRAW

WM_CTLCOLORSCROLLBAR

WM_HSCROLL

WM_VSCROLL

▲ページトップに戻る

【本を読んでもよくらからない】 … 個別指導でわかりやすくお教えします