VisualStudio2022 C#プログラミング
Windowsフォームアプリを作成しよう.
「MyNote」というメモ帳に便利機能を拡張したテキストエディタをつくります。VisualStudioで実際に手を動かして(コードを書いて)一から作成していきます。最後にはアイコン作成やインストーラの作成方法、パソコンへのインストール方法までご説明します。プログラミング初心者でも分かりやすいように解説いたしますので、プログラミングに興味がある方やスキルアップしたい方はぜひこの機会にアプリ作成に触れてみましょう。完成したアプリは業務にも十分つかえるモノになっています。なお、C#で作るアプリは今回が初めてでしたが下記書籍を参考にしました。
基礎からきちんと知りたい人の C#プログラミング入門 (日経BPパソコンベストムック)
- 1 【プロジェクトの作成】
- 1-1 Visual Studio 2022を開く
- 1-2 新しいプロジェクトの作成
- 1-3 Windowsフォームアプリケーションの選択
- 1-4 「MyNote」アプリの作成
- 2 【アプリケーションの作成】
- 2-1 フォームを作成する
- 2-2 タイトルバーにプログラム名を表示する
- 2-3 プロパティはコードで設定
- 3 【メニューの作成】
- 3-1 ツールボックスのMenuItemを追加する
- 3-2 メニューに「終了」項目を作成する
- 3-3 アクセスキーを設定する
- 3-4 テキストボックスをフォームに配置する
- 3-5 リサイズに対応する
- 3-6 「ファイル」メニューに機能を追加する
- 3-7 セパレータを挿入する
- 3-8 ショートカットキーを設定する
- 3-9 「開く」機能を作る
- 3-10 ダイアログのキャンセル時の処理を追加する
- 3-11 「名前を付けて保存」機能を作る
- 3-12 ダイアログに「ファイルの種類」表示する
- 3-13 名前を付けて保存のファイル名を表示させる
- 3-14. タイトルバーにファイル名を表示する
- 3-15. 上書き保存が可能なときだけ有効にする
- 3-16. 変更がなかれば保存しない
- 3-17. 保存するデータがあるかチェックする
- 3-18. 内容の破棄の注意を促す
- 3-19. 保存しないファイルを開かないようにする
- 3-20. 上書き保存の警告を表示させる
- 3-21. 拡張子は入力しなくても「.txt」が付く
- 3-22. ユーザーが使っているフォルダーを覚える
- 3-23. 字の大きさを見やすいように変更する
- 3-24. 「設定」メニューを作りフォント設定ができるようにする
- 3-25. 不要なフォント設定を表示しない
- 3-26. フォント設定を記憶させる
- 3-27. ウインドウが小さくなり過ぎないようにする
- 3-28. ウインドウの位置と大きさを記憶させる
- 3-29. 「編集」メニューを作る
- 3-30. 元に戻すアンドゥ(Undo)機能をつくる
- 3-31. 切り取り、コピー、貼り付け機能をつくる
- 3-32. 「削除」メニューをつくる
- 3-33. 「すべて選択」メニューをつくる
- 3-34. 「編集」メニューの不要な機能は無効にする
- 3-35. 「ヘルプ」メニューをつくる
- 3-36. テキストファイル(README)を表示する
- 3-37. Webサイトを表示する
- 3-38. バージョン情報を表示する
- 3-39. 印刷機能を追加する
- 3-40. 印刷ダイアログを表示させる
- 3-41. 印刷プレビューを表示させる
- 3-42. テキストボックスの文字数を表示する
- 3-43. コードの順番を見直す
- 4. 【アプリのインストーラの作成】
- 4-2. インストーラをつくる
- 4-3. セットアップする
1 【プロジェクトの作成】
1-1 Visual Studio 2022を開く
1-2 新しいプロジェクトの作成
右側の選択メニューから「新しいプロジェクトの作成(N)」を選択します。
1-3 Windowsフォームアプリケーションの選択
「新しいプロジェクトの作成」ウインドウで検索ボックスに「フォーム」と入力し「Windowsフォームアプリケーション(.NetFramework)」(C#が表示されてる)を選択し画面右下の次へボタンをクリックします。
1-4 「MyNote」アプリの作成
「新しいプロジェクトの構成」ウィンドウでプロジェクト名入力ボックスに「MyNote」と入力します。
場所選択ボックスで作成するフォルダを指定できます。
フレームワーク選択ボックスで「.NetFramework4.8」を選択する。(PCの環境によって変えてください)画面右下の作成ボタンをクリックします。
2 【アプリケーションの作成】
2-1 フォームを作成する
VisualStudioによってフォームアプリが自動で生成されます。
新規で生成されたフォーム名は「form1」となります。
2-2 タイトルバーにプログラム名を表示する
デザイナーエリアのフォームにカーソルを合わせてダブルクリックすると、「Form1_Load」メソッドが自動で生成されます。
class Form1 の先頭に
const string ApplicationName = "MyNote";
を入力し
アプリの名前「MyNote」を文字列型の定数(const)で宣言し「ApplicationName」に設定します。
次に、フォームを起動したときに読み込まれるメソッドForm1_Loadに
this.Text = ApplicationName;
を入力します。
VisualStudioのメニューの「▶再開」をクリックし実行してみましょう。
フォームが立ち上がり左上にアプリ名「MyNote」が表示されました。
2-3 プロパティはコードで設定
VisualStudioのIDEではプロパティウインドウが表示されています。
図のようにフォームのTextプロパティをウインドウで設定することも可能ですが
項目が多く自分で設定した内容がよく分からなくなることがあります。
コードで記述しておけば一覧でみやすくなりますのでなるべくコードで設定する
ようにしましょう。
3 【メニューの作成】
3-1 ツールボックスのMenuItemを追加する
VisualStudioの上部のメニューの表示(V)→ツールボックス(X)を
選択すると、ツールボックスウィンドウが表示されます。
ツールボックスの「メニューとツールバー」タブを選択し「MenuStrip」を
フォームにドラッグ&ドロップします。
MenuStripはフォーム上に位置と大きさを確保するものではないので
フォームの下側に貼り付きます。名前はプロパティに設定されたままの
「menuStrip1]となります。
フォーム上部にMenuStripが配置されました。
「ここへ入力」と書かれた部分をクリックし「ファイル(&F)」と入力しエンターキーを押すと、「ファイル(&F)」の文字列をTextプロパティに設定され、新しいメニュー項目が作成されます。
プロパティウインドウのNameプロパティを選択し、「MenuItemFile」へ
名前を変更しましょう。
3-2 メニューに「終了」項目を作成する
先ほど作成したメニューの「ファイル」の下に「終了(&X)」を入力し項目を
作成します。
Nameプロパティを選択し、「MenuItemFileExit」へ名前を変更しましょう。
フォームデザイナーで作成した項目の「終了」の入力欄をクリックすると
コードエディタが開き「MenuItemFileExit_Click」メソッドが自動生成
されます。そこにフォーム(Form1)を閉じる処理のコードを入力しましょう。
this.Close();
3-3 アクセスキーを設定する
ここまで作った「ファイル(&F)」と「終了(&X)」のように「&」(アンバサンド)に続けて英文字一文字を書いた部分を「アクセスキー」と呼びます。
アクセスキーを設定していると、Alt + Fを押すとファイルメニューが表示され、Alt + Xを押すとプログラムを終了します。
3-4 テキストボックスをフォームに配置する
ツールボックスの「コモンコントロール」タブの「TextBox」コントロールを
選択し、ドラッグアンドドロップでフォーム上に配置します。
Nameプロパティを「textBoxMain」に変更しましょう。
3-5 リサイズに対応する
Form1_Loadメソッドに下記を追加してください。
//テキストボックスに複数行入力可 textBoxMain.Multiline = true; //垂直スクロールバー textBoxMain.ScrollBars = ScrollBars.Vertical; //使用できる領域を全てにする textBoxMain.Dock = DockStyle.Fill;
3-6 「ファイル」メニューに機能を追加する
図のように「ファイル」メニューの下の階層に「新規」「開く」「上書き保存」「名前を付けて保存」
を追加しましょう。
「開く」と「名前を付けて保存」の後ろに半角ピリオドが3つ「…」付いているのは、Windowsアプリケーションの約束事で、選択した後にダイアログボックス(問い合わせ画面)で何らかの作業が必要であることを示しています。
「終了」は最初に作りましたので順番を一番下に移動させます。項目を移動させるには「終了」項目をドラッグアンドドロップで順番を変えることができます。
Nameプロパティ Textプロパティ MenuItemFileNew 新規(&N) MenuItemFileOpen 開く(&O)... MenuItemFileSave 上書き保存(&S) MenuItemFileSaveAs 名前を付けて保存(&A)... MenuItemFileExit 終了(&X)
3-7 セパレータを挿入する
メニュー項目の間にはセパレータ(区切り線)を使うことでメニュー項目をより見やすくできます。
セパレータを挿入するには選択された項目の上部に挿入するので、今回は「終了」項目を右クリックし「挿入」を呼び出し「Separator」を選択します。
セパレータの名前は「MenuItemFileSeparator1」に変更しましょう。
3-8 ショートカットキーを設定する
「新規」のプロパティの「ShortCutKeys」項目の選択欄ほ開いて修飾子に「Ctrl」にチェックしキーに「N」を選択します。
同様に「開く」は、「Ctrl」にチェックしキーに「O」を選択します。
「上書き保存」は、「Ctrl」にチェックしキーに「S」を選択します。
すると、フォームの「ファイル」メニュー内の項目にショートカットキーの内容が自動で表示されます。
3-9 「開く」機能を作る
ツールボックスの「ダイアログ」タブにある「OpenFileDialog」をフォームに貼り付けます。名前は作成されたままの「openFileDialog1」とします。
「開く」メニュータブをクリックして「MenuItemFileOpen_Clickメソッドを作り{ }の中に(1)~(3)のコードを記述してください。
private void MenuItemFileOpen_Click(object sender, EventArgs e) { openFileDialog1.FileName = ""; //(1) openFileDialog1.ShowDialog(); //(2) LoadFile(openFileDialog1.FileName); //(3) } private void LoadFile(string value) //(4) { textBoxMain.Text = System.IO.File.ReadAllText( value, System.Text.Encoding.GetEncoding( "Shift_JIS")); textBoxMain.Select(0, 0); }
(1)はopenFileDialog1.FileNameを空欄で初期化します。
(2)はダイアログを表示させます。
(3)はダイアログで選択されたファイル名FileNameを(4)のFileNameメソッドへ渡します。
(4)のLoadFileメソッドは全てキーボードで入力します。内部の処理はReadAllTextメソッドを
使ってテキストファイルの内容を読み込み、textBoxMain.Textへ格納します。
textBoxMain.Select(0, 0);はテキストボックスなお内容が全選択されないための処理です。
実行してメニューから「ファイル」→「開く」を選択すると画像のようにダイアログが表示されます。
3-10 ダイアログのキャンセル時の処理を追加する
今のままでは「開く」ダイアログをキャンセルしたときにエラーとなってしまいます。
ユーザーが「キャンセル」ボタンを押せばFileNameプロパティにファイル名が入ってきませんので(2)と(3)のコードを修正します。下記のように書き換えてください。(if文追加)
if (DialogResult.OK == openFileDialog1.ShowDialog()) LoadFile(openFileDialog1.FileName);
3-11 「名前を付けて保存」機能を作る
ツールボックスの「ダイアログ」タブにある「SaveFileDialog」をフォームに貼り付けます。名前は作成されたままの「SaveFileDialog1」とします。
メニュー項目の「名前を付けて保存」をダブルクリックしてMenuItemFileSaveAs_Clickメソッドを作成し下記コードを記載します。
private void MenuItemFileSaveAs_Click(object sender, EventArgs e) { if (DialogResult.OK == saveFileDialog1.ShowDialog()) //(5) SaveFile(saveFileDialog1.FileName); //〃 } private void SaveFile(string value) //(6) { System.IO.File.WriteAllText(value, textBoxMain.Text, //(7) System.Text.Encoding.GetEncoding("Shift_JIS")); //〃 }
(5)はダイアログを表示して「OK」を押したら(6)のSaveFileメソッドを呼び出します。
(6)は全てコード記載します。(5)で呼び出されるメソッドです。
(7)はWriteAllTextメソッドを使ってテキストボックスの内容を保存します。
文字のエンコードは「Shift_JIS」に指定します。テキストエディタで読み込んだとき文字化けする
かもしれませんがアプリを実行すると正しく表示されます。だめなときは(“UTF-8”)に変えて試してみましょう。
3-12 ダイアログに「ファイルの種類」表示する
名前を付けて保存を押したときのダイアログの「ファイルの種類」が空欄になっているので拡張子「.txt」になるように下記コードをFile_Loadメソッドに追記します。
saveFileDialog1.Filter = "テキスト文書|*.txt|すべてのファイル|*.*";
実行すると図のようにファイルの種類が表示されます。
3-13 名前を付けて保存のファイル名を表示させる
Form1クラスの先頭部分に下記コードを追加します。
private string FileName = "";
LoadFileメソッドの最後と、SaveFileメソッドの最後に下記を追加します。
FileName = value;
3-14. タイトルバーにファイル名を表示する
C#が持つ「プロパティ」をつかうと、タイトルバーにファイル名を表示することが簡単にできます、class Form1の上部に下記コードを追加しましょう。
private string FileNameValue; //(8) private string FileName //(9) { get { return FileNameValue; } //(10) set { //(11) string s = ApplicationName; if (value != "") s += " - " + value; this.Text = s; //(12) FileNameValue = value; //(13) } }
(8)のFileNameValueは、プロパティの裏側にあって、値を保持する変数です。
ほかの部分からは、必ずプロパティを介して読み書きすると決め、このFileNameValueを直接扱わないようにします。
(9)がFileName というプロパティです。(元々記載したprivate string FileName = “”; は削除します)FileNameの値を参照したときは(10)のgetアクセサが呼び出されFileNameValueの値を戻します。
FileNameの値を変更したときは(11)のsetアクセサが呼び出され、変数sにアプリの名前を入れ
受け取ったvalueに中身があれば、半角ハイフンをvalueの中身をsに追加します。
(12)でタイトルバーにsを表示します。
(13)でvalueの値をFileNameValueにセットします。
これで、Form1_Loadメソッドの「this.Text = ApplicationName;」を削除し代わりに下記を追記します。これはFileNameメソッドが子に時点では初期化できないのでForm1_Loadメソッドに初期化のコードを追加する必要があるためです。
FileName = "";
実行してみると、下記図のようにフォームのタイトルバーにファイル名が表示されます。
3-15. 上書き保存が可能なときだけ有効にする
上書き保存は「開く」で読み込んだファイル名がすでに決まっているときだけ有効にします。
(11)のFileNameプロパティのsetアクセサの内容を下記に変更します。
set { string s = ApplicationName; if (value != "") { s += " - " + value; MenuItemFileSave.Enabled = true; //(14) } else { MenuItemFileSave.Enabled = false; //(15) } this.Text = s; FileNameValue = value; }
(14)ではファイル名が指定されているときはMenuItemFileSaveのEnabledプロパティをtrueに設定し上書き保存ができるようにします。
(15)ではファイル名が指定されていないのでメニュー項目を無効にします。
フォームデザインを開き「ファイル」→「上書き保存」の項目をダブルクリックし
MenuItemFileSave_Clickメソッドを作成し下記コードを追記します。
SaveFileメソッドを呼び出しファイル名に上書き保存します。
SaveFile(FileName);
3-16. 変更がなかれば保存しない
変更があったかどうかを記録し、それをタイトルバーに表示し変更があった場合だけ保温の機能を有効にします。
下記のコードを追加・修正しましょう。このあと説明します。
private string FileNameValue; //(16) private string FileName //(17) { get { return FileNameValue; } set { FileNameValue = value; //(18) Edited = false; //(19) } } private bool EditedValue; //(20) private bool Edited //(21) { get { return EditedValue; } set { EditedValue = value; UpdateStatus(); //(22) } } private void UpdateStatus() //(23) string s = ApplicationName; //(24) if (FileName != "") s += " - " + FileName; if (Edited) s += "(変更あり) "; //(25) this.Text = s; //(24) if (FileName == "" || !Edited) //(26) MenuItemFileSave.Enabled = false; else MenuItemFileSave.Enabled = true; if (!Edited) //(27) MenuItemFileSaveAs.Enabled = false; else MenuItemFileSaveAs.Enabled = true; }
(16)のFileNameValue変数は変更ありません。
(17)のFileNameプロパティは処理を(23)のUpdateStatusメソッドに移しスッキリしました。
(18)のsetアクセサはFileNameValueの値を変更します。
(19)でこれから記載するEditedプロパティをfalseにします。FileNameが更新されるのは新規作成、読み込み、保存をしたときだけでありその際は編集されているかを示すEdited
プロパティはfalse(偽)にセットします。
(20)のEditedValueはEditedプロパティの値を保持する変数です。
(21)のEdited プロパティは値が変更された場合に、(22)の UpdateStatusメソッドを呼び出します。
(23) は(22)で呼び出されるUpdateStatusメソッドです。
(24)でタイトルバーの表示を作ります。
(25)でEditedがtrueのときにタイトルバーに「(変更あり)」と表示します。
(26)で上書き保存の有効無効の判断をし、編集されていなければ無効にします。
(27)で編集されていなければ、「名前を付けて保存」を無効に、編集済みであれが有効にします。
次に、Form1デザイナーでtextBoxMainをダブルクリックしてtextBoxMain_TextChangedメソッドを作り下記コードを追記します。
Edited = true;
3-17. 保存するデータがあるかチェックする
保存するデータがないのに保存しようとすると大きさゼロのファイルが作成されてしまいます。前項で作成したコードを修正し、textBoxMainが持つテキストの長さがゼロである場合は保存を無効にします。
(26)のif (FileName == “” || !Edited) を下記に書き換えます。
if (FileName == "" || !Edited || textBoxMain.TextLength == 0)
(27)のif (!Edited)を下記に書き換えます。
if (!Edited || textBoxMain.TextLength == 0)
3-18. 内容の破棄の注意を促す
ユーザーが作業中うっかりフォームを閉じようとした場合に、内容の破棄を促すようにします。フォームが閉じるときのFormClosingイベントのハンドラを作成します。FormClosingは標準イベントではないのでプロパティウインドウから作成します。
プロパティウインドウのカミナリマークの「イベント」アイコンをクリックし、「FormClosing」を選択したあと、その項目をダブルクリックすると(28)のForm1_FormClosingメソッドが自動作成され(29)を記載します。これから記載する(30)のAskGiveUpTextメソッドを呼び出します。
(30)のAskGiveUpTextはユーザーに編集内容を破棄するかどうか尋ねるメソッドです。
(31)はEdited がfalseかデータがなければtrueを返しこのメソッドを抜けます。編集されていないので尋ねる必要がないからです。
(32)のif文は「編集内容を破棄しますか?」のダイアログを表示します。
(33)はDialogResult.Yesである場合はtrueを返しいいえであればfalseを返します。
private void Form1_FormClosing( object sender, FormClosingEventArgs e) //(28) { if (!AskGiveUpText()) e.Cancel = true; //(29) } private bool AskGiveUpText() //(30) { if (!Edited || textBoxMain.TextLength == 0) //(31) return true; if (DialogResult.Yes == MessageBox.Show("編集内容を破棄しますか?", //(32) ApplicationName, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)) return true; //(33) else return false; }
ここまで記述したら保存して実行してみましょう。
何か文字を入力して、終了を選択するか、フォームの×マークで閉じようとすると図のダイアログが表示され「はい」を押すと終了し、「いいえ」を押すと終了がキャンセルされ編集が続行されます。
(30)で記述したAskGiveUpTextメソッドは「ファイル」メニューの「新規」イベントでも利用ができます。Form1デザイナーを開き「ファイル」メニュー「新規」項目をダブルクリックしMenuItemFileNew_Clickメソッドを作成し、下記コードを記述しましょう。
private void MenuItemFileNew_Click(object sender, EventArgs e) { if (!AskGiveUpText()) return; //(34) textBoxMain.Clear(); //(35) FileName = ""; //(36) }
(34)でAskGiveUpTextメソッドを呼び出しユーザーの判断を仰ぎます。
(35)でtextBoxMainの内容を消します。
(36)でファイル名(FileName)を消去します。
同様に「ファイル」メニュー「開く」項目のMenuItemFileOpen_Clickメソッドにも下記コードを追加します。
if (!AskGiveUpText()) return;
3-19. 保存しないファイルを開かないようにする
ここでは何も作成しません。コモンダイアログのコンポsdーネントが標準状態でその機能をすでに持っているからです。実際に「MyNote」アプリを起動しメニューの「ファイル」→「開く」を選択してファイル選択のダイアログを表示し、ファイル名の入力欄に、存在しないファイル名を入力して「開く」ボタンを押すと図のように「ファイルが見つかりません。ファイル名を確認して再実行してください」というメッセージが表示されます。
これはOpenFileDialogコンポーネントのCheckFileExistsプロパティが標準状態でtrueになっているためです。
3-20. 上書き保存の警告を表示させる
今度はMyNoteを起動し適当に文字を入力し、メニュー「ファイル」→「名前を付けて保存」を選択し保存しようとすると、図のように「既に存在します。上書きしますか?」というダイアログが表示されます。これは、SaveFileDialogコンポーネントのOverwritePromptプロパティが標準でtrueとなっているためです。これをfalseにすると上書き保存の警告はでなくなります。用途に合わせ切り替えてみましょう。
3-21. 拡張子は入力しなくても「.txt」が付く
名前を付けて保存ダイアログで「test」のようにファイル名を入力して保存すると、ドットと「txt」という拡張子が付けれれて「test.txt」といいう名前になります。また「ファイルの種類」で「すべてのファイル(*.*)」を指定すると拡張子なしのファイルを作成することもできます。
3-22. ユーザーが使っているフォルダーを覚える
ユーザーが使っているフォルダを保存して復元しましょう。
ここではWindowsの設定データベースである「レジストリ」を使用します。
(37)と(38)をclass Form1の先頭部分に追記します。
また、FileNameプロパティのsetアクセサに(39)を追記します。
const string RegistryKey = @"C#_Apps\VisualStudio\" + ApplicationName; //(37) private string FilePath; //(38) private string FileNameValue; private string FileName { get { return FileNameValue; } set { FileNameValue = value; if (value != "") FilePath = System.IO.Path.GetDirectoryName(value); //(39) Edited = false; } }
(37)はレジストリのどの場所を使うかを決める定数文字列(RegistryKey)です。
(38)のFilePathはフォルダーを保持する変数です。FileNameプロパティが更新されたとき、FileNameの値が空欄でなければ(39)でFilePathを更新します。
下記はフォルダーの情報をレジストリに保存して復元するコードです。Form1_Loadメソッドに追加します。
Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(RegistryKey); //(40) FilePath = regKey.GetValue("FilePath", System.Environment.GetFolderPath( Environment.SpecialFolder.MyDocuments)).ToString(); //(40)
(40)では起動時にレジストリからFilePathに格納するコードです。レジストリにFilePathの値がない場合(初回時はそうなります)は「MyDocument」をいれます。
下記コードを「MenuItemFileOpen_Click」メソッドと「MenuItemFileSaveAs_Click」メソッドに追加します。「開く」と「名前を付けて保存」のダイアログを表示する前にInitialDirectoryプロパティにFilePathを代入します。
openFileDialog1.InitialDirectory = FilePath;
次にフォームデザインを開きプロパティウインドウのイベントから「FormClosed」を選択しその項目をダブルクリックし「Form1_FormClosed」メソッドを作成し下記コードを記載します。
Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey( RegistryKey); regKey.SetValue("FilePath", FilePath); //(41)
(41)はフォームが閉じられた時に呼び出される「Form1_FormClosed」メソッドです。レジストリにFilePathの値を書き込む処理です。
3-23. 字の大きさを見やすいように変更する
初期設定ではテキストボックスのfontは「MS UI Gothic」の9ポイントとなっていて字が小さく醜いのでプロパティで変更します。フォームデザイナーを開きTextBoxMainのプロパティウインドウのFont項目を「MS ゴシック」の太字の12ポイントに設定します。(適切な文字サイズに変えてみましょう)
3-24. 「設定」メニューを作りフォント設定ができるようにする
字の大きさをユーザーが設定できるようにします。フォームデザイナーを開き「設定」メニューを作りNameプロパティを「MenuItemSetting」にしTestプロパティを「設定(&S)」としアクセスキーも設定します。その下の項目に「フォント」メニュを作りNameプロパティを「MenuItemSettingFont」にしTestプロパティを「フォント(&F)」としこちらもアクセスキーも設定します
続いてフォームにツールボックスの「ダイアログ」の中の「FontDialog」コンポーネントを貼り付けます。名前は自動的に作成される「fontDialog1」のままにします。
「設定」→「フォント」メニューをダブルクリックしイベントハンドラである「MenuItemSettingFont_Click」を作成し下記(42)(43)(44)のコードを記載します。
private void MenuItemSettingFont_Click(object sender, EventArgs e) { fontDialog1.Font = textBoxMain.Font; //(42) if (DialogResult.OK == fontDialog1.ShowDialog()) //(43) textBoxMain.Font = fontDialog1.Font; //(44) }
(42)でtextBoxMain.FontをfontDialog1.Fontに代入し、現在の設定がフォント設定ダイアログに反映されるようにします。
(43)でフォント設定ダイアログを開き、戻り値がtrueであれば(44)でフォント設定ダイアログでの設定内容をtextBoxMain.Fontに代入します。このフォント設定は後でレジストリに保存し印刷にも使えるようにします。
3-25. 不要なフォント設定を表示しない
Form1_Loadメソッドに下記コードを追加します。
fontDialog1.ShowEffects = false; //(45) fontDialog1.AllowScriptChange = false; //(46)
(45)のfontDialog1.ShowEffectをfalseにすることで「文字飾り」を消すことができます。
(46)のfontDialog1.AllowScriptChangeをfalseにすることで「文字セット」の項目を日本語以外に変更できなくなります。
3-26. フォント設定を記憶させる
「フォント」ダイアログでユーザーがフォント設定をできるようになりましたが起動のたびに設定しなおすのは大変です。フォント設定をレジストリに保存し、起動時に復元するようにします。Form1_FormClosedメソッドに下記コードを追記します。
上から「フォント名」「フォントサイズ」「太字」「斜体」の設定となります。
regKey.SetValue("FontName", textBoxMain.Font.Name); regKey.SetValue("FontSize", textBoxMain.Font.Size); regKey.SetValue("FontBold", textBoxMain.Font.Bold); regKey.SetValue("FontItalic", textBoxMain.Font.Italic);
次に、フォントの設定をレジストリから復元する処理です。Form1_Loadメソッドに下記コードを追記します。
string name = //(45) regKey.GetValue("FoneName", "MS ゴシック").ToString(); Single size = Single.Parse(regKey.GetValue("FontSize", 12).ToString()); bool bold = bool.Parse(regKey.GetValue("FontBold", false).ToString()); bool italic = bool.Parse(regKey.GetValue( "FontItalic", false).ToString()); //(45) System.Drawing.FontStyle style = new System.Drawing.FontStyle(); //(46) if (bold) style = System.Drawing.FontStyle.Bold; //(47) if (italic) style = style ^ System.Drawing.FontStyle.Italic; //(48) textBoxMain.Font = new System.Drawing.Font(name, size, style); //(49)
(45)ではレジストリから読み取った値を一時的な変数(name)に格納します。
(46)のstyleも一時的な変数です。
(47)では変数boldの値を使ってstyleを設定します。
(48)では既に設定した太字かどうかの設定はそのままで斜体であるかどうかの設定をします。
(49)以上の準備によりtextBoxMainのFontプロパティを設定できます。
3-27. ウインドウが小さくなり過ぎないようにする
フォームのサイズが必要以上に小さくならないように設定します。Form1_Loadメソッドに下記コードを追加します。
const int initialWidth = 400; const int initialHeight = 200; this.MinimumSize = new System.Drawing.Size( initialWidth, initialHeight);
3-28. ウインドウの位置と大きさを記憶させる
フォームを閉じたときの位置と大きさをレジストリに保存します。Form1_FormClosedメソッドの最後に下記コードを追記します。(上から左、先頭、幅、高さ)
regKey.SetValue("Left", DesktopBounds.Left); regKey.SetValue("Top", DesktopBounds.Top); regKey.SetValue("Width", DesktopBounds.Width); regKey.SetValue("Height", DesktopBounds.Height);
次はウィンドウの位置と大きさを復元するコードです。Form1_Loadメソッドに下記コードを追記します。
const int initialLeft = 100; const int initialTop = 100; int l = int.Parse(regKey.GetValue("Left", initialLeft).ToString()); int t = int.Parse(regKey.GetValue("Top", initialTop).ToString()); int w = int.Parse(regKey.GetValue("Width", initialWidth).ToString()); int h = int.Parse(regKey.GetValue("Height", initialHeight).ToString()); if (l < Screen.GetWorkingArea(this).Left || //(50) l >= Screen.GetWorkingArea(this).Right) l = initialLeft; if (t < Screen.GetWorkingArea(this).Top || t >= Screen.GetWorkingArea(this).Bottom) t = initialTop; //(50) this.SetDesktopBounds(l, t, w, h);
(50)のif文2つでウインドウの左上が表示されない場合に位置を初期化するコードです。
ただし、画素数が変わったときの対応がうまくできない場合があります。
また、今回の実装ではタスクバーの部分を除外するようにDesktopBoundsメソッド、GetWorkingAreaメソッド、SetDesktopBoundsメソッドを使っていますがタスクバーをどこにどんな大きさで置いてよいのか難しいという問題があります。
3-29. 「編集」メニューを作る
フォームデザイナーを開きメニューに「編集」の項目を作成します。
作成した項目のTextプロパティ、Nameプロパティ、ShortCutKeysプロパティを下記のように変更します。
Text | Name | ShortCutKey |
---|---|---|
編集(&E) | MenuItemEdit | |
元に戻す(&U) | MenuItemEditUndo | Ctrl+Z |
MenuItemEditSeparator1 | ||
切り取り(&X) | MenuItemEditCut | Ctrl+X |
コピー(&C) | MenuItemEditCopy | Ctrl+C |
貼り付け(&P) | MenuItemEditPaste | Ctrl+P |
削除(&L) | MenuItemEditDelete | Del |
MenuItemEditSeparator2 | ||
すべて選択(&A) | MenuItemEditSelectAll | Ctrl+A |
3-30. 元に戻すアンドゥ(Undo)機能をつくる
Windowsアプリケーションでは元に戻すときはショートカットキーCtrl+Zに割り当てます。TextBoxコントロールのUndoメソッドを呼び出すコードです。フォームデザインの「編集」→「元に戻す」項目をクリックしMenuItemEditUndo_Clickを作成し下記コードを記述します。
private void MenuItemEditUndo_Click(object sender, EventArgs e) { textBoxMain.Undo(); }
3-31. 切り取り、コピー、貼り付け機能をつくる
TextBoxコントロールがもともと持っている機能を使います。
フォームメニューの「切り取り」「コピー」「貼り付け」項目をクリックしそれぞれのメソッドを作成し下記コードを記述します。
private void MenuItemEditCut_Click(object sender, EventArgs e) { textBoxMain.Cut(); } private void MenuItemEditCopy_Click(object sender, EventArgs e) { textBoxMain.Copy(); } private void MenuItemEditPaste_Click(object sender, EventArgs e) { textBoxMain.Paste(); }
3-32. 「削除」メニューをつくる
フォームメニューの「削除」項目をクリックしMenuItemEditDelete_Clickメソッドを作成し下記コードを記述します。ただしこのままでは範囲指定していない場合はDeleteキーで1文字削除することができません。以降で対策します。
private void MenuItemEditDelete_Click(object sender, EventArgs e) { textBoxMain.SelectedText = ""; }
3-33. 「すべて選択」メニューをつくる
フォームメニューの「すべて選択」項目をクリックしMenuItemEditSelectAll_Clickを作成し下記コードを記述します。
private void MenuItemEditSelectAll_Click(object sender, EventArgs e) { textBoxMain.SelectAll(); }
3-34. 「編集」メニューの不要な機能は無効にする
「編集」メニューの中身の有効と無効を制御する機能をつくります。「編集」メニューを選択した状態で、プロパティウインドウのイベントの「DropDownOpening」項目をダブルクリックしMenuItemEdit_DropDownOpeningメソッドを作成します。
そのメソッドに下記コードを記述します。これは「編集」メニューがドロップダウンするときに呼び出されます。
private void MenuItemEdit_DropDownOpening(object sender, EventArgs e) { //(51) MenuItemEditPaste.Enabled = Clipboard.ContainsText(); //(52) bool b = textBoxMain.SelectionLength == 0; //(53) MenuItemEditCut.Enabled = !b; //(53) MenuItemEditCopy.Enabled = !b; //(53) MenuItemEditDelete.Enabled = !b; //(53) b = textBoxMain.TextLength == 0; //(54) MenuItemEditSelectAll.Enabled = !b; //(54) }
(52)で「貼り付け」、(53)で「切り取り」「コピー」「削除」を(54)で「すべて選択」を制著します。
(51)のMenuItemEdit_DropDownOpeningメソッドは起動時に一度呼び出す必要があります。Form1_Loadメソッドの最後に下記コードを追記します。
MenuItemEdit_DropDownOpening(sender, e);
先ほどと同様の手順で「編集」メニューを選択した状態で、プロパティウインドウのイベントの「DropDownClosed」項目をダブルクリックしMenuItemEdit_DropDownOpeningメソッドを作成します。
作成されたメソッドに下記コードを記述します。「編集」メニューのドロップダウンが閉じられた時に呼び出されるメソッドで、MenuItemEditDelete.Enabledを無効にします。
private void MenuItemEdit_DropDownClosed(object sender, EventArgs e) { MenuItemEditDelete.Enabled = false; }
3-35. 「ヘルプ」メニューをつくる
フォームデザイナーを開きメニューに「ヘルプ」の項目を作成します。
作成した項目のTextプロパティ、Nameプロパティ、ShortCutKeysプロパティを下記のように変更します。
Text | Name | ShortCutKey |
---|---|---|
ヘルプ(&H) | MenuItemHelp | |
README.TXTの表示(&R) | MenuItemHelpReadMe | Ctrl+R |
Webサイトの表示(&W) | MenuItemHelpWeb | Ctrl+W |
バージョン情報(&V) | MenuItemHelpVersion | Ctrl+V |
3-36. テキストファイル(README)を表示する
フォームデザインの「ヘルプ」→「README.TXTの表示」項目をダブルクリックしMenuItemEditHelpReadMe_Clickメソッドを作成し下記コードを記述します。
private void MenuItemEditHelpReadMe_Click(object sender, EventArgs e) { string s = System.IO.Path.GetDirectoryName( Application.ExecutablePath); //(55) s = System.IO.Path.Combine(s, "README.TXT"); //(55) if (System.IO.File.Exists(s)) //(56) System.Diagnostics.Process.Start(s); //(57) else MessageBox.Show(s + "が見つかりません", ApplicationName); //(58) }
(55)でMyNoteの実行ファイルがあるフォルダ(ディレクトリ)を取得し、それに「README.TXT」というファイル名を連結して、文字列sに格納します。
(56)のif文でそのファイルが存在しているかを調べ、存在すれば(57)で開きます。ユーザーが拡張子「TXT」に関連付けしているプログラムがREADME.TXTを開いて起動します。README.TXTが見つからない場合は(58)でエラーメッセージを出力します。
3-37. Webサイトを表示する
ホームページを表示する機能をつくります。
フォームデザインの「ヘルプ」→「Webサイトの表示」項目をダブルクリックしMenuItemHelpWeb_Clickメソッドを作成し下記コードを記述します。
private void MenuItemHelpWeb_Click(object sender, EventArgs e)
{
System.Diagnostics.Process.Start(“https://sumiox.com/”);
}
3-38. バージョン情報を表示する
プログラムのバージョン情報と作った年と作った人をダイアログボックスで表示します。
フォームデザインの「ヘルプ」→「バージョン情報」項目をダブルクリックしMenuItemHelpVersion_Clickメソッドを作成し下記コードを記述します。
private void MenuItemHelpVersion_Click(object sender, EventArgs e) { MessageBox.Show(ApplicationName + " 0.01)" + Environment.NewLine + "(C)2023 sumiox.com", "バージョン情報"); }
3-39. 印刷機能を追加する
フォームデザイナーを開き、「ファイル」メニューに項目を追加します。
Text | Name | ShortCutKey |
---|---|---|
印刷プレビュー(&V)… | MenuItemFilePrintPreview | |
印刷(&P)… | MenuItemFilePrint | |
MenuItemFileSeparator2 |
次に、ツールボックスの「印刷」タブにあるPrintDocumentコンポーネントをフォーム上に貼り付けます。Nameプロパティは作成されたままの「PrintDocument1」のままにしておきます。
これから印刷機能のコードを実装していきます。「印刷」をクリックしたときのイベントハンドラと、クラスレベルの変数PrintString、「印刷」イベントハンドラから呼び出メソッドを2つ「SetPrintDocument1」と「printDocument_PrintPage」を記述していきます。
まず、フォームデザインの「ファイル」→「印刷」項目をダブルクリックしMenuItemFilePrint_Clickメソッドを作成し下記コードを記述します。
private void MenuItemFilePrint_Click(object sender, EventArgs e) { SetPrintDocument1(); //(59) printDocument1.Print(); //(60) } //(61)
次に、下記コードを先ほどの(61)のコードの下にすべて記述します。
private string PrintString; //(62) private void SetPrintDocument1() //(63) { PrintString = textBoxMain.Text; //(64) printDocument1.DefaultPageSettings.Margins = new System.Drawing.Printing.Margins(20, 60, 20, 60); //(65) printDocument1.DocumentName = FileName; //(66) } private void printDocument_PrintPage (object sender, System.Drawing.Printing.PrintPageEventArgs e) //(67) { int charactersOnPage = 0; //(68) int linesPerPage = 0; //(69) e.Graphics.MeasureString(PrintString, textBoxMain.Font, //(70) e.MarginBounds.Size, System.Drawing.StringFormat.GenericTypographic, out charactersOnPage, out linesPerPage); //(70) e.Graphics.DrawString(PrintString, textBoxMain.Font, //(71) System.Drawing.Brushes.Black, e.MarginBounds, System.Drawing.StringFormat.GenericTypographic); //(71) PrintString = PrintString.Substring(charactersOnPage); //(72) if (PrintString.Length > 0) //(73) e.HasMorePages = true; //(74) else { e.HasMorePages = false; //(75) PrintString = textBoxMain.Text; //(76) }
(61)は「印刷」をクリックしたときのイベントハンドラです。
(59)で(63)のSetPrintDocument1メソッドを呼び出します。このメソッドは印刷の準備をする処理をまとめたものです。
(60)で先ほどフォームに貼り付けたprintDocument1のPrintメソッドを呼び出しここで印刷を開始します。
(62)は印刷すべき文字列を一時的に格納するクラスレベルの変数PrintStringを宣言します。
(63)は(59)で呼び出されるメソッドです。
(64)はtextBoxMain.Textを変数PrintStringへ代入します。
(65)はマージンを、(66)でドキュメント名を設定します。
(67)のprintDocument_PrintPageメソッドが、1ページごとに呼ばれます。
(68)は、この1ページで印刷される文字数を格納する変数charactersOnPageを宣言します。
(69)は、この1ページで印刷される行数を格納する変数linesPerPageを宣言します。
(70)はPrintStringをtextBoxMain.Fontを使って、e.MarginBounds.Sizeの領域に印字した場合、何文字何行になるのかを設定し、それをcharactersOnPage(何文字)とlinesPerPage(何行)に格納します。
(71)で黒色Brushes.Blackで印字します。
(72)でPrintStringから今印刷した文字列を取り除くことで次のページで続きが印刷できるようになります。
(73)のif文ではPrintStringの中身があれば、(74)でe.HasMorePages をtrueにします。PrintStringの中身がなければ(75)でe.HasMorePages をfalseにし印刷を終了し(76)でPrintString にtextBoxMain.Textを代入します。(76)はここの時点では必要がありませんが、後ほど実装する印刷プレビューで使います。
3-40. 印刷ダイアログを表示させる
フォームデザインを開き、ツールボックスの「印刷」タブにあるPrintDialogコンポーネントをフォーム上に貼り付けます。Nameプロパティは作成されたままの「PrintDialog1」のままにしておきます。
Form1_Loadの最後に下記コードを追記します。
printDialog1.Document = printDocument1;
次に、フォームデザインの「ファイル」→「印刷プレビュー」項目をダブルクリックしMenuItemFilePrintPreview_Clickメソッドを作成し下記コードを記述します。
private void MenuItemFilePrintPreview_Click(object sender, EventArgs e) { if (DialogResult.OK == printDialog1.ShowDialog()) //(77) { SetPrintDocument1(); printDocument1.Print(); } }
(77)のif文で下記図のようなダイアログが表示されます。
3-41. 印刷プレビューを表示させる
フォームデザインを開き、ツールボックスの「印刷」タブにあるPrintPreviewDialogコンポーネントをフォーム上に貼り付けます。Nameプロパティは作成されたままの「PrintPreviewDialog1」のままにしておきます。
先ほどと同様にForm1_Loadの最後に下記コードを追記します。
printPreviewDialog1.Document = printDocument1;
今度は、先ほど記述したMenuItemFilePrintPreview_Clickメソッドのif文 (DialogResult.OK == printDialog1.ShowDialog()を削除し下記のように編集します。
private void MenuItemFilePrintPreview_Click(object sender, EventArgs e) { SetPrintDocument1(); //(78) printPreviewDialog1.ShowDialog(); //(79) }
(78)でSetPrintDocument1メソッドを呼び出し印刷の設定を行います。
(79)でprintPreviewDialog1が呼び出され印刷プレビューが表示されます。
印刷関連メニューの有効・無効を制御するためUpdateStatusメソッドの最後に下記コードを追加します。これはtextBoxMainのTextLengthがゼロかどうかをtrueかfalseを変数bに格納し、それをつかってメニュー項目の有効無効を制御しています。
bool b = textBoxMain.TextLength == 0; MenuItemFilePrint.Enabled = !b; MenuItemFilePrintPreview.Enabled = !b;
ちなみにVisualStudioのForm1.csファイルを開いた状態でCtrl+Fで文字列を検索できます。コードが長くなったときに文字検索するときに便利です。
3-42. テキストボックスの文字数を表示する
テキストボックスに入力された文字数をラベルに表示させます。フォームデザインを開き、ツールボックスの「コモン コントロール」タブにあるLabelコンポーネントをフォーム上に貼り付けます。Nameプロパティは「LabelTextLength」に変更します。
テキストボックスの文字数をラベルに表示するTextLengthを作成します。下記コードをclass Form1の最後に追加します。
private void TextLength() { int textLength = textBoxMain.Text.Length; //(80) labelTextLength.Text = "文字数= " + textLength.ToString(); //(81) }
(80)でtextBoxMainのText.Length(文字数)を数値型の変数textLengthを定義します。
(81)でラベルlabelTextLengthに文字列型に変換し代入します。
次に下記のコードをForm1_Loadメソッドの最後とtextBoxMain_TextChangedの最後に追記します。Form1の読み込んだときと、テキストボックスが編集されたときに文字数を表示します。
TextLength();
実行するとフォーム上に文字数が表示されます。
3-43. コードの順番を見直す
文字数の表示が終わったところで今回のアプリケーションの作成は完了しました。ここで、もうひと手間をかけて一度完成したForm1クラス(Form1.cs)のコードを見直してみましょう。見た目をよくすることで、わかりやすく保守しやすいコードにできます。まずはメソッドや変数の順番を整えていきましょう。クラスレベルの定数や変数はクラスの先頭部分にまとめます。その次にプロパティと、関連するメソッドを置きます。続いて、起動時に呼び出されるForm_Loadメソッド、そこから呼び出されるメソッドを置きます。その後のイベントハンドラや関連メソッドもわかりやすいように編集します。また、メソッドの内部を一つひとつ見直し横に長い行があれば改行したりわかりにくい変数名があったら変更することなども大切です。
4. 【アプリのインストーラの作成】
4-1. アイコンをつくる
VisualStudioが既定で入れているアイコンを変更します。「マルチアイコン作成」というソフトウェアを使って、画像(.jpegなど)からアイコンを作成することができます。下記URLへアクセスし、アイコンファイル(拡張子.ico)をダウンロードしPCへ保存します。
下記画像は「マルチアイコン作成」アプリで作成したアイコンです。
4-2. インストーラをつくる
ここまで作ってきたテキストエディタ「MyNote」は、.NET Framework上で動作するWindowsアプリケーションです。別のパソコンで利用する際は実行ファイル「MyNote.exe」をコピーしただけでは動作しません。VisualStudioの「発行」機能を使ってインストーラを作りましょう。まず、プログラムと一緒に配布すべきである「README.TXT」をMyNoteプロジェクトのフォルダーにコピーし「プロジェクト」→「規定項目の追加」で追加します。「出力ディレクトリにコピー」を「新しい場合にコピーする」にしましょう。次に「プロジェクト」→「MyNoteのプロパティ」→「公開」の画面を表示します。「オプション」ボタンを押して「公開オプション」ダイアログを出し「発行者名」「スイート名」「製品名」を設定します。
「公開」画面に戻ったら、「公開フォルダーの場所」の項目でフォルダーを指定します。「公開バージョン」の項目でバージョン番号を設定します。
画面右下の「公開ウィザード」ボタンを押すと下記画面が表示されますので「次へ」ボタンを押して進みます。
さらに次へを押します。
さらに次へを押します。
これで完了ボタンを押すと「公開ウィザード」が完了します。
「公開フォルダーの場所」で指定したフォルダーを見てみると「setup.exe」を含むインストーラのフォルダが作成されました。
4-3. セットアップする
それではインストーラでパソコンにインストールしてみましょう。先ほど作成されたインストーラのフォルダ内の「setup.exe」ファイルをダブルクリックすると下記の画面が表示されます。「インストール」ボタンを押すとインストールが開始されます。
PCのプログラムファイルへ「MyNote」アプリがインストールされます。
ウインドウボタンを押すと、一番上に最近追加されたものに「MyNote」が表示されます。タスクバーやスタートボタンに追加してすぐに使えるようにしましょう。
以上で「MyNote」アプリケーション作成の完成となります。
お疲れさまでした!
基礎からきちんと知りたい人の C#プログラミング入門 (日経BPパソコンベストムック)
コメント