Chapter 03

初めてのアプリケーション

eagle
eagle
2021.12.26に更新

目標

本章では次のように画面上にテキストとボタンを配置し、ユーザーがボタンをクリックしたときにテキストの内容を変化させるアプリケーションを作成します。


成果物

本章では以下の事項を学びます。

  • フォームデザイナの使い方
  • イベントの購読と購読解除
  • デフォルトフォントの設定(.NET 6)

フォームデザイナ

ソリューションエクスプローラーのMainForm.csをダブルクリックし、フォームデザイナを起動します。


フォームデザイナの起動

フォームデザイナ表示中はツールボックスを利用できます。


ツールボックス

もしVisual Studio上にツールボックスが表示されない場合は画面上部の「表示」から「ツールボックス」を選択すると表示させることができます。


ツールボックスの表示

コントロールの配置

画面上のテキストやボタンなどはWindowsフォームでは総称してControlと呼ばれます。
コントロールを作成、表示するにはそれに対応するコードを書く必要がありますが、先ほどのフォームデザイナを使うとその手のC#コードを自動生成することができます。

コントロールを配置するには、ツールボックスからコントロールを選び、フォームデザイナへドラッグ&ドロップします。
試しに、LabelコントロールとButtonコントロールを1つずつ追加してみましょう。


コントロールの配置

配置したラベルを選択し、そのTextプロパティをHello, F#!に変更してみましょう。


Textプロパティの変更

プロパティウィンドウが見つからない場合は「表示」から「プロパティウィンドウ」を選択すると表示させることができます。


プロパティウィンドウの表示

ここまでできたらApp.Mainを実行してみましょう。


実行結果

フォームデザイナで配置した通りにコントロールが表示されていますね!
ただし、ボタンをクリックしても現時点では何も起きません。

アクセス修飾子の変更

デフォルトでは他プロジェクトからフォームのコントロールを参照することができないようになっています。
そこで、フォームデザイナですべてのコントロールのModifierプロパティをPublicに変更します。
アクセス制御に関する詳細はこちらを参照してください。


Modifierプロパティの変更

コントロールを全選択しておくと一括Modifierプロパティを変えることができます。

コントロールの参照

さて、次はコードを書いて動的にコントロールを制御してみましょう。

現時点では、MainプロジェクトのMainForm.fsは以下のようになっています。

MainForm.fs
module MainForm

open App.Designer

type View() =
    inherit MainForm()

ここでは、DesignerプロジェクトのMainForm.csで定義したMainFormクラスを継承し、新たにViewクラスを定義しています。
ここで定義したViewクラスはProgram.fsによって表示されます。

以下のようにコードを追加してみます。

MainForm.fs
module MainForm

open App.Designer

type View() as this =
    inherit MainForm()

    do this.label1.Text <- "Hello, from source code!"

追加されたのはas thisdo this.label1.Text <- "Hello, from source code!"の2箇所です。

as this自己識別子構文です。
これにより、thisを使って先ほどフォームデザイナで配置したコントロールにアクセスできるようになります。

コントロール名はNameプロパティによって決まります。
今回は変更していないのでそれぞれlabel1, button1という名前になっているはずです。
コードからコントロールを参照するときは先ほど定義した自己識別子を使ってthis.label1this.button1などとします。

doはクラスのインスタンスが生成されるときに呼ばれるコンストラクタです。
this.label1.Textとして、先ほどフォームデザイナで変更したTextプロパティを今度はコードから操作できます。
<-破壊的代入の演算子です。

それでは実行してみましょう。


実行結果

フォームデザイナで設定したTextプロパティのHello, F#!が、コードの実行中にHello, from source code!に上書きされていることに注目してください。

クリックイベントの購読

それでは、MainForm.fsにボタンをクリックされたときの処理を書いていきましょう。
さて、ユーザーがボタンをクリックした際の処理を記述しましょう。
これはフォームの見た目、すなわちコントロールの配置とは異なりますので、引き続きMainプロジェクトに記載することとします。

以下のようにMainForm.fsを書き換えます。

MainForm.fs
module MainForm

open App.Designer

type View() as this =
    inherit MainForm()

    do
        this.button1.Click
        |> Observable.subscribe (fun e ->
            this.label1.Text <- "Hello, from source code!")
        |> ignore

this.button1.Clickはクリックイベントを表す値です。
今回はこの値の型をIObservable<EventArgs>と見なせます。
ここでは「Clickの型がIObservable<'T>であり、次に述べるObservable.subscribe関数を使うことができる」という事実が重要です。

厳密には

厳密にはClickの型はIEvent<EventHandler, EventArgs>です。
この型はIObservable<EventArgs>を継承しています。

EventArgsはイベントのデータを表す値の型ですが、特に意味のあるデータを含まないイベントに用いられます。
したがって、「Clickの型がIObservable<'T>であり、次に述べるObservable.subscribe関数を使うことができる」という事実のみ着目すれば十分です。

Observable.subscribeは標準モジュールに定義されている関数です。
この関数は次の2つの引数をとり、イベントを購読することができます。

  1. イベントが起こったときにその都度実行する関数(イベントハンドラ)
  2. イベントを表す値(型IObservable<'T>の値)

funラムダ式(無名関数)を定義するために必要です。
eは引数で、その型はEventArgsですが今回は使いません。
->の後に関数の本体を記述します。本体部分でラベルのテキストを上書きします。

|>パイプライン演算子です。
F#では関数適用f(x)の際にカッコを省略し、間に半角スペースを入れてf xと記述できますが、パイプライン演算子を使うとx |> fとも書けます。
2引数関数適用f x yのことはパイプライン演算子を使うとy |> f xとも書けます。Observable.subscribeではこれを利用しています。

ignoreはコンパイルを通すために便宜上必要です。
詳細はこちらです。

それでは実行してみましょう。


実行結果

ボタンをクリックする前はデザイナで設定したテキストですが、クリックすると動的に上書きされるようになります。

イベントの購読解除

先ほどはコンパイルを通すためにignoreを使用しましたが、これは返り値を無視する演算子です。
一般にignoreは安易に使用するべきではなく、今回も例外ではありません。
Observable.subscribeの返り値の型はIDisposableであり、これは適切なタイミングで破棄する必要があります。

そこで、まずは返り値を無視する代わりに変数subscriptionにバインドしましょう。

MainForm.fs
module MainForm

open App.Designer

type View() as this =
    inherit MainForm()

    let subscription =
        this.button1.Click
        |> Observable.subscribe (fun e ->
            this.label1.Text <- "Hello, from source code!")

このsubscriptionはクリックイベントの購読を表します。
フォームを使い終わったタイミングで一緒にこのイベントの購読も解除することにしましょう。
そのために、フォームのDisposeメソッドオーバーライドします。

MainForm.fs
module MainForm

open App.Designer

type View() as this =
    inherit MainForm()

    let subscription =
        this.button1.Click
        |> Observable.subscribe (fun e ->
            this.label1.Text <- "Hello, from source code!")

    override this.Dispose(disposing) =
        if disposing then subscription.Dispose()

        base.Dispose disposing

ここまででアプリケーションの挙動は変わりませんが、破棄すべきリソースを適切に破棄するようになりました。

フォントサイズの変更

最後に、ボタンのテキストや全体的なフォントを変更して本章を終わりましょう。
ここまで見てきた通り、フォームデザイナで設定する方法と、コード上で設定する方法の2種類があります。
動的に変わらない部分はフォームデザイナで設定するのが簡便で良いでしょう。

まず、ボタンのテキストがbutton1と味気ないですので、ここをクリックなどに変更しておきましょう。
これはフォームデザイナでTextプロパティを変更するのが簡便で良いでしょう。

次に、フォントを変えてみましょう。
フォントはFontプロパティで個別に設定することができます。


フォントの設定

ただし、.NET 6からはデフォルトのフォントを設定することもできますので、その方法を説明します。
今回のプロジェクト構成では、DesignerMainの両方のプロジェクトで設定が必要です。

Designerプロジェクトのデフォルトフォントを変更するにはApp.Designer.csprojを編集します。
そのためにはソリューションエクスプローラーでApp.Designerをダブルクリックします。


App.Designer.csprojの編集

App.Designer.csprojファイルを開いたら、PropertyGroupの中にApplicationDefaultFontを追加します。

App.Designer.csproj
  <Project Sdk="Microsoft.NET.Sdk">
  
      <PropertyGroup>
          <TargetFramework>net6.0-windows</TargetFramework>
          <Nullable>enable</Nullable>
          <UseWindowsForms>true</UseWindowsForms>
          <ImplicitUsings>enable</ImplicitUsings>
+         <ApplicationDefaultFont>メイリオ, 14.25pt</ApplicationDefaultFont>
      </PropertyGroup>
  
  </Project>

これにより、フォームデザイナの表示が変わります。
フォントが先ほどの設定値になり、フォームのサイズも自動的に伸縮されます。
したがって、デフォルトのフォントはプロジェクト初期に決定すべき事項であり、後から変更するのは推奨されないことが分かります。
現時点ではフォーム数1、コントロール数2と影響範囲は小さいですのでこのまま進めます。

フォーム自体のサイズはフォームデザイナ上でフォームの右下隅をドラッグ&ドロップすることで変更できるほか、Sizeプロパティで直接数値を設定することができます。


プロパティウィンドウによるフォームサイズ調整

フォントに関する同様の設定をMainプロジェクトにも行います。
Program.fsファイルを開きます。

Program.fs
  module Program
  
  open System
  open System.Drawing
  open System.Windows.Forms
  
  [<STAThread; EntryPoint>]
  let main argv =
      Application.EnableVisualStyles()
      Application.SetCompatibleTextRenderingDefault false
+     new Font("メイリオ", 14.25f) |> Application.SetDefaultFont
      new MainForm.View() |> Application.Run
      0

fは単精度浮動小数点数を表す数値リテラルです。

アプリケーションの初期設定に関する詳細はこちらを参照してください。

初めてのアプリケーションは以上で完成です。お疲れさまでした。


実行結果