管理人のふたこと Tweet
リッチエディットコントロール2.0でリンクを実現する方法が今更必要になったのでメモ
公開日:2021/02/01
マウスふるふるについて、とある要望が寄せられたことを受けて、すでにヘルプ中でその要望内容のことは触れているのだが…と思っていたら、ダイアログ上にも書いてあって、いったい要望者はどうしてこれらを見ていないのかな?と思ったことが発端です。
これくらいは目立つようにしないとダメかな?というところから、上記のリンク先の記事にもある通り、ダイアログ上にリンクのようなものを設けて、マウスでクリックしたら、タスク スケジューラの起動や、設定方法を記載したヘルプページを表示するようにしよう、ということになりました。
Webブラウザでハイパーリンクすることが当たり前になった今日では、こういうUIは当たり前の存在なのですが、「マウスふるふる」(の前身となった「MouseLR」)はかなり古いソフトウェアをベースにしているということもあって、「マウスふるふる」は、C++でWindows APIをキックするという、古風な構造をしています。
(マウスイベントのシミュレーションを行うにはちょうど良い、という事情もあったりしますけど、ダイアログ上にリンクを設けることの大変さと比べたら、微々たるもんです。MFC、あるいは、.NETのWindows Formでも使えば、深いことを考えなくてもできますから)
で、過去に、リッチエディットコントロール(2.0)を用いて「ハイパーリンク」(文字列の一部がクリック可能になっており、マウスでクリックすることでその先の文章へジャンプする、あるいは、何かの機能を実行する→こんな文字列)を実現したことがあったので、それを使えば今回もうまく行くかな?とか思いまして。
リッチエディットコントロール2.0で、リンクを実現する方法なんて、古い技術だから、ググればさっさと出てくるだろうと思っていたら、あまりに誰も使っていないせいか、全然ヒットしないんですね。
Googleが10年以上前のページをヒットさせないようにしているとかいう話を聞いたことがありましたので、もしかすると、そのせいかもしれませんけど。
とにかく、ヒットしない。ヒットすれば、コピペ+多少の改造で作れるかと思ったのに!
怠惰プログラマな自分には敗北宣言に等しいのですが、脳を使ってプログラミングをすることにしたいと思います。で、もったいないので、そのことを、この「管理人のふたこと」に残そうかと思います。
今更、C++でリッチエディットコントロールを使おうという人がいるとは思えないけどね。
- Visual Studioのダイアログ用のツールボックスから Rich Edit 2.0 Control を連れてきます。
なお、Rich Edit 2.0 Controlではなく、下から4番目の「SysLink Control」も使えるとのコメントをいただきました。ありがとうございました。これについては知りませんでした。実際に使ってみたら、背景色の設定以外は明らかに「SysLink Control」の方がラクだったので、次回は「SysLink Control」を使った場合の作り方についても解説記事を書こうかと思います。
- 今回は編集不可としたいため、プロパティでRead Onlyを指定します。
- 残念ながらこれだけだとこのRich Edit 2.0 Controlは利用できないため、プログラム中の、ダイアログを表示するよりも前のところに、DLLを読み込むための指示を入れます。プログラム開始時でもいいです。
// リッチエディット用に読み込み HMODULE hLibRichEdit = LoadLibrary( L"RICHED20.DLL" );
- ダイアログを利用し終わったら、DLLを解放する指示を入れます。入れなくてもプログラム終了と共に解放されるかもしれないですけどね。
if ( hLibRichEdit ) { FreeLibrary( hLibRichEdit ); }
- ダイアログのWM_INITDIALOGらへんで、リッチエディットコントロールに対して、細かい設定をしてやります。
case WM_INITDIALOG: // タスク スケジューラに関する解説文の作成 hRichEditAboutTask = GetDlgItem(hDlg, IDC_RICHEDIT2_ABOUTTASK); if (hRichEditAboutTask) { // (1)リッチエディットのイベントマスク設定 CHARFORMAT2 cf2; LRESULT dwEvent = SendMessage(hRichEditAboutTask, EM_GETEVENTMASK, 0, 0); dwEvent |= ENM_MOUSEEVENTS | ENM_SELCHANGE | ENM_LINK; SendMessage(hRichEditAboutTask, EM_SETEVENTMASK, 0, (LPARAM)dwEvent); // (2)背景色の変更 SendMessage(hRichEditAboutTask, EM_SETBKGNDCOLOR, (WPARAM)0, (LPARAM)RGB(0xee,0xff,0xff) ); // (3)文字列の設定 SetWindowText(hRichEditAboutTask, _T("指定時刻で機能ON/OFFを切り替えたい場合は、Windowsの「タスク スケジューラ」に本プログラムのコマンド\r\nラインを登録することで実施できます。詳細はヘルプをご覧ください。")); // (4-1)「タスク スケジューラ」を選択状態にする SendMessage(hRichEditAboutTask, EM_SETSEL, (WPARAM)33, (LPARAM)43); // (4-2)選択文字列にリンクを設定 cf2.cbSize = sizeof(CHARFORMAT2); cf2.dwMask = CFM_LINK; cf2.dwEffects = CFE_LINK; SendMessage(hRichEditAboutTask, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2); // (5-1)「ヘルプ」を選択状態にする SendMessage(hRichEditAboutTask, EM_SETSEL, (WPARAM)78, (LPARAM)81); // (5-2)選択文字列にリンクを設定 SendMessage(hRichEditAboutTask, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2); }
(1)リッチエディットのイベントマスク設定 … クリッカブルテキストをクリックしたときにイベントを発生させるための指定です。
(2)背景色の変更 … リッチエディットの背景の色です。ここでは薄い水色にしています。もし、通常通りのダイアログの背景色にしたければ、RGB(0xee,0xff,0xff) の部分を GetSysColor(COLOR_3DFACE) にするとよいです。
(3)文字列の設定 … 表示する文字列を設定します。ここでは、プレーンなテキストを設定しているだけなので、この方法にしていますが、リッチテキストを指定したければ、もっと小難しい方法で指定することになります。
(4)「タスク スケジューラ」の文字列を選択状態にし、クリッカブルテキスト化する … 33文字目~44文字目の文字列を選択状態にします。その後、選択状態のテキストに対して、クリッカブルテキスト化します。
(5)「ヘルプ」の文字列を選択状態にし、クリッカブルテキスト化する … 78文字目~81文字目の文字列を選択状態にします。その後、選択状態のテキストに対して、クリッカブルテキスト化します。
うん、面倒くさいですね…。まぁ、クリッカブルテキスト化する箇所が増えれば、もうちょっと効率の良いやり方をするのですが、今回は2か所しかなく、固定なので、こんな感じでやることにしました。
- リンクがクリックされたイベントを捕捉して、必要なコマンドが実行できるようにします。
case WM_NOTIFY: if (wParam == (WPARAM)IDC_RICHEDIT2_ABOUTTASK) { // リッチエディットからの通知メッセージ LPNMHDR lpN = (LPNMHDR)lParam; ENLINK *penlk; TCHAR szLINK[32]; // リンク文字列最大長は32文字まで。それ以上は無反応とする。 switch (lpN->code) { // リンクをクリックした場合の処理 case EN_LINK: penlk = (ENLINK *)lParam; if (penlk->msg == WM_LBUTTONDOWN) { // 文字列が長すぎたら無反応 if (penlk->chrg.cpMax != -1 && penlk->chrg.cpMax - penlk->chrg.cpMin < _countof(szLINK)) { SendDlgItemMessage(hDlg, IDC_RICHEDIT2_ABOUTTASK, EM_EXSETSEL, 0, (LPARAM)(&(penlk->chrg))); if (0 < SendDlgItemMessage(hDlg, IDC_RICHEDIT2_ABOUTTASK, EM_GETSELTEXT, 0, (LPARAM)szLINK)) { // 文字列取得 if (lstrcmp(szLINK, _T("タスク スケジューラ")) == 0) { TCHAR sysdir[MAX_PATH*2] = L"\\"; GetSystemDirectory(sysdir, _countof(sysdir)); ShellExecute(hDlg, _T("open"), _T("taskschd.msc"), _T("/s"), sysdir, SW_SHOWNORMAL); // タスク スケジューラを起動する } else if (lstrcmp(szLINK, _T("ヘルプ")) == 0) { HtmlHelp(hwnd, _T(".\\mousefr.chm::/_RESOURCE/HLP000013.html"), HH_DISPLAY_TOPIC, NULL); // タスク スケジューラの設定方法を書いたページを開く } } } } break; } } break;
リンクをクリックしたときのイベントは、WM_NOTIFYのEN_LINKで飛んできます。WM_NOTIFYを補足すると、wParamに出元のコントロールIDが入っていますので、そこを確認し、リッチエディットコントロールから来たメッセージであることを調べます。このとき、クリッカブルテキストは「選択状態」になりますので、「選択状態」になっているテキストをEM_GETSELTEXTで取り出して、どこがクリックされたかを把握して、コマンドを実行してやります。
どこが選択状態になったかを把握する方法はいろいろあると思いますが、今回は単純に、選択状態の部分の文字列を取得し、その文字列が特定の文字列になっているかどうかを判定することにしました。もちろん、他にスマートなやり方はいくらでもあると思いますが、今回はたまたまそうしました。
これにより、クリッカブルテキスト(青色文字でアンダーバーが引かれている部分)にマウスカーソルを合わせれば、マウスカーソルの形が指の形に変わります。そこでマウスをクリックすれば、指定の挙動が実行されます。
次回:SysLinkコントロールを使った場合
本ページへは、自己責任の範囲内であれば自由にリンクしていただいて構いません。
本ページに掲載されている内容は、自由にお使いいただいて構いませんが、必ずしも筆者が内容を保証するものではありませんので、ご利用に際しては自己の責任においてお使いいただきますよう、お願いいたします。
このページのURLやアンカーは、サーバ運営・サイト運営・ページ運営・その他の都合により無告知で一時的あるいは永遠に消滅したり、変更したりする可能性がありますので、あらかじめご了承下さい。
本ページは、公開から1年半経過後の任意のタイミングで削除される予定です。本ページの内容は複製・公開していただいて構いません。