VS 2005, C++, MFC, SDI アプリです。
呼び出す子ダイアログにリストビューを設け、レポートスタイルで一覧表を表示しています。
各列のコラムをクリックしてソートするようにしたいのです。
ソートについて、ここを参考にして作りました。
http://homepage3.nifty.com/mitui707/VisualC/VClistview_sort.html/
ビルドはエラーなく出来たのですが、実行してコラムをクリックすると、
次のデバッグメッセージが出て終了します。
”Debug Assetion Failed!
・・・・・\VC\altmfc\src\mfc\winctrl2.cpp
Line:544”
ちなみに、このファイルの544行には「ASSERT(::IsWindow(m_hWnd));」と書かれています。
動作エラーになるのは、前記サンプルでいうと、CALLBACK関数内の次の箇所です。
CListview_sortView* pDlg = (CListview_sortView*)AfxGetMainWnd();
CString str1 = pDlg->m_list1.GetItemText((int)param1, sCol);
リストコントロールをゲット出来ていない感じがします。
前記のサンプルは、CFormViewベースアプリの親自体にリストビューを設けた場合のようで、この通り作ったら勿論OKでした。
子ダイアログの場合、どういう変更をしたらいいか、教えてください。
どなたか、よろしくお願いいたします。
>AfxGetMainWnd
が何を指しているのかヘルプで確認してみてはどうでしょうか?
単にリストビューの載っているダイアログを取得するならば、
this->GetParent(); // 親ウィンドウの取得
とかではないでしょうか?
Blue 様、早速のご教示、有り難うございます。
しかし、this->GetParent(); でビルドするとエラーとなり、
「静的なメンバ関数は 'this' ポインタをアクセスすることができません。」とメッセージが出ます。
前記のサンプルにも、次の記述があります。
「//■ staticメンバ関数なので、GetParent()で親ウィンドウを取得することはできない」
自分はまだ、static や CALLBACK の取り扱いが未熟なようで、このフォーラムにすがろうと考えた次第です。済みません。
子から親を意識するようなつくりはあまり好ましくないです。
データ部分とコントロール部分を分けて設計すべきです。
とりあえず、アプリケーションクラス(CXXXApp)にでも親となるウィンドウの
ポインタを覚えさせておいて、それを参照するとか。
ありがとうございます。
ご回答を戴くまでの間にネット上を検索していたら、全く同じ問題にぶつかった人の記事を見付けました。
http://forums.belution.com/ja/vc/000/196/07s.shtml
このページの最後のところに、「コールバック関数に、引数で渡すようにしたらできました。」と書いてありました。
そうか! と思い、やってみようと思いましたが、コールバック関数に引数で渡すやりかたが判りません。(苦笑)
static int CALLBACK CompareFunc(LPARAM param1, LPARAM param2, LPARAM param3, CDialog* pDlg); ←違うようです。
ご教授にある「親となるウィンドウのポインタを覚えさせておいて、それを参照する」。このやり方も出来るかどうか。 m(_ _)m
CListCtrl::SortItems
のヘルプを見るとわかると思いますが、2番目の引数で
「比較関数に渡されるアプリケーション定義の値。」
を指定できます。
よって一番最初のサイトの
>m_list1.SortItems(CompareFunc, bSort);
を変更します。
おそらく
m_list1.SortItems(CompareFunc, reinterpret_cast<DWORD>this);
となるでしょう。
そして、CompareFuncの三番目の引数でSortItemsの二番目の引数がわたってくるので、
CXXXDlg* pDlg = reinterpret_cast<CXXXDlg*>(param3);
とすることで取得できます。
http://www.paw.hi-ho.ne.jp/ynagata/softknowhow/cpp/cppdoc6.htm
が参考になるでしょう。
>m_list1.SortItems(CompareFunc, reinterpret_cast<DWORD>this);
括弧が抜けていました。
m_list1.SortItems(CompareFunc, reinterpret_cast<DWORD>(this));
>とりあえず、アプリケーションクラス(CXXXApp)にでも親となるウィンドウの
>ポインタを覚えさせておいて、それを参照するとか。
は単に CXXXApp クラスにメンバ変数として CXXXDlg*型の値を用意して
どこかで
static_cast<CXXXApp*>(AfeGetApp())->m_pXXDlg = this;
と入れておくって方法です。
まぁ、SortItemsの2番目の引数を使うのが一般的でしょうけど。
勉強させて戴きました。
出来ました!
SortItemsの2番目の引数にリストコントロールのポインタを入れました。
昇順・降順の bSort はグローバル変数で定義しました。
void CDlg1::OnLvnColumnclickList1(NMHDR *pNMHDR, LRESULT *pResult)
{
・・・
CListCtrl* pLC = &m_list1;
m_list1.SortItems(CompareFunc, (LPARAM)pLC);
if (bSort == FALSE) bSort = TRUE;
else bSort = FALSE;
}
int CALLBACK CDlg1::CompareFunc(LPARAM param1, LPARAM param2, LPARAM param3)
{
CListCtrl* pLC = (CListCtrl*) param3;
CString str1 = pLC->GetItemText((int)param1, sCol);
CString str2 = pLC->GetItemText((int)param2, sCol);
int iReturn;
if(!bSort) iReturn = wcscmp(str1, str2);
else iReturn = wcscmp(str2, str1);
return iReturn;
}
これでバッチリです。
ご親切にご教授、ありがとうございました。
>昇順・降順の bSort はグローバル変数で定義しました。
だったら、親ウィンドウのメンバ変数に指定しまえばどうでしょうか?
また
> if (bSort == FALSE) bSort = TRUE;
> else bSort = FALSE;
なら単に
bSort = !bSort;
でよさげ。
訂正
>だったら、親ウィンドウのメンバ変数に指定しまえばどうでしょうか?
はパラメタでthisを指定するときはでしたね。
ついでに
> int iReturn;
> if(!bSort) iReturn = wcscmp(str1, str2);
> else iReturn = wcscmp(str2, str1);
も、変数使わないで
if (!bSort) return wcscmp(str1, str2);
return wcscmp(str2, str1);
でもいいですね。
というか、CString の変数を対称にしているのであれば、
> wcscmp
ではなく、_tcscmpもしくはCString::Compareを使うべきでしょう。
有り難うございます。
CString::Compare を使うことにします。
_tcscmp 系統は、UNICODE とかマルチバイトとかで使い分けが厄介に思います。
ツイート | ![]() |