ファイルをバイナリ形式で表示する (C/C++編)

ファイルデータをバイナリ形式で表示する

ファイルデータ・ビューでは以下のイベントを処理します。

  • 描画イベント。

  • 垂直スクロールバーの操作イベント。

  • 水平スクロールバーの操作イベント。

  • マウスホイールの操作イベント。

  • マウスの左ボタンを押下したときの操作イベント。

  • マウスホイールの操作イベント。

  • ウインドウサイズの変更イベント。

ファイルデータ・ビューの描画イベントハンドラ

ファイルデータ・ビューの描画イベント処理を追加します。

「クラスウィザード」を開いて、「CFileDataView」クラスに「仮想関数」タブから「OnDraw」を選択してイベントハンドラ「OnDraw()」を追加します。



画面の描画を直接画面のコンテキストに対して行うと、描画の実行が即時画面に反映されてしまう為に、画面表示がチラついてしまいます。

(描画開始時の全画面クリアで全画面が一瞬 背景色になるのが原因です。)

これ、見えている画面に直接描画しないで、画面と同じサイズのビットマップを用意して、ビットマップに全て描画してからビットマップを画面に上書きすれば、直接画面のコンテキストに対する全画面クリアが必要なくなります。

これを「ダブルバッファリング」といいます。

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  ウインドウの描画イベント処理.
 *
 *  @param  [in]    pDC     デバイスコンテキスト.
 *
 *  @retval なし.
 */
void CFileDataView::OnDraw( CDC* pDC )
{
    //! ウインドウのクライアント領域のサイズを取得する.
    CRect area;
    GetClientRect( &area );

    //! 仮想デバイスコンテキスト作成する.
    CDC memDC;
    memDC.CreateCompatibleDC( pDC );

    //! 仮想デバイスコンテキストにビットマップ設定する.
    CBitmap memBmp;
    memBmp.CreateCompatibleBitmap( pDC, area.Width(), area.Height() );
    CBitmap* def_bitmap = memDC.SelectObject( &memBmp );

    //! 等幅フォントを作成して描画に使用するフォントを入れ替える.
    CFont font;
    if( !CreateFont( font ) ){
        return;
    }
    CFont* def_font = memDC.SelectObject( &font );

    //! 背景をクリアする(ビットマップを背景色で塗りつぶす).
    CBrush white( RGB( 255, 255, 255 ) );
    memDC.FillRect( &area, &white );

    //! 仮想デバイスコンテキストに対して画面の描画を行う.
    DrawMain( &memDC, area );

    //! 仮想デバイスコンテキストのビットマップを描画する.
    BitBlt( pDC->GetSafeHdc(), 0, 0, area.Width(), area.Height(), memDC.GetSafeHdc(), 0, 0, SRCCOPY );

    //! 入れ替えたフォントを元に戻して、作成した等幅フォントを破棄する.
    memDC.SelectObject( def_font );
    font.DeleteObject();

    //! 仮想デバイスコンテキストのビットマップを元に戻して、作成したビットマップを破棄する.
    memDC.SelectObject( def_bitmap );
    memBmp.DeleteObject();

    //! 仮想デバイスコンテキストを破棄する.
    memDC.DeleteDC();
}
	

ダブルバッファリングよる画面描画の手順は以下のとおりです。

  1. ウインドウのクライアント領域のサイズを取得します。

  2. 仮想デバイスコンテキスト作成します。

  3. 仮想デバイスコンテキストにビットマップ設定します。

  4. 今回は等幅フォントを使用するのでフォントを作成して入れ替えます。

  5. ビットマップを背景色でクリアします。

  6. 仮想デバイスコンテキストに対してファイルデータの描画を行います。

  7. 仮想デバイスコンテキストのビットマップを画面のコンテキストに描画します。

  8. 入れ替えたフォントを元に戻して、作成した等幅フォントを破棄します。

  9. 仮想デバイスコンテキストのビットマップを元に戻して、作成したビットマップを破棄します。

  10. 仮想デバイスコンテキストを破棄します。

ファイルデータ・ビューを描画する

ファイルデータの描画は以下の手順で行います。

  1. ビューの描画先立ちフォントの幅と高さを取得します。

  2. ドキュメントから描画するファイルデータとサイズを取得します。

  3. 最初にスケールを描画します。

  4. 1画面に描画可能な行数を画面の高さとフォントの高さから算出します。

    (描画可能な行数) = (画面の高さ + (フォントの高さ - 1)) ÷ (フォントの高さ)

  5. 画面の高さがフォントの高さで割り切れない場合に(画面の高さ)÷(フォントの高さ)では端数の領域が空白になってしまうので、(フォントの高さ-1)だけ画面の高さを割り増しして端数の領域も1行分として増やして文字を描画するようにして、空白にならないようにします。

  6. スケールの1行を除いた1画面に描画可能な行数分のファイルデータを1行づつバイナリ形式で描画します。

  7. 最後にスクロールバーの表示を更新します。
    (通常はスクロールバーの表示は画面の再描画では変わらないのですが、ビューの画面サイズが変更されたときの再描画のときは、変更された画面サイズに合わせてスクロールバーのつまみの幅も変更が必要になります。)

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  描画処理のメイン.
 *
 *  @param  [in]    pDC     デバイスコンテキスト.
 *  @param  [in]    area    ウインドウのクライアント領域のサイズ.
 *
 *  @retval なし.
 */
void CFileDataView::DrawMain( CDC* pDC, CRect& area )
{
    //! フォントの高さと幅を取得する.
    TEXTMETRIC tm;
    pDC->GetTextMetrics( &tm );
    int fontH = tm.tmHeight + tm.tmExternalLeading;
    int fontW = tm.tmAveCharWidth;

    CMFCApp001Doc* pDoc = dynamic_cast( GetDocument() );

    //! ファイルの内容とバイト数を取得する.
    BYTE* buf  = pDoc->GetFileBuffer();
    DWORD size = pDoc->GetFileSize();
    if( buf == NULL || size == 0 ){
        return;
    }
    //! スケールを描画する.
    DrawScale( pDC, area, fontH, fontW );

    //! 1画面に表示可能な行数を算出する.
    int lines = (fontH > 0) ? (area.Height() + (fontH - 1)) / fontH : 1;

    //! ファイルデータをバイナリ形式で描画する.
    DWORD adr = m_ScrollV.GetPos() * 16;
    for( int i = 1; i <= lines; i++ ){
        if( adr >= size ){
            break;
        }
        //! 1行分のバイナリデータを描画する.
        DrawBinaryData( pDC, buf, size, i, fontH, fontW, adr );
        adr += 16;
    }
}
	

等幅フォントを作成する

以下の固定幅フォントを作成します。

LOGFONT構造体の設定値

メンバ名設定値
lfHeight16
lfWeightFW_DONTCARE
lfCharSetDEFAULT_CHARSET
lfPitchAndFamilyFIXED_PITCH | FF_DONTCARE
lfFaceName"MS ゴシック"

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  等幅フォントを作成する.
 *
 *  @param  [in]    font    フォント・オブジェクト.
 *
 *  @retval TRUE = OK. / FALSE = NG.
 */
BOOL CFileDataView::CreateFont( CFont& font )
{
    LOGFONT lf;
    memset( &lf, 0, sizeof(LOGFONT) );
    lf.lfHeight         = 16;
    lf.lfWeight         = FW_DONTCARE;
    lf.lfCharSet        = DEFAULT_CHARSET;
    lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
    _tcscpy_s( lf.lfFaceName, LF_FACESIZE, _T("MS ゴシック") );

    return font.CreateFontIndirect( &lf );
}
	

スケールを描画する

バイナリデータが見易くなるようにスケールを描画します。

垂直方向にスクロールしてもスケールは常に表示するようにしますが、水平方向のスクロールした場合はスケールも移動するようにします。

横方向の描画開始座標を水平スクロールバーの現在の値から算出します。

(横方向の描画開始座標) = (水平スクロールバーのつまみ位置) × (フォントの幅) × -1

水平スクロールバーのつまみが右に移動するとビューの画像は左に移動することになるので、オフセットに-1をかけます。

次にオフセットアドレスを描画する領域の背景を塗りつぶしておきます。

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  スケールを描画する.
 *
 *  @param  [in]    pDC     デバイスコンテキスト.
 *  @param  [in]    area    ウインドウのクライアント領域のサイズ.
 *  @param  [in]    fontH   フォントの高さ.
 *  @param  [in]    fontW   フォントの幅.
 *
 *  @retval なし.
 */
void CFileDataView::DrawScale( CDC* pDC, CRect& area, int fontH, int fontW )
{
    CRect rc1( area );
    rc1.bottom = fontH;

    CBrush bkCol( RGB( 128, 128, 128 ) );
    pDC->FillRect( &rc1, &bkCol );

    int x = -(m_ScrollH.GetPos()) * fontW;

    CRect rc2( area );
    rc2.right = rc2.left + (9 * fontW);
    rc2.MoveToX( x );
    pDC->FillRect( &rc2, &bkCol );

    pDC->SetTextColor( RGB( 255, 255, 255 ) );
    pDC->SetBkColor(   RGB( 128, 128, 128 ) );

    pDC->TextOut( x               , 0, CString(_T("ADDRESS:")) );
    pDC->TextOut( x + (10 * fontW), 0, CString(_T("+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F")) );
    pDC->TextOut( x + (58 * fontW), 0, CString(_T("ASCII")) );
}
	

1行分のバイナリデータを描画する

1行分のファイルデータの表示を行います。

  1. 最初に描画する行のファイル上のオフセット・アドレスを描画します。

  2. オフセット・アドレスは16進数で8桁固定で描画するので、オフセット・アドレスを16進数8桁の文字列に変換して描画します。

  3. オフセット・アドレスの描画とファイルデータの描画では文字色と背景色が違うのでテキスト描画の文字色と背景色を変更します。

  4. 次にファイルデータを16バイト(1行分)描画します。

    forループで以下の処理を16回実行します。

    16バイト描画する途中でファイルデータが終わってしまった場合は、そこでファイルデータの描画は終了にします。

  5. ファイルデータは16進数で2桁固定で描画するので、ファイルデータを16進数2桁の文字列に変換して描画します。

  6. ファイルデータをASCII文字に変換して、描画用のASCII文字列も併せて作ります。
    (ファイルデータが0x20以上0x7F以下の場合は該当するASCII文字にし、それ以外は「.」に変換します。)

  7. 最後にforループ内で作成しておいたASCII文字列を描画します。

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  1行分のバイナリデータを描画する.
 *
 *  @param  [in]    pDC     デバイスコンテキスト.
 *  @param  [in]    buf     ファイルバッファのポインタ.
 *  @param  [in]    size    ファイルバッファのサイズ.
 *  @param  [in]    line    描画開始する行位置.
 *  @param  [in]    fontH   フォントの高さ.
 *  @param  [in]    fontW   フォントの幅.
 *  @param  [in]    adr     描画開始するバイナリデータのアドレス.
 *
 *  @retval なし.
 */
void CFileDataView::DrawBinaryData( CDC* pDC, BYTE* buf, DWORD size, int line, int fontH, int fontW, DWORD adr )
{
    int x = -(m_ScrollH.GetPos()) * fontW;
    int y = fontH * line;

    //! オフセット・アドレスを表示する.
    CString address;
    address.Format( _T("%08X "), adr );
    pDC->SetTextColor( RGB( 255, 255, 255 ) );
    pDC->SetBkColor(   RGB( 128, 128, 128 ) );
    pDC->TextOut( x, y, address );

    //! バイナリデータの文字色と背景色を設定する.
    pDC->SetTextColor( RGB(   0,   0,   0 ) );
    pDC->SetBkColor(   RGB( 255, 255, 255 ) );

    CString bin,ascii;
    for( int i = 0; i < 16; i++ ){
        //! ファイルデータが無くなったら処理を終了する.
        if( adr >= size ){
            break;
        }
        TCHAR c = buf[adr];

        //! ファイルデータを16進数で表示する.
        bin.Format( _T("%02X"), c );
        pDC->TextOut( x + ((10 + (i * 3)) * fontW), y, bin );

        //! ファイルデータをASCIIデータで貯めておく.
        if( c < _T(' ') || c >= 0x80 ){
            c = _T('.');
        }
        ascii += c;

        adr++;
    }
    //! 最後にASCIIデータを表示する.
    pDC->TextOut( x + (58 * fontW), y, ascii );
}
	

スクロールバーを設定する

ウインドウサイズ等が変更されると、スクロールバーのページサイズの変更が必要になります。

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  スクロールバーを設定する.
 *
 *  @param  なし.
 *
 *  @retval なし.
 */
void CFileDataView::SetScrollBar( void )
{
    CDC* pDC = GetDC();

    //! 等幅フォントを作成して描画に使用するフォントを入れ替える.
    CFont font;
    if( !CreateFont( font ) ){
        return;
    }
    CFont* def_font = pDC->SelectObject( &font );

    //! フォントの高さと幅を取得する.
    TEXTMETRIC tm;
    pDC->GetTextMetrics( &tm );
    int fontH = tm.tmHeight + tm.tmExternalLeading;
    int fontW = tm.tmAveCharWidth;

    if( fontH > 0 && fontW > 0 ){
        //! ファイルのバイト数を取得する.
        CMFCApp001Doc* pDoc = dynamic_cast( GetDocument() );
        DWORD          size = pDoc->GetFileSize();

        //! ウインドウのクライアント領域のサイズを取得する.
        RECT area;
        GetClientRect( &area );

        //! 縦スクロールバーを設定する.
        int height = (area.bottom - area.top) / fontH;
        m_ScrollV.SetScrollBar( (size / 16) + (height >> 1), height, m_ScrollV.GetPos(), 2 );

        //! 横スクロールバーを設定する.
        m_ScrollH.SetScrollBar( 120, (area.right - area.left) / fontW, m_ScrollH.GetPos(), 2 );
    }
    //! 入れ替えたフォントを元に戻して、作成した等幅フォントを破棄する.
    pDC->SelectObject( def_font );
    font.DeleteObject();
}
	

設定値の計算でフォントサイズを割り算の除数(分母側)で使いますので、仮に値が0だと例外が発生するからフォントサイズが0の時は処理しないようにガードしておきます。
(まぁ、フォントサイズが取得できないことは無いんですけど。。。)

最初にファイルデータ・ビューの表示領域のサイズを取得します。

次に表示領域のサイズとフォントの高さから1ページに表示可能な行数を計算します。

次に表示領域のサイズとフォントの幅から1ページに表示可能な文字数を計算します。

垂直スクロールバーに以下の値を設定します。

項目設定値
スクロール最大値ファイルデータの全行数 + ページサイズの1/2。
ページサイズ1ページに表示可能な行数。
つまみ位置現在のつまみ位置。
スクロールバーの刻み幅2固定。

水平スクロールバーに以下の値を設定します。

項目設定値
スクロール最大値120固定。
ページサイズ1ページに表示可能な文字数。
つまみ位置現在のつまみ位置。
スクロールバーの刻み幅2固定。

垂直スクロールバーの操作イベント

垂直スクロールバーの操作イベント処理を追加します。

「クラスウィザード」を開いて、「CFileDataView」クラスに「メッセージ」タブから「WM_VSCROLL」を選択してイベントハンドラ「OnVScroll()」を追加します。



垂直スクロールバーの操作イベントでは以下の処理を行います。

nSBCodeにスクロールバーの操作が指示されているので、ヘルパークラスに渡してスクロールバーの表示を更新します。

このときスクロールバーのつまみの表示位置が変更された場合はファイルデータ・ビューを再描画します。

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  垂直スクロールバーの操作イベント (WM_VSCROLL).
 *
 *  @param  [in]    nSBCode     スクロールバーの動作.
 *  @param  [in]    nPos        スクロール・ボックスの位置.
 *  @param  [in]    pScrollBar  スクロールバー・コントロールのポインタ.
 *
 *  @retval なし.
 */
void CFileDataView::OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar )
{
    //! スクロールバーのつまみ位置が変更された場合、ウインドウを再描画する.
    if( m_ScrollV.Action( nSBCode ) ){
        InvalidateRect( NULL, FALSE );
    }
    //! 最後に基底クラスのメソッドを呼び出す.
    CView::OnVScroll( nSBCode, nPos, pScrollBar );
}
	

水平スクロールバーの操作イベント

水平スクロールバーの操作イベント処理を追加します。

「クラスウィザード」を開いて、「CFileDataView」クラスに「メッセージ」タブから「WM_HSCROLL」を選択してイベントハンドラ「OnHScroll()」を追加します。



水平スクロールバーの操作イベントでは以下の処理を行います。

nSBCodeにスクロールバーの操作が指示されているので、ヘルパークラスに渡してスクロールバーの表示を更新します。

このときスクロールバーのつまみの表示位置が変更された場合はファイルデータ・ビューを再描画します。

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  水平スクロールバーの操作イベント (WM_HSCROLL).
 *
 *  @param  [in]    nSBCode     スクロールバーの動作.
 *  @param  [in]    nPos        スクロール・ボックスの位置.
 *  @param  [in]    pScrollBar  スクロールバー・コントロールのポインタ.
 *
 *  @retval なし.
 */
void CFileDataView::OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar )
{
    //! スクロールバーのつまみ位置が変更された場合、ウインドウを再描画する.
    if( m_ScrollH.Action( nSBCode ) ){
        InvalidateRect( NULL, FALSE );
    }
    //! 最後に基底クラスのメソッドを呼び出す.
    CView::OnHScroll( nSBCode, nPos, pScrollBar );
}
	

マウス・ホイールの操作イベント

マウスホイールの操作イベント処理を追加します。

「クラスウィザード」を開いて、「CFileDataView」クラスに「メッセージ」タブから「WM_MOUSEHWHEEL」を選択してイベントハンドラ「OnMouseWheel()」を追加します。



マウスホイールの操作イベントでは以下の処理を行います。

zDeltaにマウスホイールの回転方向が格納されているので、zDeltaが正数の場合、スクロールバーの上矢印ボタンを押したときと同じ動作、zDeltaが負数の場合、スクロールバーの下矢印ボタンを押したときと同じ動作になるようにします。

ファイルデータ・ビューのウインドウに対して垂直スクロールバーの操作メッセージ(WM_VSCROLL)を発行して垂直スクロールバーを操作したようにします。

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  マウス・ホイールの操作イベント (WM_MOUSEHWHEEL).
 *
 *  @param  [in]    nFlags      押されているキー.
 *  @param  [in]    zDelta      マウス・ホイールの回転方向.
 *  @param  [in]    pt          マウスカーソルの座標.
 *
 *  @retval なし.
 */
BOOL CFileDataView::OnMouseWheel( UINT nFlags, short zDelta, CPoint pt )
{
    //! マウス・ホイールの回転方向からスクロールバーの操作イベントを決める.
    WPARAM up_down = (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP;

    //! 垂直スクロールバーの操作イベントを発生させる.
    SendMessage( WM_VSCROLL, up_down, 0 );

    //! 最後に基底クラスのメソッドを呼び出す.
    return CView::OnMouseWheel( nFlags, zDelta, pt );
}
	

マウスの左ボタンを押下したときの操作イベント

ファイルデータ・ビューにフォーカスをセットしないとマウスホイールのイベントが受けられませんので、マウスホイールでスクロールする為にファイルデータ・ビューを左クリックしたときにフォーカスをセットする為にイベント処理を追加します。



マウスの左ボタンを押下したときの操作イベント処理を追加します。

「クラスウィザード」を開いて、「CFileDataView」クラスに「メッセージ」タブから「WM_LBUTTONDOWN」を選択してイベントハンドラ「OnLButtonDown()」を追加します。



ファイルデータ・ビューでマウスの左ボタンがクリックされたときのイベント処理です。

ファイルデータ・ビューでマウスの左ボタンがクリックされたということは、ファイルデータ・ビューがアクティブになるということなので、ファイルデータ・ビューにフォーカスをセットします。

ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  マウスの左ボタンを押下したときの操作イベント (WM_LBUTTONDOWN).
 *
 *  @param  [in]    nFlags      押されているキー.
 *  @param  [in]    point       マウスカーソルの座標.
 *
 *  @retval なし.
 */
void CFileDataView::OnLButtonDown( UINT nFlags, CPoint point )
{
    //! フォーカスをこのウインドウにセットする.
    SetFocus();

    //! 最後に基底クラスのメソッドを呼び出す.
    CView::OnLButtonDown( nFlags, point );
}
	

ウインドウサイズが変更されたときのイベント

ウインドウサイズが変更されると、ビューの描画できる領域が変わるので、スクロールバーのつまみの長さをビューの描画領域に合わせて変更します。
その為、ファイルデータ表示ビューのウインドウサイズが変更されたときのイベント処理を追加し、イベントが発生したときにスクロールバーの再設定を行います。



ウインドウサイズが変更されたときのイベント処理を追加します。

「クラスウィザード」を開いて、「CFileDataView」クラスに「メッセージ」タブから「WM_SIZE」を選択してイベントハンドラ「OnSize()」を追加します。



ソースコード [FileDataView.cpp]


/****************************************************************************/
/*!
 *  @brief  ウインドウサイズが変更されたときのイベント (WM_SIZE).
 *
 *  @param  [in]    nType       サイズ変更の種類.
 *  @param  [in]    cx          クライアント領域の新しい幅.
 *  @param  [in]    cy          クライアント領域の新しい高さ.
 *
 *  @retval なし.
 */
void CFileDataView::OnSize( UINT nType, int cx, int cy )
{
    //! 最初に基底クラスのメソッドを呼び出す.
    CView::OnSize( nType, cx, cy );

    //! ウインドウが最小化されるときは何もしない.
    if( nType == SIZE_MINIMIZED ){
        return;
    }
    //! スクロールバーを更新する.
    SetScrollBar();
}
	

スクロールバーのヘルパークラス

今回のプログラムではファイルデータ・ビューのウインドウスタイルを変更してスクロールバーを表示する為、独立したスクロールコントロールのクラスがありません。

ファイルデータ・ビューのクラス内でスクロールバーの操作処理を記述してもよかったのですが、汎用性を考えてスクロールバーの操作クラスを分離しました。

スクロールバーの操作はファイルデータ・ビューから行わなければならないので、スクロールバーのヘルパークラスを生成するときにファイルデータ・ビューのオブジェクトを引数で渡してスクロールバーのヘルパークラスとファイルデータ・ビューの紐づけを行います。

ファイルデータ・ビューでスクロールバーの操作イベントが発生すると、スクロールバーのヘルパークラスを呼び出してスクロールバーの操作を行います。

スクロールバーとウインドウを結びつける

スクロールバーのヘルパークラスとスクロールバーを表示するウインドウを紐づけします。

Bind()しないとヘルパークラスの他のメソッドは動作しないようにガードをかけるようにします。

ソースコード [WrapScrollBar.cpp]


/****************************************************************************/
/*!
 *  @brief  スクロールバーとウインドウを結びつける.
 *
 *  @param  [in]    wnd     スクロールバーを表示するウインドウ.
 *  @param  [in]    type    SB_VERT=垂直スクロールバー / SB_HORZ=水平スクロールバー.
 *
 *  @retval なし.
 */
void    CWrapScrollBar::Bind( CWnd* wnd, int type )
{
    m_CWnd = wnd;
    m_Type = type;
}
	

スクロールバーを設定する

スクロールバーの設定では以下の処理を行います。

  • 現在のスクロールバーの値を設定する値が同じ場合は何もしません。

  • ウインドウのスクロールバーに以下の値を設定します。

    項目設定値
    スクロールバーの最小値0固定。
    スクロールバーの最大値引数で指定。
    スクロールバーのページサイズ(つまみの幅)引数で指定。
    スクロールバーのつまみの位置引数で指定。

    引数scaleは、スクロールバーの矢印ボタンを1回押したときのスクロールバーの移動量で、ウインドウのスクロールバーには設定しませんが、スクロールバーの操作処理で使用するので値を保存しておきます。

ソースコード [WrapScrollBar.cpp]


/****************************************************************************/
/*!
 *  @brief  スクロールバーを設定する.
 *
 *  @param  [in]    maximum スクロールバーの最大値.
 *  @param  [in]    page    スクロールバーのつまみの幅.
 *  @param  [in]    pos     スクロールバーのつまみ位置.
 *  @param  [in]    scale   スクロールバーの刻み幅.
 *
 *  @retval なし.
 */
void    CWrapScrollBar::SetScrollBar( int maximum, int page, int pos, int scale )
{
    //! Bind()を呼び出していない場合は処理しない.
    if( m_CWnd == NULL ){
        return;
    }
    //! 設定値が変更されない場合は処理しない.
    if(( m_Scale == scale   )
    && ( m_Max   == maximum )
    && ( m_Pos   == pos     )
    && ( m_Page  == page    )){
        return;
    }
    //! スクロールバーの刻み幅等を保存する.
    m_Scale = scale;
    m_Max   = maximum;
    m_Pos   = pos;
    m_Page  = min( page, maximum );

    //! スクロールバーを設定する.
    SCROLLINFO info;
    memset( &info, 0, sizeof(SCROLLINFO) );
    info.cbSize     = sizeof(SCROLLINFO);
    info.fMask      = SIF_POS | SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL;
    info.nMin       = 0;
    info.nMax       = m_Max;
    info.nPage      = m_Page;
    info.nPos       = m_Pos;
    m_CWnd->SetScrollInfo( m_Type, &info, TRUE );
}
	

スクロールバーを操作する

スクロールバーの操作手順は以下のようにします。

  1. 現在のスクロールバーの設定をウインドウのスクロールバーから取得します。

  2. 引数で指示された動作により以下のようにスクロールバーのつまみ位置を決めます。

    項目設定値
    SB_TOPinfo.nMin (スクロールバーの最小値)。
    SB_BOTTOMinfo.nMax (スクロールバーの最大値)。
    SB_LINEUPinfo.nPos - m_Scale (現在のつまみ位置 - スクロールバーの刻み幅)。
    SB_LINEDOWNinfo.nPos + m_Scale (現在のつまみ位置 + スクロールバーの刻み幅)。
    SB_PAGEUPinfo.nPos - info.nPage (現在のつまみ位置 - ページサイズ)。
    SB_PAGEDOWNinfo.nPos + info.nPage (現在のつまみ位置 + ページサイズ)。
    SB_THUMBTRACKinfo.nTrackPos (現在のトラッキング位置)。
    その他info.nPos (現在のつまみ位置)。

  3. ウインドウのスクロールバーのつまみ位置を実際に変更します。

ソースコード [WrapScrollBar.cpp]


/****************************************************************************/
/*!
 *  @brief  スクロールバーの操作.
 *
 *  @param  [in]    action  スクロールバーの動作.
 *
 *  @retval TRUE = つまみ位置変更あり / FALSE = つまみ位置変更なし.
 */
BOOL    CWrapScrollBar::Action( UINT action )
{
    //! Bind()を呼び出していない場合は処理しない.
    if( m_CWnd == NULL ){
        return FALSE;
    }
    SCROLLINFO info;
    memset( &info, 0, sizeof(SCROLLINFO) );
    info.cbSize = sizeof( SCROLLINFO );
    info.fMask  = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;

    //! 現在のスクロールバーの設定を読み込む.
    if( m_CWnd->GetScrollInfo( m_Type, &info ) ){

        //! スクロールバーの表示位置を決定する.
        int pos;
        switch( action ){
          case SB_TOP       : pos = info.nMin;                      break;      // 最上部から表示.
          case SB_BOTTOM    : pos = info.nMax;                      break;      // 最下部から表示.
          case SB_LINEUP    : pos = info.nPos - (int)m_Scale;       break;      // n行  上にずらす.
          case SB_LINEDOWN  : pos = info.nPos + (int)m_Scale;       break;      // n行  下にずらす.
          case SB_PAGEUP    : pos = info.nPos - (int)info.nPage;    break;      // 1画面上にずらす.
          case SB_PAGEDOWN  : pos = info.nPos + (int)info.nPage;    break;      // 1画面下にずらす.
          case SB_THUMBTRACK: pos = info.nTrackPos;                 break;      // つまみ位置で表示.
          default           : pos = info.nPos;                      break;
        }
        //! スクロールバーのつまみ位置を設定する.
        return SetScrollPos( pos );
    }
    return FALSE;
}
	

スクロールバーのつまみ位置を設定する

スクロールバーのつまみ位置の設定は以下のようにします。

  1. 指定されたつまみ位置がスクロールバーの最小値より小さい値の場合は、設定するつまみ位置はスクロールバーの最小値にします。

  2. 指定されたつまみ位置がスクロールバーの最大値より大きい値の場合は、設定するつまみ位置はスクロールバーの最大値にします。

  3. 設定するつまみ位置が現在のスクロールバーのつまみ位置と変わる場合のみ、実際にウインドウのスクロールバーに対してつまみ位置を再設定します。

ソースコード [WrapScrollBar.cpp]


/****************************************************************************/
/*!
 *  @brief  スクロールバーのつまみ位置を設定する.
 *
 *  @param  [in]    pos     変更後のつまみ位置.
 *
 *  @retval TRUE = つまみ位置変更あり / FALSE = つまみ位置変更なし.
 */
BOOL    CWrapScrollBar::SetScrollPos( int pos )
{
    //! Bind()を呼び出していない場合は処理しない.
    if( m_CWnd == NULL ){
        return FALSE;
    }
    //! スクロールバーのつまみ位置を計算する.
    int new_pos = min( max( pos, 0 ), (m_Max - m_Page) );

    if( m_Pos != new_pos ){
        m_Pos  = new_pos;

        //! スクロールバーのつまみ位置を設定する.
        SCROLLINFO info;
        memset( &info, 0, sizeof(SCROLLINFO) );
        info.cbSize = sizeof(SCROLLINFO);
        info.fMask  = SIF_POS;
        info.nPos   = m_Pos;
        m_CWnd->SetScrollInfo( m_Type, &info, TRUE );

        return TRUE;
    }
    return FALSE;
}
	



商標に関する表示