making of text editor -メモ帳を作ろう-

 ここでは,Windowsでいうメモ帳に相当するテキストエディタを作ってみたいと思います.MFCやWindowsプログラミングの知識がなくても,ソフトウェアを製作できる配慮をしていくつもり(C言語は知っていないと大変ですが・・・)なので,安心してついてきてくださいね.実際にソフトウェアを作成しながら,キーワードや重要な操作を書いていきます.そのあたりはぜひ習得してください.

このセクションは,MFCプログラミング初心者向けですので,より分かりやすくするために文書を色分けしています.

VisualC++でプログラミングを進めていく上で,是非知っておくと便利なキーワードとその説明です.
具体的な操作手順が書かれています.
プログラムに関するちょっとしたことが書かれています.
ソースファイル

■ AppWizardを使ってプロジェクトを作成する.

 AppWizardは,その名の通り,Windowsプログラミングのためのプロジェクトを自動生成してくれる「魔法使い」のことです.これを利用して簡単にプロジェクトを作成することができます.

VisualC++によるアプリケーション開発をする時に使われる概念

ワークスペース(WorkSpace)
 1つあるいは複数のプロジェクトを総括するもの.現在は1つのワークスペースに1つのプロジェクトというのが
管理の面で主流になっている.
※ 机の引出しのようなもの.引出しの中には,1つあるいは複数の授業科目のルーズリーフ帳(プロジェクト)が入っている.
プロジェクト(Project)
 1つのアプリケーションを作成するための土台.
※ 1つの科目のルーズリーフ帳のようなもの.この中に挟まっているルーズリーフに勉強したこと(プログラム)が書かれている.
プログラム,ソース(Program,Source)
 プログラムのソースコード.プロジェクト内に入っているソースのみがアプリケーション作成に適用される.
※ ルーズリーフに書いた内容に相当

一般的なアプリケーション形式の種類

SDI(Single Document Interface)
 Windows付属のメモ帳のように,メインウィンドウのみで,サブウィンドウが存在しないウィンドウ形式.
MDI(Multi Document Interface)
 メインウィンドウがあり,その中にサブウィンドウが存在するウィンドウ形式.VisualC++もそのひとつ.
機能は充実するが,プログラミングは結構大変になることが多い.
DLG(Dialog)
 メニューが存在せず,ボタン等のGUIを多用するウィンドウ形式.ファイルを開くときに出てくる,
ファイル選択ウィンドウがこの形式の代表.

では実際の手順に入りましょう.

  1. [ファイル]--[新規作成] を選び、プロジェクトのタブをクリック
  2. MFC AppWizard(exe)を選びプロジェクト名を記述
    ※ ここではNotepadというプロジェクト名でソフトを作成する.
    ※ ここで「新しくワークスペースを作成する」にチェックが入れておく.
  3. [Step1] でMDIを選択.
    ※ 複数ファイルを操作できるアプリケーションになる.
  4. [Step5]までは「次へ」を押していきます.
  5. [Step6]にてCNotepadViewクラスをハイライトすると,「基本クラス」コンボボックスが選択できる.
    ここをCEditViewに変更する.
  6. 最後に終了ボタンを押す.
    ※ このアプリケーションに必要なファイルの雛形が作られる.

 上記の手順の中で,一番謎な部分は,おそらく手順5のところだと思います.C言語を知っている方でもC++のことをまったく知らなければ「クラス」という言葉やましては「基本クラス」なんていうものが分からないからです.ここではクラスに関する詳しい説明はしませんが,簡単に言うと,”構造体と構造体のデータを変更する関数がセットになったもの”を想像しておいてください.

 MFCアプリケーションでは,「内部データ」と「表示」のコーディングを分離させることで,プログラムの複雑化を防ぐという考え方があります.これを「Document-View構造」というのですが,手順5では,そのView(表示;ビュー)の基本をCEditViewというものに指定したということになります.アプリケーションにはいろんな表示方法があります.例えば,
 「テキストエディタ」のように単色で飾りのないテキストを表示するもの
 「ワープロ」のように複数のいろや写真などを使って飾ったテキストを表示するもの
 「ブラウザ」のようにHTML言語で記述されたもの
 「お絵かき帳」のようにビットマップを表示するもの
 「エクスプローラ」のように,リストやツリーを表示するもの
などです.いちいちこれらを実現しようと思うと非常に大変なことになります.だいたい私のようにデザインセンスがなければ(^-^;)かっこ悪いものになりそうですし.そこでMFCではあらかじめよく使いそうなものをクラスという形で提供しています.今回使用するCEditViewクラスは,表示部分に関して,テキストエディタとして便利なデータや関数を提供してくれます.今回はその恩恵を授かろうということです.ちなみに・・・表示に関する基本クラスの一例を載せておきます.

CView
すべてのビューの基本.(ビットマップを表示していることになります)
CEditView
エディットボックスに基づいたビュー.テキストの入力と編集ができます.
CRichEditView
CEditViewと似ていますが,書式化された(例えばフォント種類,フォントサイズ,カラー)テキストや画像などのオブジェクトの貼り付けなどが可能なビュー.
CListView
フォルダ内やエクスプローラの右側部分のような表示をするビュー.
CTreeView
エクスプローラの左側部分や,フォルダ選択ダイアログのように,ファイル構造などの木構造を表示するビュー
CHtmlView
Webブラウザビュー.VisualC++6.0から適用できます.
 AppWizardの”[Step6]--AppWizardで作成される新規アプリケ−ションクラス--”で基本クラスを変更しなくても,後で基本クラスの変更は可能です.作成したプロジェクトがNotePadならば,NotePad.cppに含まれるCNotePadApp::InitInstanceという部分で変更が可能です.ただしこれにはドキュメントテンプレートの概念はもちろん,Document-View構造をよく理解しないとなかなか出来ないので,最初に変更しておくのです.

■ プロジェクトの実行

 アプリケーションを作るためには,実行をしなくてはいけません.VisualC++における実行までの操作は,大きく分けて3種類あります.

コンパイル(Compile)
 現在アクティブなプロジェクト内に登録されている,*.cおよび*.cppファイルのシンタックス(文法)チェックを行い,*.objという(オブジェクト)ファイルを生成します.簡単にいうと,0と1で記述されたファイルに変換することを指します.
ビルド(Build)
 プロジェクト内のすべてのファイルをコンパイルし,dllなどのライブラリをリンクします.コンパイラとリンカの仕事を一般にやってくれます.
実行(execute)
 ビルドと実行形式のファイルの実行まですべてを行います.

 「プロジェクトの実行」をする際には,メニューから「ビルド」-「実行」を選択すればOKです.実行できれば,簡単なメモ帳が生成されるでしょう.テキストファイルを開いてみるなりして動作を確認してみましょう.

■ アプリケーション外観のカスタマイズ(リソースの編集)

 前項にて,プロジェクトの実行ができましたか?そしてメモ帳のように編集することが可能なアプリケーションができているでしょうか?私達は実際コーディングをしたわけでもないのにある程度のものが出来上がってしまいましたが,これは基本クラスとしてCEditViewに設定した賜物です.しかしちょっと操作すると,いろんなところが使いにくいのです....

 私は,プログラムの良し悪しには3つあると考えています.

 1つ目は当たり前なんですが,一番みんなが苦しむところです.一見正しそうなコーディング(もちろんコンパイルが通るので文法的には間違っていない)でも,実行すると強制終了してしまったり,動作がおかしかったりすること,つまるところバグですね.2つ目はアルゴリズムとアプリケーションの相性.いくらすばらしいアルゴリズムを使っても,使い方によっては,ほとんど意味をなさないことなんてあります.まぁこれらは経験で学ぶとして,Windows Programmingで重要になるのは,プログラムという「機械」と「人間」の間の仲介役となるユーザインターフェースなのです.

 ではユーザインターフェースをある程度改良してみましょう.このユーザインターフェースの改良に欠かせないのが,リソースと呼ばれるものです.

リソース(Resource)
 メニュー,アイコン、ビットマップなどのGUIを支援する共通のデータパーツ資源で,プログラムとは別に作成され,実行時にバインド(結びつけ)される。リソースのメリットは,

などがある.

以下に示すものは,MFCで使用される主なリソースの種類です.

アクセラレータ(Accelerator)
ショートカットキーを設定します.
ビットマップ(Bitmap)
ビットマップを作成・設定します.
カーソル(Cursul)
特別なカーソルを設定します.
ダイアログ(Dialog)
ダイアログウィンドウを作成・設定します.
アイコン(Icon)
プログラム中のアイコンを作成・設定します.
メニュー(Menu)
メニューを作成・設定をします.
ストリングテーブル(String Table)
アプリケーションで使用する固有の文字列(ウィンドウタイトル・エラーメッセージなど)を設定します.
ツールバー(Tool Bar)
ツールバーを作成・設定します

ここでは,以下の点のリソースを変更していきたいと思います.

◇ 開くファイルをテキストに指定する<ストリングテーブルの改良>

ストリングテーブル(String Table)
 ツールバー,ボタン,メニューなどの上にマウスカーソルがあると、小さな説明(ツールチップ)が表示されたり,ウィンドウ下のステータスバーに文字列が表示されることに気づく.これは,そのリソースのIDにそれぞれ割り当てられているプロンプトが表示されているためであるが,そのプロンプトをまとめたものをストリングテーブルという.

 ストリングテーブルの中でも特別なものが幾つかある.
IDR_MAINFRAME
タイトル名や開かれるファイルに関する指定がなされる重要な項目
IDR_******TYPE
MDI子ウィンドウ用の重要な(SDIでIDR_MAINFRAMEに対してするような)指定がなされる項目
AFX_IDS_IDLEMESSAGE
何もされていない時に,ステータスバーに表示される項目(レディなど)
AFX_IDS_APP_TITEL
アプリケーションのタイトル(厳密に言うとプロジェクト名?)

 今回は,「開くファイルをテキストファイルに指定する」のが目的なのですから,ストリングテーブルのIDR_MAINFRAMEを編集すればいいのですねが,実際はIDR_******_TYPEを編集していきます.それはこのアプリケーションがMDIタイプであるためです.SDIであったらIDR_MAINFRAMEの方でOKです.では作業を進めていきます.

  1. ワークスペースウィンドウ(通常左端のドッキングウィンドウ)のリソースタブをクリック.
  2. String Tableフォルダのstring tableをダブルクリック
    ※ ID/値/キャプション などが書かれた表(ストリングテーブル)が表示される.
  3. IDR_NOTEPATYPEと書かれているものをダブルクリック.
    ※ String プロパティが開く.
  4. キャプションを以下のように変更します.
     NotePad\n新規\n\nTextFile(*.txt)\n.txt\n\n
  5. プロジェクトを実行し,ファイルを開いてみる.
    ※ figure1のように反映されるはずである.

 

figure.1 ストリングテーブルの変更

 先ほど,IDR_NOTEPATYPEのキャプションを以下のように書き換えてみました.なにやら見慣れない文字列のような気がしますが,ちゃんとした意味があります.

NotePad\n新規\n\nTextFile(*.txt)\n.txt\n\n

NotePad (赤字):ウィンドウ上部のタイトルバーに示されるウィンドウのタイトル名.
新規 (青文字):ディフォルトで表示されるドキュメント名,省略するとUntitledになる.
TextFile(*.txt) (緑字):「ファイルの種類」コンボボックスに表示されるもの.
.txt (黒字):開く・保存されるときにダイアログにリストアップされるファイルの拡張子.

 分かりやすくするためにfigure1にも色別に示しています.また,"\n"がいっぱいあるなぁと思うかもしれません.これは,IDR_MAINFRAMEやIDR_NOTEPATYPEでは7つのプロンプトを設定する必要があるため,その区切り記号として"\n"が使われているからです.設定しない場合は単に省略するだけでOKです.

■ (コラム) MFC (Microsoft Foundation Class) と オブジェクト指向(Object Oriented)

 さて,そろそろ疲れてきたころだと思うので,少しコラムを1つ.コラムといってもこれから「MFC」というライブラリを使うための重要な概念についてなのでしっかり読んでいただけたら幸いです.

 ハンバーガを食べたい時,皆さんはハンバーガショップに行くと思います.そこで,ハンバーガを注文すると無事食べることができます.ここで変な質問です.「あなたはなぜハンバーガを食べることができたのですか?」 この質問に対してこう答えるしかないですよね.「ハンバーガ屋に注文すれば,食べることができるのを知っていたから」,と.ここで重要なのは,ハンバーガを食べる側は,ハンバーガの材料は何か?調理法は?などの細かい操作を知る必要がないということです.

 これは実世界の思考と通じる所がありますよね.例えば,配属されてきた新入社員たちに,先輩社員は「自己紹介してよ」といえば,新入社員はそれぞれの方法,それぞれの手段をつかって自己紹介するでしょう.先輩社員は,細かい指示をせず自己紹介をさせることができるのです.このように物や人(オブジェクト)が行う細かい動作を気にすることなく,作業を実現する物主体の考え方を,「オブジェクト指向」といい,プログラムでもこの考えを取り入れることが盛んになってきています.オブジェクト指向についての細かい話は他の文献に譲りますが,大体こんなものだということだけ頭に入れておいてください.

 さていま私達が取り組んでいるMFCに話は変わりますが,MFCとは,Microsoft Foundation Class libraryと呼ばれる,C++言語によるオブジェクト指向がサポートされたWindows Programmingライブラリのことです.私達が取り組んでいるメモ帳も,たいしたプログラミングもせずにある程度のメモ帳が出来上がっていますよね.それはMFCが提供するCEditViewというものが,メモ帳に必要な細かい操作を行ってくれているので,私達は細かい操作を気にしないでプログラミングしているわけです.だから私達が習得することは,如何にしてCEditViewの恩恵を受けるか,つまりCEditViewの使い方を知るかどうか?だけを考えればよいのですね.そこで次節では,メニューコマンドの操作について学んでいきましょう.

■ メニューコマンドの操作

さて,実際にCEditViewのクラスライブラリを使って,メニューコマンドから検索や置換などができるようにしてみましょう.文字列の操作なんてできないよ・・・と思っている人もいるかもしれませんが,ここではCEditViewのそれぞれの機能を呼び出してあげれば簡単に実現できます.

◇ 削除(D)というメニューを追加する.
  1. ワークスペースのリソースタブをクリックし,メニューフォルダのIDR_NOTEPATYPEをダブルクリック.
  2. メニューの「編集」をクリックし,メニュー内で項目が無いところをハイライト,右クリック,プロパティ(figure.2).
  3. プロパティダイアログに以下の内容を記述(figure.3).
    キャプション 削除(&D)
    ID:ID_EDIT_CLEAR
    ※ 記述すると,プロンプトの欄に自動的に何か文字列が書き込まれますが,これはID_EDIT_CLEARというメッセージIDが,CEditViewで定義されているためです.
  4. ダイアログを閉じ,ビルドすると削除のメニューが追加され,エディタ内の文字を削除することが可能.

figure.2 位置の選択

figure.3 プロパティの変更

◇ 水平線(セパレータ)を入れる
  1. 削除のメニュー追加と同様に1と2を行う.
  2. 現れたダイアログでセパレータにチェックを入れる.

 このような感じでメニューをどんどん追加していけばいいのです.ただこれはこれらのIDがすでにどこかで定義されているからこのように簡単な操作でできるということを覚えておいてください.もう少し具体的に言えば,例えば削除メニューを押すと,OSにID_EDIT_CLEARというメッセージが流れます.それをエディタがキャッチし,CEditViewでID_EDIT_CLEARに割り当てられている操作を行っているだけです.

 残りの操作は自力でやってみてください.そのためには操作に対応したIDが分からないとダメですね.簡単なやつだけ表にまとめておきます.

メニュ- 追加項目 ID メニュ- 追加項目 ID
ファイル 新規作成 ID_FILE_NEW 編集 コピー ID_EDIT_COPY
  開く ID_FILE_OPEN   貼りつけ ID_EDIT_PASTE
  上書き保存 ID_FILE_SAVE   削除 ID_EDIT_CLEAR
  名前をつけて保存 ID_FILE_SAVE_AS   文字列の検索 ID_EDIT_FIND
  閉じる ID_FILE_CLOSE   文字列の置換 ID_EDIT_REPLACE
  プリンタの設定 ID_FILE_PRINT_SETUP   全て選択 ID_EDIT_SELECT_ALL
  印刷 ID_FILE_PRINT ウィンドウ 新しいウィンドウを開く ID_WINDOW_NEW
  アプリケーションの終了 ID_APP_EXIT   重ねて表示 ID_WINDOW_CASCADE
編集 元に戻す ID_EDIT_UNDO   並べて表示 ID_WINDOW_HORZ
  切り取り ID_EDIT_CUT ヘルプ バージョン情報 ID_APP_ABOUT

こんな風にできたらいいと思います.頑張ってみてください.

■ フォントを変更できるようにしよう

 Windowsに付いてくるメモ帳も,フォントを変更することができます.私達もフォントが変更できるように頑張ってみましょう.まず,フォントの変更に関する仕様は以下の通りとします.

  1. ユーザがメニュー内の[フォントの変更]をクリック.
  2. フォント選択ダイアログが開く.
  3. フォントをユーザが指定する.
  4. 指定された通りにフォントに変更する.

 このようにプログラムを書く前にある程度の仕様というかストーリー?を考えておくことは,プログラミングでは重要です.ではまずはじめに,「フォントの変更」メニューを追加してみましょう.といっても別に目新しいことではありません.先ほど削除メニューを追加したこととほとんど同じです・

  1. ワークスペースのリソースタブをクリックし,メニューフォルダのIDR_NOTEPATYPEをダブルクリック.
  2. メニューの「表示」をクリックし,メニュー内で項目が無いところをハイライト,右クリック,プロパティ.
  3. プロパティダイアログに以下の内容を記述.
    キャプション フォントの変更(&F)
    ID:ID_VIEW_FONT
    プロンプト:使用するフォントを変更する\nフォントの変更
  4. ダイアログを閉じる.

こうするとメニューの「表示」に「フォントの変更」という項目が追加されます.先ほど「削除」メニューを追加した時と違うことはメニューのIDが適当でいいということです.つまりこのIDは自分で決めることが可能です.でもできるだけわかりやすいIDにしておくことも重要です.ここでIDとWindowsについて少しお話をしておきます.

Windowsとメッセージ

 Windowsはメッセージと呼ばれるものを使って動いています.例えばウィンドウの右上の×ボタン(システムボタン)が押されると,ウィンドウは消えることになっていますが,これは「ウィンドウ消去」というメッセージがOS(Windows)から送られてくるのをアプリケーションが取得し,既存の動作をさせているのです.このようにWindowsプログラミングはメッセージのやり取りだと思ってもいいかもしれません.

 さっきからIDと呼ばれているものは,実はメッセージに相当します.つまり「フォントの変更」を押すとOSから,ID_VIEW_FONTというメッセージがやってくるので,それを捕まえてフォントの変更処理をすることで先ほど仕様として決めたことが実現できるのです.となるとメッセージを捕まえる機構が必要ですよね〜.それを実現するのがClassWizardです.

  1. メニュー「表示」-「ClassWizard」を開き,メッセージマップタブをクリック.
  2. 以下のように選択・ハイライトする(figure5)
    プロジェクト:NotePad
    クラス名:CNotePadView
    オブジェクトID:ID_VIEW_FONT
    メッセージ:COMMAND
  3. 関数の追加ボタンを押し,関数名をOnViewFont(ディフォルト)に設定しOKを押す.
  4. 最後にコード編集ボタンを押す.
    ※ これを押さずに下のOKボタンの押してClassWizardを閉じた後,ワークスペースのクラスビューから,CNotePadViewを参照することも可能.

figure.5 ClassWizardによる設定

 いったい何をしたのだろう?と思う人は,ID_VIEW_FONTが押された時にCNotePadView内の関数OnViewFontが実行されるようにマッピングしたと考えれば多少は分かるかも知れません.だからClassWizardのこの画面は,メッセージマップというのです.したがってフォントの変更のコーディングはOnViewFontに書けばよいということになります.ちなみにフォントの変更は外観の変更に過ぎません.したがって関数は外観を扱うViewに追加するのがベストです.

 いま,NotePadView.cppが開かれていると思います(そうでなかったら開いておいてくださいね).多分以下のように書かれているので,

/////////////////////////////////////////////////////////////////////////////
// CNotePadView クラスのメッセージ ハンドラ

void CNotePadView::OnViewFont() 
{
	// TODO: この位置にコマンド ハンドラ用のコードを追加してください
	
}

 これを以下のように変更してみましょう. 

void CNotePadView::OnViewFont() 
{
	AfxMessageBox("VisualC++");		// Open Message Box
}

 では実行してみましょう.フォントの変更をクリックすると,VisualC++と書かれた小さなウィンドウ(メッセージボックス)が表示されたら成功です.これでメニューと関数がしっかり対応付けされているということがわかりますね.AfxMessageBox関数は,MFCを使用している場合ならどこでも使用することが可能なので,C言語のprintf関数のようにデバックにも有効だと思います.

 それではフォントの変更プログラムを書いていきましょう.まずビュークラス(CNotePadView)にフォントを扱う変数を追加しておきます.このような変数を,C++ではメンバ変数と呼びます.ちなみに先ほど追加したOnViewFontはメンバ関数になります.

  1. ワークスペースのクラスタブをクリックし,CNotePadViewを右クリックし,メンバ変数の追加をクリック
    ※ ダイアログが開かれます.
  2. ダイアログに以下の値を設定してOKを押す.
    変数のタイプ:CFont
    変数名:m_Font
    アクセス制御:private

 追加したものは,フォントを操作するためにMFCで用意されているCFontクラスの変数です.うまく追加できると,クラスビューがこんな感じに表示されます.

figure.6 メンバ変数の追加
 

 実際には,NotePad.hファイルにこのように宣言されます.逆にいえばメンバの追加は,直接コーディングで行うことができるということですね.

// 生成されたメッセージ マップ関数
protected:
	//{{AFX_MSG(CNotePadView)
	afx_msg void OnViewFont();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
private:
	CFont m_Font;
};

 ちなみに上のコードを見てみると,OnViewFontの定義もされていますね.これはClassWizardのメッセージマップで作られために,//{{AFX_MSG(CNotePadView) なんていうやつで囲まれていますが,メンバ関数のひとつです.先ほどから気になっている人がいるかもしれませんが,アクセス制御(privateとかpublicとか)とはなんでしょう?それは実はオブジェクト指向に秘密があります.

アクセス制御
 オブジェクト指向では,オブジェクトの「状態」と「状態を変化させる手段」を持つことになっています.例えば,人間というオブジェクトの状態の1つに「体重」があります.そして体重を変化させる手段として「食事」「ダイエット」などがあります.ここで重要なことは,一般的に「状態」は「状態を変化させる手段」以外では変化させることができないのです.だから「運動しない」という手段では当然「体重」は減りません.こうすることで,ある種の情報を外部から見えなくすることができます.これを情報隠蔽といい,オブジェクト指向の重要概念の1つです.

 しかし,これをプログラムで実現しようとする時,すべて外部から見えなくなるのも不便です.そこで3種類のアクセス制御を書けることができます.

private クラス内部からしかみることができない.
protected クラス内部からしか見ることができないが例外もある(説明は省略)
public クラス外部からも参照することができる.

 C++では,この「状態」に当たるものをメンバ変数,「手段」に当たるものをメンバ関数と呼びます.大抵のメンバ関数はpublicにされます.そうしないとクラス外部からはまったくアクセスができなくなってしまうからです.

では,フォント変更に関する準備が整いましたのでコーディングしていきましょう.

void CNotePadView::OnViewFont() 
{
	LOGFONT		lf;	// LOGFONT構造体の宣言
	m_Font.GetLogFont(&lf);	// 現在のフォント情報を取得

	// コモンフォントダイアログの作成
	CFontDialog	dlg(&lf, CF_SCREENFONTS|CF_INITTOLOGFONTSTRUCT);

	// ダイアログを開く
	if (dlg.DoModal() == IDOK)
	{
		m_Font.DeleteObject();
		if(m_Font.CreateFontIndirect(&lf))
			SetFont(&m_Font);
	}
}

 はじめてC++のコーディングをする人は,へんなコーディングだなぁと思うかもしれません.クラスの変数の後ろに,"."を付けて記述していますね.この"."をつけることで,そのクラス変数のメンバ関数を呼び出すことができるのです.つまり人間というクラスがあるとして,メンバ関数に自己紹介というものがあったら,以下のようにすれば自己紹介してくれます.

人間	日本太郎;
日本太郎.自己紹介();

では,コーディングの説明をしていきます.

LOGFONT  lf;
  フォントの情報を格納するLOGFONT構造体変数lfの定義です。LOGFONT型は、フォントの大きさ・種類・高さ・角度・幅・色などの情報を格納できる変数です。 CFontクラス変数は,あくまでフォントを操作するための変数なので,フォントを扱う時には,CFont変数にLOGFONT構造体変数をセットして使います.

m_Font.GetLogFont(&lf);
 現在表示に使用されているフォントの情報を,CFont型変数m_Fontを使ってLOGFONT変数lfに書込みます.

CFontDialog   dlg(&lf, CF_SCREENFONTS|CF_INITTOLOGFONTSTRUCT);
 フォントを設定するコモンダイアログ(共通ダイアログ)を開きます.このとき設定した内容はLOGFONT構造体lfに格納されるように引数を指定しておきます.

if (dlg.DoModal( ) == IDOK) {
 
フォントコモンダイアログを開き,OKボタンが押された時のみif構造の中を実行します.

m_Font.DeleteObject( );
 現在設定されているフォントを削除します.この変数が消えてしまうわけではなく,メモリーに残された現在のフォント情報を消すということです.

if(m_Font.CreateFontIndirect(&lf))
 LOGFONT構造体を使って,新しいフォントを作成します.成功したらif構造の中を実行します.

SetFont(&m_Font);
 先ほど作成したフォントm_Fontを現在のフォントとしてビューにセットし,プログラムで使用するフォントとして反映します.

どんなことをしているか分からない場合は,以下の図を見てもらえれば多少分かると思います.

figure.7 フォントの変更の仕組み

早速実行してみましょう.うまくフォントが変更できたでしょうか?残念ながら「フォントの変更」を押すと,

figure.8 エラー発生

 というエラーが出てきてプログラムは強制終了になってしまいます.これはASSERTといってソースファイルのシンタックス(文法)には問題はないが,おかしな動作をしている時に出るエラーです.もちろん自分でASSERTで強制終了させるプログラムを書くこともできますが.
 まぁ結果的にはプログラムが間違っているわけなので修正が必要なのですが,今回は特別に?どこが間違っているかを説明してしまいましょう(実際は自分がなんとかして調べるのです)

 ソース2行目のm_Font.GetLogFont(&lf);で現在のCFontクラスからフォント情報を得るということになっていますが,現在のCFontクラスとは何のことでしょうか?そもそもm_Fontはこちらが決めた変数ですから,Viewクラスと何も関係を持っていないのでフォント情報を持っていないのです.したがってフォントの変更が行われる前に,m_Fontにフォント情報を当てておかなくてはいけません.さて問題はいつそれをやるかです.

 あれこれ悩むところですが,MFCのフレームワークは,タイミングや用途に合わせて既存の関数が実行させることができます(正しく言うと勝手に実行される).テキストが開かれて表示される直前に実行される関数として,OnInitialUpdate() というビューのメンバ関数を使ってフォント情報を与えるコーディングを書いていきます.まずはOnInitialUpdate関数が呼ばれるための手続きをしてあげます.基本的にアプリケーションが自動的に呼び出す関数を設定する時はClassWizardを使います.

  1. クラスウィザードを起動し,メッセージマップタブをクリックする.
  2. 以下のように選択・ハイライトする
    プロジェクト:NotePad
    クラス名:CNotePadView
    オブジェクトID:CNotePadView
    メッセージ:OnInitialUpdate
  3. 関数の追加ボタンを押し,編集ボタンを押す.(関数名の指定はなし)

するとNotePadView.cppにOnInitialUpdate()という関数が追加されます.ここで以下のようにコーディングしてみましょう.

void CNotePadView::OnInitialUpdate() 
{
	CEditView::OnInitialUpdate();

	LOGFONT   lf;
	
	// 表示フォントの初期設定
	lf.lfHeight		=	18;
	lf.lfWidth		=	8;
	lf.lfEscapement		=	0;
	lf.lfOrientation		=	0;
	lf.lfWeight		=	400;
	lf.lfItalic		=	false;
	lf.lfUnderline		=	false;
	lf.lfStrikeOut		=	false;
	lf.lfCharSet		=	SHIFTJIS_CHARSET;
	lf.lfOutPrecision		=	OUT_DEFAULT_PRECIS;
	lf.lfClipPrecision		=	OUT_DEFAULT_PRECIS;
	lf.lfQuality		=	OUT_DEFAULT_PRECIS;
	lf.lfPitchAndFamily	=	OUT_DEFAULT_PRECIS;
	strcpy( lf.lfFaceName, "FixedSys" );

	ASSERT(m_Font.CreateFontIndirect(&lf));
	SetFont(&m_Font);	
}

ではまた解説していきます.

CEditView::OnInitialUpdate();
関数を作った時にかかれていたものです.ここではくわしく説明しません.「触らぬものにたたりなし」です.

LOGFONT   lf;
LOGFONT構造体変数のlfです.OnViewFont関数のlfでなくて、この関数内だけのlfだということに注意してください.

lf.〜
 これはフォントの設定をしています.LOGFONT構造体について調べればある程度分かると思いますが,こういうものだと認識してもかまいません.初期フォントとして,VisualC++のソースで使用されるフォントを採用しています.

ASSERT(m_Font.CreateFontIndirect(&lf));
 m_Font.CreateFontIndirect(&lf)で上で指定したフォント情報をm_Fontに取り込みます.この関数が失敗するとフォントの設定がうまくいかなくなり,アプリケーションとして成り立たないので,この関数が失敗したらASSERTされるようにしておきます.つまり強制終了です.成功したらASSERTはされません.

SetFont(&m_Font);
 フォントの設定に成功しているのであとはビューとフォントを関連付けするだけです.

 最後に実行してみましょう.フォントの変更が正しくできますか?テキストごとに異なるフォントが設定できましたか?いろいろチェックしてみてください(figure.9).

figure.9 フォントの変更に成功

■ 配布できるプログラムにしよう.

 通常VisualC++のプログラミング環境は大きく分けて,

という2つの環境が存在します.Win32 Debug環境は,デバッグ環境が整った開発環境ですが,コードの最適化がなされていません.逆にWin32 Release環境は,デバッグ環境はないものの,コードの最適化がなされます.通常はWin32 Debug環境でプログラミングを行い,プログラムが出来上がって実用するときにRelease環境でビルドしてあげるというのが一般的です.メモ帳も出来上がったのでここでRelease環境にしてビルドしてみましょう.そうすることでアプリケーションのファイルサイズが小さくなったり,スピードが上がります.

  1. メニューから[ビルド]-[アクティブな構成の設定]をクリック.
  2. プロジェクトの標準構成を,Win32 Releaseに変更してOK.
  3. [ビルド]-[実行]でプロジェクトを再ビルド.

 またデバッグが必要になったときなどは同じようにして設定をすれば両方が使い分けられますね.ちなみに出来上がった実行形式のファイルは,プロジェクトが存在するディレクトリの中にある,Debug/Releaseのディレクトリにそれぞれ入っています.

メモ帳を通してMFCプログラミングが,少し分かっていただけましたでしょうか?分からないことがありましたらお気軽に連絡をください.

[back to index]

Copyright by Chiaki Fujimon.
All Rights Reserved.