VC++6.0 MFC OSwindows2000 です。
クラスの初期化InitInstance()で最初に作成したダイアログではなく
別のダイアログを表示できるように表示順番を変更しました。
最初に作成したダイアログにはリストビューの詳細設定があります。
ダイアログの表示順番を変更する以前はリストビューの項目ボタンを押すと
ソート処理を行うプログラムが正常に行われていました。
しかしダイアログ順番変更後、リストビューの項目ボタンを押すとエラーになります。
ソースを追っていくと
//メインウィンドウのポインタを取得する
CVclsvwDlg* pDlg = (CVclsvwDlg*)AfxGetMainWnd();
//比較される2つのアイテム
CString str1 = pDlg->m_listctrl.GetItemText(param1,0);←ここでエラー
CString str2 = pDlg->m_listctrl.GetItemText(param2,0);
サブウィンドウのポインタを取得できれば解決できると思います。
よろしくお願いします
処理してる場所がよくわからないですが、記載されてるコードからCALLBACK 内だと想像しま
す。
であれば、param1 とparam2 がどのような値を取っているか確認してみてください。
たしか、SetItemData でセットした値になっていたと思います。
# どこかでSetItemData してアイテムを識別できるようにする必要があります。
# セットしてなければ、ソートは出来ないはず・・・
param1 とpalam2 で与えられたデータよりリスト上のアイテムを識別し、
その上で必要な値を取得するよう記述する必要があります。
# たとえば、リスト上のindex と同じ値でSetItemData しておくとかね
# エラーであればそのエラー内容をなるべく省略せずに記載した方が良いかと思います。
# また、コードの省略も必要以上に省略してしまうと何をしたいのかわかりかねますので、
# こちらも最小限に抑えた方がいいかと思います。
あ・・・
全然関係なかったっぽいかも・・・
> クラスの初期化InitInstance()で最初に作成したダイアログではなく
> 別のダイアログを表示できるように表示順番を変更しました。
ここで、m_pMainWnd はどのウィンドウのポインタを保持させていますか?
AfxGetMainWnd はこのm_pMainWnd を返しますので、
この値が適切でなければ上記のコードではどうなるかわかりません。
# おそらく、違うウィンドウを参照してるのだと・・・
# これも違うかな?(^^;
あいるさん申し訳ありません。説明不足でした。
処理している場所はCALLBACK関数内です。
int CALLBACK CVclsvwDlg::CompareFunc(LPARAM param1,LPARAM param2,LPARAM param3)
{
CVclsvwDlg* pDlg = (CVclsvwDlg*)AfxGetMainWnd();
//比較される2つのアイテム
CString str1 = pDlg->m_listctrl.GetItemText(param1,0);
CString str2 = pDlg->m_listctrl.GetItemText(param2,0);
int iReturn;//判定引数
if(!param3)
{
iReturn = strcmp(str1,str2);//昇順
}
else
{
iReturn = strcmp(str2,str1);//降順
}
return iReturn;
}
原因
クラスの初期化InitInstance()で最初に作成したダイアログではなく
別のダイアログを表示できるように表示順番を変更しました。
最初に作成したダイアログにはリストビューの詳細設定があります。
ダイアログの表示順番を変更する以前はリストビューの項目ボタンを押すと
ソート処理を行うプログラムが正常に行われていました。
原因は
CVclsvwDlg* pDlg = (CVclsvwDlg*)AfxGetMainWnd();
メインダイアログのポインタが入るためにエラーになる
AfxGetMainWnd()の個所をAfxGetThread()やAfxGetApp()に変更して
サブダイアログのポインタを取得を試みたのですが、
強制終了になってしまします。
ちなみに本来のダイアログをメインウィンドウに設定するとうまくソートされます。
んっとですね・・・
SortItems の第二引数に CListCtrl のアドレスいれておくと、
param3 にその値が入ってますので・・・
-----
CListCtrl* pListCtrl = static_cast<CListCtrl*>(param3);
CString str1 = pListCtrl->GetItemText(param1,0);
CString str2 = pListCtrl->GetItemText(param2,0);
-----
で、いいかも?
# 詳しくは、MSDN のCListCtrl::SortItems を参照してください。
て、param3 に昇順/降順を持たせてるのですね・・・(^^;
ん~・・・だとしたら・・・
Appクラスのメンバーに取得したいダイアログのポインタを保持して、
それを参照するのが一番簡単かも・・・??
# 何をして強制終了になったのかは気になりますが・・・(^^;
ちなみに、私なら・・・ですが、
ClistCtrl のポインタと、昇順/降順のフラグを持たせた構造体を定義して
値をセットした構造体をSortItems の第二引数に渡してCALLBACK 内で利用するかな(^^;
# 好みによりますけどね・・・(^^;
param3を構造体のポインタにすると言う手もあるかな。
ソート条件とリストコントロールのポインタが入った構造体を宣言しておいて
この構造体の変数を作ってそこに必要な情報を設定し、変数のアドレスを
引き渡す。受け取った方はparam3を構造体のポインタに読み替えて処理する。
構造体を使用すれば、一つのアドレスで複数のデータを扱えるので
どうにでもなると思います。
コールバック内はなるべく引き渡された引数の中で完結するようにしないと
今回のように使う側の環境が変わると使えなくなったりするので
そこまで考えて設計する事が必要だと思います。
かぶった。(T-T)
1 CListCtrl* pListCtrl = static_cast<CListCtrl*>(param3);
2 CString str1 = pListCtrl->GetItemText(param1,0);
3 CString str2 = pListCtrl->GetItemText(param2,0);
1だとキャストがうまくいかなかったので
CListCtrl* m_listctrl = reinterpret_cast<CListCtrl*>(param3);
//比較される2つのアイテム
CString str1 = m_listctrl->GetItemText(param1,0);//失敗
CString str2 = m_listctrl->GetItemText(param2,0);//失敗
に変更してみました。コンパイルは通りましたが、
ハンドルが取得できなかったので他の方法を探ってみます。
> 1 CListCtrl* pListCtrl = static_cast<CListCtrl*>(param3);
> 1だとキャストがうまくいかなかったので
> CListCtrl* m_listctrl = reinterpret_cast<CListCtrl*>(param3);
きゃ~恥ずかしいミスしちゃった・・・orz
訂正ありがとです(^^;
> かぶった。(T-T)
考えることは同じなんですな~w
# 常套句って話もなきにしもあらず・・・(^^;
えーと、SortItemsの第二引数にリストコントロールのポインタを渡していますか?
渡していないと受け取れないはずです。
私的にはクラスの独立性というのは再利用の時や構成を変えたい時に
影響が出るので予めクラス間の結合度は弱く設定するようにしています。
今回の話で言えば、ソート対象のリストコントロールを持つウインドウが
メインウインドウではなくなる可能性を考慮に入れていなかったわけです。
ウインドウの構成が変わってもうまく行くようにする為には、
出来る限りそのクラス内で解決できるような作りにした方が良いと思います。
PATIOさんあいるさんアドバイスありがとうございました。
無事解決しました。
結局SortItemsの第2引数にClistCtrlのアドレスを渡して解決しました。
sortItemsの引数は2つしか使えないので
tempDataとbSortは外部で定義してしまいました。
構造体にすればよかったんですが…。
void CVclsvwDlg::OnColumnclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
ColumnSelectNo = pNMListView->iSubItem;//カラム選択NOを取得する
tempData = ColumnSelectNo;
RenumberItem();
CListCtrl* m_list = &m_listctrl;
m_listctrl.SortItems(CompareFunc,(LPARAM)m_list);
if(bSort == FALSE)
{
bSort = TRUE;
}
else
{
bSort = FALSE;
}
*pResult = 0;
}
int CALLBACK CVclsvwDlg::CompareFunc(LPARAM param1,LPARAM param2,LPARAM param3)
{
int n=0,i;
int str1_no=0,str2_no=0;
char strp1[8];
char strp2[8];
CListCtrl* pListCtrl = (CListCtrl*) param3;
//比較される2つのアイテム
CString str1 = pListCtrl->GetItemText(param1,tempData);//
CString str2 = pListCtrl->GetItemText(param2,tempData);//
if(tempData==1)
{
int iReturn;//判定引数
if(!bSort)
{
iReturn = strcmp(str1,str2);//昇順
}
else
{
iReturn = strcmp(str2,str1);//降順
}
return iReturn;
}
else
{
n=str1.GetLength();
for(i=0;i<n;i++)
{
strp1[i]=str1.GetAt(i);
}
strp1[i]='\0';
n=str2.GetLength();
for(i=0;i<n;i++)
{
strp2[i]=str2.GetAt(i);
}
strp2[i]='\0';
str1_no = atoi(strp1);//文字列をint型にキャスト
str2_no = atoi(strp2);//文字列をint型にキャスト
int iReturn;//判定引数
if(!bSort)
//昇順
if(str1_no==str2_no)
{
iReturn=0;
}
else if(str1_no>str2_no)
{
iReturn=1;
}
else//str1_no<str2_no
{
iReturn=-1;
}
}
else
{
//降順
if(str2_no==str1_no)
{
iReturn=0;
}
else if(str2_no>str1_no)
{
iReturn=1;
}
else//str2_no<str1_no
{
iReturn=-1;
}
}
return iReturn;
}
}
void CVclsvwDlg::RenumberItem()
{
LV_ITEM IvItem;
int i;
for(i=0;i<m_listctrl.GetItemCount();i++)
{
IvItem.iItem = i;
IvItem.iSubItem = 0;
IvItem.mask = LVIF_PARAM;
IvItem.lParam = i;
m_listctrl.SetItem(&IvItem);
}
}
解決になっていますが、
C++を使ってオブジェクト指向で組むのであれば、
グローバル変数に出すより構造体として定義して
クラス内に押し込めた方がらしかったかなと思います。
特別難しいテクニックを使うわけでもありませんし。
例えば、
typedef struct _SORT_ITEM_DATA
{
CListCtrl* pListCtrl;
BOOL blSortFlag;
int iSubItem;
} SORT_ITEN_DATA;
void CVclsvwDlg::OnColumnclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
SORT_ITEM_DATA sortData;
sortData.iSubItem = pNMListView->iSubItem;//カラム選択NOを取得する
sortData.pListCtrl = &m_listctrl;
sortData.blSortFlag = m_bSort;
m_listctrl.SortItems(CompareFunc, reinterpret_cast<LPARAM>(&sortData));
if(m_bSort == FALSE) // メンバー変数にする。
{
m_bSort = TRUE;
}
else
{
m_bSort = FALSE;
}
*pResult = 0;
}
int CALLBACK CVclsvwDlg::CompareFunc(LPARAM param1,LPARAM param2,LPARAM param3)
{
int n=0,i;
int str1_no=0,str2_no=0;
int index1, index2;
char strp1[8];
char strp2[8];
SORT_ITEM_DATA* pSortItem = reinterpret_cast<SORT_ITEM_DATA*>(param3);
LVFINDINFO info;
// アイテムに割り振られたlparamからアイテム番号を取得
memset(&info, 0x00, sizeof(info));
info.flags = LVFI_PARAM;
info.lParam = param1;
index1 = pSortItem->pListCtrl->FindItem(&info);
info.lParam = param2;
index2 = pSortItem->pListCtrl->FindItem(&info);
//比較される2つのアイテム
CString str1 = pListCtrl->GetItemText(index1, pSortItem->iSubItem);
CString str2 = pListCtrl->GetItemText(index2, pSortItem->iSubItem);
if(pSortData->iSubItem == 1)
{
int iReturn;//判定引数
if(!pSortData->blSortFlag)
{
iReturn = strcmp(str1,str2);//昇順
}
else
{
iReturn = strcmp(str2,str1);//降順
}
return iReturn;
}
else
{
n=str1.GetLength();
for(i=0;i<n;i++)
{
strp1[i]=str1.GetAt(i);
}
strp1[i]='\0';
n=str2.GetLength();
for(i=0;i<n;i++)
{
strp2[i]=str2.GetAt(i);
}
strp2[i]='\0';
str1_no = atoi(strp1);//文字列をint型にキャスト <-キャストではなく変換
str2_no = atoi(strp2);//文字列をint型にキャスト <-キャストではなく変換
int iReturn;//判定引数
if(!pSortData->blSortFlag)
//昇順
if(str1_no==str2_no)
{
iReturn=0;
}
else if(str1_no>str2_no)
{
iReturn=1;
}
else//str1_no<str2_no
{
iReturn=-1;
}
}
else
{
//降順
if(str2_no==str1_no)
{
iReturn=0;
}
else if(str2_no>str1_no)
{
iReturn=1;
}
else//str2_no<str1_no
{
iReturn=-1;
}
}
return iReturn;
}
}
コンパイル等の検証は行っていないので細かい部分は御自分でご確認ください。
参考程度と言うことで。
因みにC++で組むならstatic_cast等のC++のキャストを使った方が
私はよいと思います。
それぞれのキャストには意味がありますし、
意識してそのキャストを使っていると言う提示にもなります。
C言語スタイルのキャストの場合、どのキャストも一緒くたなので
キャストした時の意図が伝わりにくいです。
後でキャストをした所の検索をする時も重宝します。