MFC   VC++   Visual Studio   Visual Studio   プログラミング

スプリッタウインドウの最小制限 その2

 前回紹介した方法だと一度小さくなったあと指定のサイズになるため、場合によってはビュー内がちらついたりスクロールバーが一瞬表示されるなどの症状が出るため、今回は別の方法を紹介します。

 今回は、仮想関数「TrackRowSize/TrackColumnSize」を使います。
 これらの関数はペインサイズ変更後に呼ばれ、引数にウインドウ内の仕切り線の位置とサイズ変更されたペインの行/列番号が渡されます。
 例えば、3行になっているビューの真ん中のペインサイズを変更した場合(2番目の仕切り線を移動)、スプリッタウインドウ内の仕切り線の位置「y」とペイン番号「1」が渡されます。

 実際にコードで表すと、CSplitterWnd(Ex)から派生させたクラスのヘッダに、
    virtual void TrackRowSize(int y, int row);
と記述し、cppファイル内に以下のように記述します。
void CMySplitterWndEx::TrackRowSize(int y, int row)
{
    ASSERT_VALID(this);
    ASSERT(m_nRows > 1);

    CPoint pt(0, y);
    ClientToScreen(&pt);
    GetPane(row, 0)->ScreenToClient(&pt);
    m_pRowInfo[row].nIdealSize = pt.y;      // new size
    if (pt.y < m_pRowInfo[row].nMinSize)
    {
        // resized too small
       // m_pRowInfo[row].nIdealSize = 0; // make it go away
        m_pRowInfo[row].nIdealSize = m_pRowInfo[row].nMinSize; // ここを変更
        if (GetStyle() & SPLS_DYNAMIC_SPLIT)
            DeleteRow(row);
    }
    else if (m_pRowInfo[row].nCurSize + m_pRowInfo[row+1].nCurSize
            < pt.y + m_pRowInfo[row+1].nMinSize)
    {
        // not enough room for other pane
        if (GetStyle() & SPLS_DYNAMIC_SPLIT)
            DeleteRow(row + 1);
    }
}
 このとき、引数「y」はペインの高さではなく、スプリッタウインドウ内での仕切り位置で、クライアント領域との隙間などを引き、ペインのクライアント座標に変更した「pt.y」がペインの高さになります。

 今回掲載したコードは、あらかじめ「SetRowInfo/SetColumnInfo」関数で最小値を設定しておき、それ以下になったときに最小値にする、というものです。
 このコードは、何行何列でもすべての行のサイズを最小値にするもので、特定の行だけ制限したい場合は、制限したい行だけ「SetRowInfo」で最小値を設定して下さい。

 あと、行ではなく列で制限したい場合は以下のように記述します。
void CMySplitterWnd::TrackColumnSize(int x, int col)
{
    ASSERT_VALID(this);
    ASSERT(m_nCols > 1);

    CPoint pt(x, 0);
    ClientToScreen(&pt);
    GetPane(0, col)->ScreenToClient(&pt);
    m_pColInfo[col].nIdealSize = pt.x;      // new size
    if (pt.x < m_pColInfo[col].nMinSize)
    {
        // resized too small
       // m_pColInfo[col].nIdealSize = 0; // make it go away
        m_pColInfo[col].nIdealSize = m_pColInfo[col].nMinSize; // ここを変更
        if (GetStyle() & SPLS_DYNAMIC_SPLIT)
            DeleteColumn(col);
    }
    else if (m_pColInfo[col].nCurSize + m_pColInfo[col+1].nCurSize
            < pt.x + m_pColInfo[col+1].nMinSize)
    {
        // not enough room for other pane
        if (GetStyle() & SPLS_DYNAMIC_SPLIT)
            DeleteColumn(col + 1);
    }
}
 追記ですが、この方法だと大きくする方向(下/右)へ仕切り線を移動させた場合、一つ下/一つ右の行/列の最小サイズが確保出来ません。
 そこで、上記の場合でもきちんと動作する方法を次で紹介します。
ランキングへ     posted by 遠雷 | Comment(0) | フレーム

スプリッタウインドウの最小制限

 スプリッタウインドウでペインのサイズを一定以下に出来ないようにする方法を紹介します。

 通常、ペインの最小サイズを設定するときは、SetColumnInfoでcxMinを設定するか、SetRowInfoでcyMinを設定しますが、これだと設定値以下になったとき、幅や高さが"0"になると思います。

 そこで、ユーザーがペインの幅や高さを設定したサイズ以下にしようとしたとき、設定したサイズに戻るようにします。

 方法は、CSplitterWnd(Ex)クラスを派生させ、仮想関数「StopTracking」をオーバーライドします。
 まず、ヘッダで以下のように宣言し、
    virtual void StopTracking(BOOL bAccept);
 cppファイルで、
void CMySplitterWndEx::StopTracking(BOOL bAccept)
{
    CSplitterWndEx::StopTracking(bAccept);

    if(bAccept)        // bAcceptがTRUEのときだけ処理する
    {
        int iPos, iMin;
        GetRowInfo(0, iPos, iMin);    // 0行目の高さを取得
        if(iPos < 100)        // 最小値を"100"に設定
        {
            SetRowInfo(0, 100, 100);    // 高さを"100"に戻す
            RecalcLayout();
        }
    }
}
 上記の例は、0行目の高さが100未満になったとき、高さを100に戻しています。
 違う行を設定したい場合は、GetRowInfoとSetRowInfoの第一引数を指定の行に変更し、列を設定したい場合は、GetRowInfoとSetRowInfoを、それぞれGetColumnInfoとSetColumnInfoに変更して下さい。

 StopTracking関数は、ペインサイズを変更したときだけでなく、カーソル移動などでも呼ばれるため、ペインサイズが変更されたかどうかのフラグ「bAccept」が「TRUE」のときだけ処理するようにしてあります。

 ここで注意点として、このやり方だと一度小さく変更された後、指定のサイズに戻すため、最初に小さくなる描画処理が入り、そのあとまた大きくする描画処理が入るため、
場合によってはビュー内がちらついたり、スクロールバーが表示されたりすることがあります。

 他にも良い方法がありそうなのですが、今のところ確認できていないので、今回はこの方法を紹介しました。
 考えてとしては、LBUTTONUPで小さくなりそうだったら、LBUTTONUPの位置を指定の位置にすることで、一気に指定サイズに出来そうな気はするのですが・・・。
 実際内部では、Min値以下になったら"0"になるように処理している訳ですからね。

 この後、別のきちんとした方法を見つけたので、次の記事で紹介します。
ランキングへ     posted by 遠雷 | Comment(0) | フレーム

スプリッタウインドウの移動制限

 スプリッタウインドウ(CSpritterWnd/CSpritterWndEx)で、仕切り位置を固定し、ユーザーに位置変更をさせない方法。
 また、カーソルが乗っても移動用のカーソルに変更しないようにする。

 まず、CSpritterWnd/CSpritterWndExから派生させたクラスを作る。(ここではCMySplitterWndEx)
 派生先でHitTestをオーバーライドする。

 HitTestは、
virtual int HitTest(CPoint pt) const;
 と宣言されている。

 HitTestの戻り値は、
enum HitTestValue
{
    noHit                   = 0,
    vSplitterBox            = 1,
    hSplitterBox            = 2,
    bothSplitterBox         = 3,        // just for keyboard
    vSplitterBar1           = 101,        // 横方向の上下に動かすバー
    vSplitterBar15          = 115,
    hSplitterBar1           = 201,        // 縦方向の左右に動かすバー
    hSplitterBar15          = 215,
    splitterIntersection1   = 301,        // 縦と横が交わる点
    splitterIntersection225 = 525
};
 と宣言されているので、操作禁止にしたい方向のときに"0"を返すようにする。
int CMySplitterWndEx::HitTest(CPoint pt) const
{
    int ht = CSplitterWnd::HitTest(pt);// CSpritterWndExは、テーマの変更処理が追加されているだけなので、
                                            // CSplitterWndでも良い。
    if(ht == 201 || ht == 301)       // 横方向のバーだけ上下に移動可
        ht = 0;                        // ヒットしてないことにする

    return ht;
}
 この例だと、横方向のバーだけ上下に移動出来るようになり、縦方向や交差している点ではカーソルも変更されない。
 enumのHitTestValueはcppファイル内で宣言されているため、使いたい場合は自分で追加する。
ランキングへ     posted by 遠雷 | Comment(0) | フレーム