- ベストアンサー
リストコントロールのデータの変更を検知したい
- リストコントロールのデータの変更を検知する方法について教えてください。また、OnLvnItemchangedList()が正しいイベントかどうかも知りたいです。
- 削除の場合はOnLvnDeleteitemList()を使用すれば良いですが、追加の場合にはどのように対応すれば良いでしょうか。pNMLV->uChanged == 4 でも問題ないですか?
- リスト上のデータが変更されるケースについても教えてください。また、PreTranslateMessage()でリストデータの変化を検知することは可能でしょうか?
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
>BOOL CFileListCreatorDlg::PreTranslateMessage(MSG* pMsg) >のMSG* pMsgとあったので・・・参考サイトを元にに「pMsg->」を追加したのですが、うんともすんともいわないです。 そのダイアログはどのように表示しています? モードレスダイアログでしたらPreTranslateMessage()は有効ですが、 モーダルダイアログの婆とは動作しないっぽいですが。 http://support.microsoft.com/kb/126874/ja # 機械翻訳らしく読みにくいですけどね。 >OnLvnItemchangingListでもOnLvnItemchangedListでもpNMLV->iSubItem==0なので、 >正しく書いたとしても、意味がない気もしますが、もしお時間があればお教え下さい。 前回指摘されたように…カラムクリック時用のメンバかと思われます。 ので、今回の目的ではiSubItemは使えない…ということになるかと。
その他の回答 (4)
- Wr5
- ベストアンサー率53% (2173/4061)
>case LVN_COLUMNCLICK: >で、pNMLV->iSubItemを取得できるみたいですが、それでソートするのばかり、 ああ、そっち用のかも知れないですな。 アイテムを追加したときはLVN_INSERTITEMで、削除したときはLVN_DELETEITEMで取れますが… サブアイテムとかのテキストの変更は通知がなさそうですね。 MFCなのでしたら…… CListCtrlを継承したクラスを作って、SetItemText()に細工を入れる…というのはどうでしょう? もっとも、テキストの変更がCListCtrl::SetItemText()をコールして行っている場合に限りますが。
補足
ちょっと書き忘れて、繰り返しになってしまうのですが・・・ BOOL CFileListCreatorDlg::PreTranslateMessage(MSG* pMsg) のMSG* pMsgとあったので・・・参考サイトを元にに「pMsg->」を追加したのですが、うんともすんともいわないです。 switch (pMsg->message) { //←先程は書き忘れていました。 case WM_NOTIFY://http://gurigumi.s349.xrea.com/programming/visualcpp/sdk_dialog_listview3.html switch(((LPNMHDR)pMsg->lParam)->idFrom).//※pMsg-> { case IDC_LIST: //IDC_LIST←これは間違っていないと思うのですが・・・・ switch (((LPNMLISTVIEW)pMsg->lParam)->hdr.code) //※pMsg-> { case LVN_ITEMCHANGED: LPNMLISTVIEW pNMLV; pNMLV = (LPNMLISTVIEW) pMsg->lParam; //pMsg->lParamがうまく取得できてない?? MessageBox(_T("ここすら通ってないです。")); //if (pNMLV->hdr.code == LVN_ITEMCHANGED){ if (pNMLV->iSubItem == 2 || pNMLV->iSubItem == 3 || pNMLV->iSubItem == 8){ MessageBox(_T("勿論ここも")); } //} break; } } break; OnLvnItemchangingListでもOnLvnItemchangedListでもpNMLV->iSubItem==0なので、 正しく書いたとしても、意味がない気もしますが、もしお時間があればお教え下さい。 カラム10を作って、時刻を挿入して、現在時刻と比べて、一つでも更新時刻が新しいのなら、 編集したとみなしますか・・・余り時間がかかるなら、毎回保存するようにします。 あともう一歩の気がするのですが、まさか、lvi.mask = LVIF_TEXT| LVCF_SUBITEM;をどこかに書かなければいけないのでしょうか、ListInsertItem()の中に書いてみましたが、pNMLV とどこで関連付けるのかが分かりません。 また、以下のLVS_EX_FULLROWSELECT取ってみてもpNMLV->iSubItem==0でしたυ m_xcList.SetExtendedStyle( LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); //LVS_EX_CHECKBOXES | //コメント化 2012.05.19 ちなみに、以前質問をして、 http://oshiete.goo.ne.jp/qa/6734007.html で、オーバーロードはうまくいったのですが。。。 (ずっと質問に付き合っていただいてすみません。)
- Wr5
- ベストアンサー率53% (2173/4061)
>XP対策で、RedrawWindow();を結構使っているのですが、 >データに変化がなくても、RedrawWindow();を行うと >OnLvnItemchangedList()イベントが発生するのでしょうか? ユーザー操作であろうと、プログラム中での変更であろうと通知されたかと。 # ListView_SetCheckState()でチェックボックスの状態を変更したモノは…通知されてますね。 >ファイル名と備考欄が変更された時のみ、 >つまり、データ変更検知対象をカラムで指定できると助かるのですが・・・ LVIS_FOCUSEDとLVIS_SELECTEDの変動は無視してしまえば良いのではないですか? pNMLV->iSubItemにカラム番号とか入っていたりしませんか? >( pNMLV->uChanged == LVIF_STATE && pNMLV->uNewState == 4096 && pNMLV->uOldState == 0 ) >の4096とは何を表わしているのでしょうか? LVIS_STATEIMAGEMASKで調べるコトをお勧めします。 # LVIS_OVERLAYMASKなんてのもありますのでご注意を。 というか… ># チェックボックスとかつけていたり、設定しているイメージリストによっては他のところも変わりますので、ビット演算で判定して下さいな。 と回答していたりするのですが……LVS_EX_CHECKBOXES付いていたりしませんか? >(!LVIS_FOCUSED | !LVIS_SELECTED) > と >!(LVIS_FOCUSED | LVIS_SELECTED) > はとっちが正しい書き方なのでしょうか? 私なら後者で書きますかね。 >リスト上のあるセルの値 と 同じ値 を(同セルに)上書きした場合、チェンジイベントは発生しますか? 変更内容までは関知していなかった…と思いますのでおそらく通知されるでしょう。 # 実際のところはSpy++などで確認を。
補足
色々教えていただいて有難うございます。 >LVIS_FOCUSEDとLVIS_SELECTEDの変動は無視してしまえば良いのではないですか? そうですね、その方向で行きたいと思います。 >pNMLV->iSubItemにカラム番号とか入っていたりしませんか? これ、聞きたいです! 私もpNMLV->iSubItemが、使えると思って、 TRACEしてみたのですが、常に0で、インターネットで調べると・・・ case LVN_COLUMNCLICK: で、pNMLV->iSubItemを取得できるみたいですが、それでソートするのばかり、 ヒットしますし、私の考えでは、コラムのキャプション(項目名が書いてある部分)を クリックしたときしか検知できないという風に思いました。 もし、pNMLV->iSubItemを指定できるのなら、 void CFileListCreatorDlg::OnLvnItemchangedList(NMHDR *pNMHDR, LRESULT *pResult){}内で・・・ if (pNMLV->iSubItem == 2 || pNMLV->iSubItem == 3 || pNMLV->iSubItem == 8){ //0:ファイル重複識別ナンバー 1:通し番号 2:フルパス 3:ファイル名 4:おおよそのデータサイズ 5:データサイズ 6:修正日 7:修正時間 8:備考欄 9:書式情報 ・・・でカラムを指定しているつもりなのですが、できませんでした。 なにか他にも記述すべきなのでしょうか? 以下も試してみました。 BOOL CFileListCreatorDlg::PreTranslateMessage(MSG* pMsg) {}内で・・・ クリックではなく、データ変更通知を受け取りたいですυ case WM_NOTIFY: //http://gurigumi.s349.xrea.com/programming/visualcpp/sdk_dialog_listview3.html switch(((LPNMHDR)pMsg->lParam)->idFrom) { case IDC_LIST: switch (((LPNMLISTVIEW)pMsg->lParam)->hdr.code) { case LVN_ITEMCHANGED: LPNMLISTVIEW pNMLV; pNMLV = (LPNMLISTVIEW) pMsg->lParam; //pMsg->lParamがうまく取得できてない?? //if (pNMLV->hdr.code == LVN_ITEMCHANGED){ if (pNMLV->iSubItem == 2 || pNMLV->iSubItem == 3 || pNMLV->iSubItem == 8){ MessageBox(_T("1111")); } //} break; } } break; >LVIS_STATEIMAGEMASKで調べるコトをお勧めします。 ># LVIS_OVERLAYMASKなんてのもありますのでご注意を。 >というか… >># チェックボックスとかつけていたり、設定しているイメージリストによっては他のところも変わりますので、ビット演算で判定して下さいな。 と回答していたりするのですが……LVS_EX_CHECKBOXES付いていたりしませんか? m_xcList.SetExtendedStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); あ!、使ってます。チェックボックスは画面に表示されていませんが、 プロジェクト全体を検索したら、ずっと前の残骸があり、驚きました。 (同じ事を聞いてすみません。削除します) void CFileListCreatorDlg::OnNMClickList(NMHDR *pNMHDR, LRESULT *pResult){}内で、 最後にクリックした行とカラムは取得できているのですが、関係ないですよね。
- Wr5
- ベストアンサー率53% (2173/4061)
>lParamが何を意味しているか?分からないです。教えて下さい。 リストコントロールにアイテムを追加した時に設定した値…ですね。 MFCだとどのように扱われるのか不明ですが。 http://msdn.microsoft.com/ja-jp/library/8b9s12fc.aspx で、maskにLVIF_PARAMが指定してある必要はありますが。 どの形式でアイテムの追加を行ったか…が問題でしょうかね。 # 特に指定していないのであればLVIF_PARAMは付与されていないでしょうからlParamにも値が設定されていない(0が設定されているというべきか)かと。 ちなみに私だと…リストコントロールに追加した情報(たいてい構造体)へのポインタを設定していたりします。 カラムクリックでソートとかする場合にデータ自体へのアクセスが容易になるので。 もちろん32Bitの値であればなんでも設定できます。 プロジェクトがx86の場合ですが。
お礼
お礼欄に失礼します。 どうやら、LVIS_SELECTEDで判断するのは、無理っぽいです。 例えば、「検索」機能でも一旦・・・ m_Dlg->m_xcList.SetItemState(m_Dlg->FindIDX, // 非フォーカス&非選択状態にしたいアイテムのインデックス !LVIS_FOCUSED | !LVIS_SELECTED, // 状態 LVIS_FOCUSED | LVIS_SELECTED); // マスク ・・・にして、 m_Dlg->m_xcList.SetItemState(index, // フォーカス&選択状態にしたいアイテムのインデックス LVIS_FOCUSED | LVIS_SELECTED, // 状態 LVIS_FOCUSED | LVIS_SELECTED); // マスク で、一個一個選択しながら、進んで末尾までいったら、終了します。故意に選択状態と非選択状態に しているので、そこで(リスト上のデータに変化はないのですがυ)検出してしまいます。 例えば、(↓)では・・・ if ( ( pNMLV->uChanged == LVIF_PARAM && pNMLV->uNewState == 0 && pNMLV->uOldState == 0) || ( pNMLV->uChanged == LVIF_STATE && pNMLV->uNewState == 4096 && pNMLV->uOldState == 0 ) || ( pNMLV->uChanged == LVIF_STATE && pNMLV->uNewState == 0 && pNMLV->uOldState == (LVIS_FOCUSED | LVIS_SELECTED) )){ ListDataNoChange_FLG = FALSE;//追加 2012.05.13 CFileListCreatorDlg* m_Dlg = (CFileListCreatorDlg*)AfxGetMainWnd(); if( (m_Dlg==NULL)||(m_Dlg->GetSafeHwnd()==NULL) ){ return; } m_Dlg->SetWindowText(_T("FileListCreator (*)")); } ・・・とすると、FINDの2個目で (*) が付いてしまいます。 REPLACEしないFINDは、(*)がついて欲しくないです。 XP対策で、RedrawWindow();を結構使っているのですが、 データに変化がなくても、RedrawWindow();を行うと OnLvnItemchangedList()イベントが発生するのでしょうか? ファイル名と備考欄が変更された時のみ、 つまり、データ変更検知対象をカラムで指定できると助かるのですが・・・ ( pNMLV->uChanged == LVIF_STATE && pNMLV->uNewState == 4096 && pNMLV->uOldState == 0 ) の4096とは何を表わしているのでしょうか? (!LVIS_FOCUSED | !LVIS_SELECTED) と !(LVIS_FOCUSED | LVIS_SELECTED) はとっちが正しい書き方なのでしょうか? リスト上のあるセルの値 と 同じ値 を(同セルに)上書きした場合、チェンジイベントは発生しますか? //0:ファイル重複識別ナンバー 1:通し番号 2:フルパス 3:ファイル名 4:おおよそのデータサイズ 5:データサイズ 6:修正日 7:修正時間 8:備考欄 9:書式情報 パラメーターの書式情報は、文字として、カラム9に退避しているので、リストコントロールの文字が変化したことさえ分かればいいです。なにか、もっと簡単な方法はあるでしょうか? 選択状態やフォーカスは自分で変えないほうがいいのでしょうか。。。 これでは、各機能に、編集中フラグを一個一個立てたほうが早いです。 本当は、自分で検証するべき物もありますが、質問ばかりですみません、一部でも教えてくださると嬉しいです。
補足
色々試行錯誤してて、時間がかかってすみません。 書式情報を数値にしていれています。その数値がlParam:に出て・・・ CFileListCreatorDlg::m_xcList.SetItemData(index,FormatDataNum); この数字が出ているようです。 もう少し、粘ってみます。
- Wr5
- ベストアンサー率53% (2173/4061)
>OnLvnItemchangedList()であっているのでしょうか? 変更が完了した時に通知されるもの…ですね。 変更があった時だとLVN_ITEMCHANGING(MFCだとOnLvmItemChanging~()かな)ですね。 >あとは、pNMLV->uChanged などの 4とか8とか、定数で記述したいです。 定数というか…マジックナンバーなのはどうかと……4、8とかの値だけでどういう意味か理解はできないでしょう? # そういうのをマジックナンバーと言います。可読性を著しく低下させます。 http://www.geocities.co.jp/SiliconValley-PaloAlto/6827/Programing/NMLISTVIEW.htm とか、MSDNの日本語でのページとか……探せばあるかと思いますけど…。 #define LVIF_TEXT 0x00000001 #define LVIF_IMAGE 0x00000002 #define LVIF_PARAM 0x00000004 #define LVIF_STATE 0x00000008 とかになっていますね。 変更に関してはLVN_ITEMCHANGEDで通知されますので、pNMLV->uOldStateやpNMLV->uNewStateを参照して、何が変更されたのか……を判定して、処理することになるでしょう。 入る値は… #define LVIS_FOCUSED 0x0001 #define LVIS_SELECTED 0x0002 等になります。 # チェックボックスとかつけていたり、設定しているイメージリストによっては他のところも変わりますので、ビット演算で判定して下さいな。
補足
OnLvnItemchangingList() もありました。 でも、OnLvnItemchangingList()の後に必ずOnLvnItemchangedList()が来るのではと 思いました。 TRACE(↓)してみましたが、OnLvnItemchangingList()内でも、 リストに行を追加、置換、選択されている行を置換、すべてを置換において・・・ if (( pNMLV->uChanged == 4 && pNMLV->uNewState == 0 && pNMLV->uOldState == 0 )) 必ずTRUEになります。ただ、クリックしただけでも、また、データが変わらなくても該当してしまいます。 クリックした時、FALSEにしたいんですが・・・難しいです。 書式情報を数値にしていれています。その数値がlParam:に出ています。 if (FormatDataStr == _T("NORMAL,BLACK,")){ FormatDataNum = 11; } if (FormatDataStr == _T("BOLD,BLACK,")){ FormatDataNum = 12; } if (FormatDataStr == _T("NORMAL,RED,")){ FormatDataNum = 21; } if (FormatDataStr == _T("BOLD,RED,")){ FormatDataNum = 22; } CFileListCreatorDlg::m_xcList.SetItemData(index,FormatDataNum); ・・・という風にです。 マジックナンバーというんですか、初めて聞きました、有難うございますm(_ _)m 自分は、#define で定義した(数値と対応させた)定数といいたかったです。 LVIS_SELECTEDとかの方は、なんというか知りたかったです。 #define LVIS_FOCUSED 0x0001 #define LVIS_SELECTED 0x0002 #define LVIS_CUT 0x0004 #define LVIS_DROPHILITED 0x0008 #define LVIS_GLOW 0x0010 #define LVIS_ACTIVATING 0x0020 #define LVIS_OVERLAYMASK 0x0F00 #define LVIS_STATEIMAGEMASK 0xF000 ビット演算で判定 ・・・という事は、 pNMLV->u○○○State & LVIS_FOCUSED && pNMLV->u○○○State & LVIS_SELECTED フォーカスがあり、かつ、選択状態にあるという意味がu○○○Stateが3という意味ですね。 LVIS_□□□□ (ごめんなさい、自分が一番初めに載せたホームページにも少し載ってました。) ・・・で少し、イメージが付きやすくなりました。 フォーカスと選択状態を処理対象行に移すようにコーディングする方向で行こうと思いますが、 まちがった方向に行こうとしているなら、アドバイス下さい。 (ただ、たまたま、これから置換しようとする行にフォーカスや選択状態がある場合は、データが 変わったことを(行を)検出する事は出来ない気が・・・) マジックナンバーのままで、すみません、うまく動いたら直します。 ========================================TRACE=========================================== CString tempStr; tempStr.Format(_T("OnLvnItemchangedList iItem: %d, lParam: %d, uChanged: %d, uNewState: %d, uOldState: %d\n"), pNMLV->iItem, pNMLV->lParam, pNMLV->uChanged, pNMLV->uNewState, pNMLV->uOldState); TRACE(tempStr);
お礼
丁寧にお調べ頂き有難うございました。 機械翻訳のページは、読みました。ちょっと難しいですυ 今、各機能に変更があった場合に、FLGを立てました。これからテストします。 (たいしたことはやっていないですが・・・w) ついでに、カラムを押した時にソートする機能もつけようかと思っています。 さっきの ド・モルガンの法則が、当てはまるなら、 !(LVIS_FOCUSED | LVIS_SELECTED) (!LVIS_FOCUSED & !LVIS_SELECTED) (!LVIS_FOCUSED && !LVIS_SELECTED)かも知れませんね。 冗長ですが、フラグ立てわすれないようにif文をつけてます。 m_Dlg->ListDataNoChange_FLG = FALSE; CFileListCreatorDlg* m_Dlg = (CFileListCreatorDlg*)AfxGetMainWnd(); if( (m_Dlg==NULL)||(m_Dlg->GetSafeHwnd()==NULL) ){ return; } if (ListDataNoChange_FLG == FALSE){ m_Dlg->SetWindowText(_T("FileListCreator (*)")); } こっちの方がスマートですが・・・ m_Dlg->ListDataNoChange_FLG = FALSE; AfxGetMainWnd()->SetWindowText(_T("FileListCreator (*)")); 月曜日頃に締め切りたいと思います。長時間付き合ってくれてありがとうございました。
補足
補足の欄に失礼します。 >アイテムを追加したときはLVN_INSERTITEMで、削除したときはLVN_DELETEITEMで取れますが… >サブアイテムとかのテキストの変更は通知がなさそうですね。 結論はMFCに、各columnごとのデータ変更を検知する機能はないので、 OnLvnDeleteitemList()やOnLvnInsertitemList()を使用し、後は 自作するという事ですね。 ちょぴり残念ですが、目的の機能は実装できました。 参考URL→ http://wisdom.sakura.ne.jp/system/winapi/windata1.html 以下レスです。 質問に答えてないですねυ 確かにMessageBoxを記述すると、画面が更新されない、画面の一部が欠けるなど、不具合ありますが、用は足ります(!?) 単にTRACE に変えれば良いのですが…後もう少し、もう少しでとして粘っていましたが、結局デバッグした方が早かったですが・・・(おっしゃる様にマニュアルを読むようにと反省していますυ) PreTranslateMessage()の中でcolumnのクリックを検出するのは難しい(特に型を合わすのが、私にとってはまだ難しい)ので、カミナリマーク(管理イベント)で「OnLvnColumnclickList」を追加して、無事ソート機能は完成しました。 参考URL→ http://www.alpha-net.ne.jp/users2/uk413/vc/VCT_LVitemsort.html 後はチェックボックスあり(LVS_EX_CHECKBOXES)にした方が見栄えが良いので あり にします。(表示はなしですが、XP、Vista、7共に具合が良いので) チェックボックスなしにしたら、勿論、統一感がありますが、(解像度の問題があるので、)今の所は良しとして下さいm(_ _)m (古いXP機が廃れて使われなくなったら、また考えます。) ご回答有難うございました。