FlexGridをSDIで使う方法を教えてください

[戻る]
kkawa 2010/02/06(土) 19:27:10
WIN98SE VC++6.0 MFC SDI で CListCtrlを貼付けていたのをFlexGridに入替てみたのですが
うまくいきません。以前このコーナーの質疑回答にありましたコードを参考にやってみましたが
CALLBACK関数のWM_PAINTが無限ループになっているような状態?です。宜しくお願いします。

//CTestView.h
   void DrawGraph();
   CMSFlexGrid m_myGrid;

//CTestView.cpp
WNDPROC OldProc;
void *pGrid;

LRESULT CALLBACK NewProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT ret = OldProc(hwnd, message, wParam, lParam);
    if (message == WM_PAINT) 
        ((CTestView*)pGrid)->DrawGraph();
    return ret;
}

int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
   if (CTabView::OnCreate(lpCreateStruct) == -1)
      return -1;
    CRect rect;
    GetDisplayRect( &rect);
    m_myGrid.Create("grid", WS_CHILD | WS_VISIBLE, rect, this, 1);
    OldProc = (WNDPROC)::SetWindowLong(m_myGrid.m_hWnd, GWL_WNDPROC, 
                                       (LONG)NewProc);
    pGrid = this;
    GetTabCtrl().InsertItem( 0, _T("ABC") );
    return 0;
}

void CTestView::DrawGraph()
{
   m_myGrid.Clear();
   m_myGrid.SetRows(5);
   m_myGrid.SetCols(3);
}

void CTestView::OnSize(UINT nType, int cx, int cy) 
{
   CTabView::OnSize(nType, cx, cy);
   if( m_myGrid.GetSafeHwnd() != 0){
     CRect rect;
     GetDisplayRect( &rect);
     rect.DeflateRect( 1, 1, 1, 1);
     m_myGrid.MoveWindow( &rect);
   }
}

bun 2010/02/07(日) 08:44:15
>    if (message == WM_PAINT) 
>        ((CTestView*)pGrid)->DrawGraph();
の呼び出しをやめてみたら、無限ループは収まりますか?

kkawa 2010/02/07(日) 11:57:49
[[解決]]
ありがとうございます。
>    if (message == WM_PAINT) 
>        ((CTestView*)pGrid)->DrawGraph();
の呼び出しをやめてみたら、無限ループは収まりますか?

はい タブビューにグリッドコントロールが左上角に貼付いた状態で収まります。
マウスカーソルも正常に動きます。MSFexGridのポインタを横取するにはここから
どのように展開すればいいのかアドバイスいただければ幸せです。
MFCも初心者マークですが、SDKの混在も初めてなのですみません。

kkawa 2010/02/07(日) 12:12:19
FlexGridをSDIで使う方法を教えてください。

「解決済み」のチェック間違えてしまいました。
申し訳ありません。

bun 2010/02/09(火) 09:01:44
そうであれば、WM_PAINTメッセージを受け取った際に、WM_PAINTが
発生するような処理を行っていることが問題です。
DrawGraph()を呼び出すタイミングをよく考えましょう。

CTestView::OnCreate()内で、m_myGrid.Create()を呼び出した直後でも良いのでは?

PATIO 2010/02/09(火) 10:42:02
DrawGraphの内容って関数名に合っていない様な気がしますが、
この辺は感覚的なものもあるとは思うので一概には言えませんね。

内容的に表示の初期設定かなと言う気がします。
bunさんが言われているようにクリエイトした後、
初期設定として呼び出した方が良さそうです。
別の場所でも呼び出すと言うような事が無いのなら
クリエイト後に同じコードが書かれていても違和感はないかもしれません。

bunさんも書かれていますが、
WM_PAINTのメッセージを受け取った時の処理で
画面の書き換えを誘発するような処理は御法度です。
例えばですが、WM_PAINTを受け取った時の処理の中で
MessageBoxを出すような処理をすると
MessageBoxが消える時に画面の再描画が必要になる(WM_PAINTが発行される)
ので無限ループになってしまいます。
WM_PAINTを受け取った時の処理は基本的にDCへの描画処理だけに
限定した方が良いと思います。

kkawa 2010/02/09(火) 23:38:15
bunさん PATIOさん ありがとうございます。
FlexGridはサブクラス化して使うものと勘違いしておりました。
まあ、サブクラス化自体がよくわからんのですが <"o_o"">
FlexGridの解説がなかなか見当たらないので無限迷路に落ちていましたが
弄くり廻しておりましたら、とりあえず以下のコードで動き始めました。
ただ、このスケルトンが正解ではないとは思いますので、今後ともよろしく
お願いいたします。
TabViewとGridの組み合わせ(Excelもどき)をやってみたつもりです。

CMSFlexGrid m_Grid;

int C**View::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CTabView::OnCreate(lpCreateStruct) == -1)
        return -1;
    //MSFlexGridのライセンスキー
    WCHAR pwchLicenseKey[] =
    {

    };
    CRect rect;
    GetDisplayRect( &rect);
    BSTR bstrLicense = ::SysAllocStringLen(
    pwchLicenseKey, sizeof(pwchLicenseKey)/sizeof(WCHAR));
    if (m_Grid.Create(NULL, WS_VISIBLE|WS_CHILD, rect, this,
        IDC_MSFLEXGRID, NULL, FALSE, bstrLicense) == 0) {
            AfxMessageBox("Can't create MSFlexGrid");
    }
    ::SysFreeString(bstrLicense);

    COleFont oleFont = m_Grid.GetFont();
    oleFont.SetName("MS Pゴシック"); // フォントを設定
    //oleFont.SetBold(TRUE); // ボールドに
    CY cy;
    cy.int64 = 10 * 10000;
    oleFont.SetSize(cy); // フォントサイズを 10ポイントに

    //グリッドは最初に全部表示しておく
    m_Grid.Clear();
    // 行列の設定(タイトル行、見出し列含む)
    m_Grid.SetRows(10);    // 行
    m_Grid.SetCols(5);        // 列
    //タブ文字サイズをメニューバーサイズと同じにする
    SendMessage(WM_SETFONT, (WPARAM)::GetStockObject(DEFAULT_GUI_FONT), 
0);
    //文字色変化 TCS_HOTTRACK
    //上 TCS_FLATBUTTONS
    //下 TCS_BOTTOM
    //左 TCS_VERTICAL
    GetTabCtrl().ModifyStyle( 0, TCS_BOTTOM);    
    GetTabCtrl().ModifyStyle( 0,TCS_HOTTRACK);
    GetTabCtrl().InsertItem( 0, _T("最初のタブ") );
    GetTabCtrl().InsertItem( 1, _T("2番目のタブ") );
    return 0;
}

void C**View::OnSize(UINT nType, int cx, int cy) 
{
    CTabView::OnSize(nType, cx, cy);
    if( m_Grid.GetSafeHwnd() != 0){
        CRect rect;
        GetDisplayRect( &rect);
        rect.DeflateRect( 1, 1, 1, 1);
        m_Grid.MoveWindow( &rect);
    }
}


void C**View::OnInitialUpdate() 
{
    CTabView::OnInitialUpdate();
    m_Grid.Clear();
    // 固定行列の文字を設定
    CString s$ = "<  1列目|<  2列目|<  3列目|<  3列目|<   4列目     ";
    m_Grid.SetFormatString (s$);
    //セルにデータを書き込む
    for(int i=1;i<=5;i++) {
        m_Grid.SetCol(i);
        for(int j=0;j<=5;j++) {
            m_Grid.SetTextMatrix(i,j,"A");
            m_Grid.SetRow(j);
            //m_Grid.SetCellAlignment(7); //文字位置(右中央)
        }
    }

    nTab_No = GetTabCtrl().GetCurSel();
    switch(nTab_No){
        case 0:
            OnTab1(); break;
        case 1:
            OnTab2(); break;
        default:
            break;
    };

}

void C**View::OnTab1() 
{
    
}

void C**View::OnTab2() 
{
    
}

bun 2010/02/10(水) 10:25:50
> FlexGridはサブクラス化して使うものと勘違いしておりました。
私はサブクラス化することの方が多いです。
そのやり方が分からないということかな?
VC++6.0を卒業して久しいので、詳しいやり方を忘れてしまったんですが、
リソースにグリッドを貼り付けた上で、クラスウィザードから変数の追加
とかするんじゃなかったかな。
以下のようなコードが吐き出されたら、サブクラス化は完了です。
  DDX_Control(pDX, IDC_MSFLEXGRID, m_Grid);

さて、今回のコードは大体動作するのではないでしょうか。
ただ、無駄が多いコードなので、無駄を指摘させていただきますね。

まず、OnInitialUpdate() ですが、
>    for(int i=1;i<=5;i++) {
>        m_Grid.SetCol(i);
>        for(int j=0;j<=5;j++) {
>            m_Grid.SetTextMatrix(i,j,"A");
>            m_Grid.SetRow(j);
>            //m_Grid.SetCellAlignment(7); //文字位置(右中央)
>        }
>    }

m_Grid.SetTextMatrix(i,j,"A"); とするなら、
m_Grid.SetCol(i); や m_Grid.SetRow(j); は不要です。

m_Grid.SetCellAlignment(7); をやろうとした跡がありますが、
そのための SetCol(i), SetRow(j) なのかな?
全てのセルの文字位置を[右中央]にしたいなら、もっといい手があります。

常に[右中央]固定なら、リソースの設定でやりましょう。それが最速です。
ソースコードからやるなら、m_Grid.SetColAlignment(i, 7); がいいです。

と、ここまで書いてふと思ったんですが、
VC++6.0って、CTabViewクラスありましたっけ?

kkawa 2010/02/10(水) 11:22:37
bunさん ありがとうございます。
MSFlexGridのコードはまだ調べていないので何が使えるか
やって見ようと思います。以前使っていたListctrlは10000データ位
のアレーでテストしていましたのでGridの移植が出来上がったらご指摘の
部分の時間を比較計測してみようと思います。
TabViewは以下のところから流用させてもらってます。中身がその名のとおりの
クラスなんで、あとはCViewで作ってからこのTabViewに入れ替えて
使ってます。

http://software.nikkeibp.co.jp/software/backno/03vcmook2.html
「タブコントロールを活用する」

何か他人様のコードを貼り付けただけのスケルトンなんですが
まあ、SDIでFlexGridも動くかな という事で納得です。
今後ともよろしくお願い致します。

kkawa 2010/02/16(火) 00:33:43
MSFlexGridについて、いろいろWebで調べていたのですが私の知識では
理解不能 ~_~):なので、質問させてください。
MFCから作成された?CMSFlexGridのラップクラスなるものの内、
InvokeHelperの第1引数の0x??のDISPIDが解ればなにかいろいろできそうな感じ
がしますが、これって何なんでしょう?
MSFlexGridのV_Tableみたいなのから、引いてきたIDなんでしょうか?
MSFlexGridのCOMなんかで生成してるんでしょうか?
どのような経路でサーバにたどりつくのか概要だけでもご教授いただければ
助かります。

bun 2010/02/16(火) 07:30:51
詳細な仕組みとなると、とても掲示板で説明しきれるものではありません。
以下をご参照下さい。
http://eternalwindows.jp/#com

簡単に言うと、FlexGridがどこにあろうと、インストール時にレジストリ
に自分の所在パスやプロパティやメソッドなどを記録するわけです。
(regsvr32.exeでネット検索すると山ほど出てきます)
それによって、IIDやDISPID等とFlexGridの実装を関連づけるわけです。

そして、VC++から使用する際は、それらのIIDやDISPIDを用いたラップク
ラスをはき出すわけです。

重要なのは、上記、レジストリ登録、ラップクラス生成を行うための機構
をFlexGridのDLL自身が持っていることにあります。

そのため、FlexGridのDLLがどのフォルダにインストールされようと、環
境に依存せずに利用できるわけです。
また、実装ファイル(C++開発ならCPP,Hんどのファイル)を完全隠蔽する
こともできているわけです。

subaru 2010/02/16(火) 10:29:01
少し補足するとDLL/OCXのパスとタイプライブラリの所在はレジストリに記録されますが
プロパティやメソッドなどについてはタイプライブラリの方に記録されていて、
DISPIDの定義をOLE/COM オブジェクトビューア(oleview.exe)で確認することができます。
#タイプライブラリはDLLのリソースとして含まれていることが多いです。

ちなみにXP以降ではDLL/OCXをローカルに配置してexeのマニフェストリソースに
comClass要素を書いておくことでもインスタンスの作成ができるようになっているので、
必ずしもregsvr32.exeでレジストリに公開しなくてもよくなっています。

古いスレッドには追加発言できません。