フィールドプロバイダを作成することで、独自の形式のフィールドを利 用できるようになります。このドキュメントでは例としていくつかの フィールドプロバイダを作成していきます。
フィールドに画像を表示してみましょう。
以下の画像をMy.Resource.sampleという名前でリソースに登録しておき ます。
描画を行うRenderメソッドをオーバーライドしたフィールドプロバイダ を作成します。RenderメソッドにはGraphicsオブジェクト、フィールド オブジェクト、フィールドを描画すべき矩形、偶数行か奇数行かが渡さ れます。この例では単にrect全体に画像を描画しています。
Class CImageFieldProvider Inherits CFieldProvider Public Overrides Sub Render(ByVal g As System.Drawing.Graphics, _ ByVal field As systembase.table.UTable.CField, _ ByVal rect As System.Drawing.Rectangle, _ ByVal alter As Boolean) g.DrawImage(My.Resources.sample, rect) End Sub End Class
このフィールドプロバイダを利用するコードは以下のようになります。
Dim r As UTable.CRecord = Me.Table.Content.AddRecord With New CFieldBuilder With r.AddField(0, New CImageFieldProvider, .Next) .Row.Size = 100 .Col.Size = 100 End With End With
カンマ編集、右寄せのフィールドを作成してみましょう。
フィールドプロバイダは以下のようになります。ValueFormatで、表示す るデータのフォーマットを行います。また、Settingメソッドでこのフィー ルドが持つデフォルトのプロパティを指定できます。
Class CNumberFieldProvider Inherits CFieldProvider Public Overrides Function ValueFormat(ByVal v As Object) As String Return Format(v, "#,###") End Function Public Overrides Function Setting() As systembase.table.UTable.CSetting Dim s As New UTable.CSetting s.HorizontalAlignment = systembase.table.UTable.CSetting.EHAlign.RIGHT Return s End Function End Class
利用するコードは次のようになります。このフィールドはユーザからの 入力を受け付ける機能を持たないので、レコードが追加されると同時に 値を設定する例を紹介します。
Dim rp As New UTable.CRecordProvider With New CFieldBuilder rp.AddField(0, New CNumberFieldProvider, .Next) End With Me.Table.Content.SetRecordProvider(rp) Me.Table.CreateCaption() Using Me.Table.UpdateBufferBlock Me.Table.Content.AddRecord().Fields(0).Value = 1 Me.Table.Content.AddRecord().Fields(0).Value = 12 Me.Table.Content.AddRecord().Fields(0).Value = 123 Me.Table.Content.AddRecord().Fields(0).Value = 1234 Me.Table.Content.AddRecord().Fields(0).Value = 12345 Me.Table.Content.AddRecord().Fields(0).Value = 123456 Me.Table.Content.AddRecord().Fields(0).Value = 1234567 Me.Table.Content.AddRecord().Fields(0).Value = 12345678 Me.Table.Content.AddRecord().Fields(0).Value = 123456789 Me.Table.Content.AddRecord().Fields(0).Value = 1234567890 End Using
次のようなことを実現しましょう。
エディタにDateTimePickerを用いて、日付を入力可能とします。この場 合、フィールドプロバイダを作成する前に、エディタを作成する必要が あります。
UTable上で利用されるエディタは、IEditorインターフェースを実装して いなければなりません。ここでは、DateTimePickerを継承してIEditorを 実装します。
Class CDateEditor Inherits DateTimePicker Implements IEditor ... End Class
IEditorの定義は以下のようになっています。
Public Interface IEditor Sub Initialize(ByVal field As UTable.CField) Sub Enter(ByVal key As Char, ByVal hIMC As Integer, ByVal clear As Boolean) Property Value() As Object Function RaiseValidate() As Boolean Function Control() As Control Event ProcessDialogKey(ByVal sender As Object, ByVal keyData As System.Windows.Forms.Keys, ByRef handled As Boolean) Event Leave(ByVal sender As Object, ByVal direction As String) Event ValueChanged(ByVal sender As Object) End Interface
各メンバがどのような意味を持っているかを理解するために、エディタ のライフサイクルについて説明します。
エディタが生成されると、Initializeメソッドが呼ばれます。ここで フィールドに設定されているフォントやアライメントなどのプロパティ をエディタに設定してください。
フィールド上にエディタを出現させるために、Controlにサイズと位置が 設定されます。通常、Controlメソッドは自分自身を返すように実装して ください。
編集モードに入る準備が全て終わると、Enterメソッドが呼ばれます。引 数として、キーボードから最初に入力された文字、またはImeのコンテキ ストハンドルが渡されるので、必要に応じてエディタに設定して下さい。
エディタのProcessDialogKeyメソッドをオーバーライドし、同名のイベ ントを発生させてください。UTableはこのイベントを受け取り、Enterキー などが押された際に編集モードを抜けて次のフィールドへ移動するなど の操作を行います。
エディタ内で値が変更されたら、ValueChangedイベントを発生させてく ださい。
多くの入力コントロールでは、ユーザによる入力が終了し、フォーカス が他のコントロールへ移ろうとすると、Validatingイベントが発生しま す。Validatingイベントハンドラでは入力内容の検証が行われ、もしキャ ンセルされるとフォーカスを移すことはできません。キャンセルされな かった場合は、Validatedイベントが発生し、そのコントロールからフォー カスを移すことができます。
UTableはエディタがControlのValidatingイベントを発生させると、 FieldValidatingイベントを発生させます。クライアントコードはこのイ ベントハンドラを書くことで、Validatingイベントのキャンセルを行う ことができます。キャンセルされずにValidatedイベントが発生すると、 UTableは編集モードを終了します。
編集モードが終わると、編集されたValueをフィールドに設定した後、エ ディタは破棄されます。次回の編集モード時に同じエディタのインスタ ンスが使いまわされることはありません。
編集モードが終了するケースには、エディタからフォーカスが失われる 以外に、次の2つの場合があります。
UTableは編集モードを終了させる必要が生じると、エディタの RaiseValidateメソッドを呼びます。RaiseValidateメソッドは、 ControlのValidatingイベントと(キャンセルされなかったら) Validatedイベントを呼ぶように実装してください。これにより、結果的 に編集モードが終了されることになります。
エディタはLeaveイベントを発生させることで、自発的に編集モードを抜 けることができます。UTableはLeaveイベントを受け取ると、エディタの RaiseValidateメソッドを呼びます。つまり、どのケースでも最終的には エディタがValidatedイベントを発生させることによって、編集モードは 終了されるという仕組みになっています。
大まかな流れを確認したところで、実装をしていきましょう。
Initializeの実装は以下のようになります。DynamicSettingからは、そ のフィールドを含むコンテントやレコードなどから、最も優先度の高い プロパティ値を取得できます。つまり、そのフィールドにいま設定され るべき値を得ることができます。
Public Sub Initialize(ByVal field As UTable.CField) Implements IEditor.Initialize With field.DynamicSetting Me.Font = .Font End With End Sub
Enterの実装は以下のようになります。keyは編集モードに入るときに入 力された文字で、hIMCはIMEのコンテキストハンドルです。エディタの内 容をいったんクリアする必要があるときは、clearにtrueが渡されます。 DateTimePickerは値をクリアすることもIMEによる入力も不可能なので、 単に入力フォーカスを移すだけとします。
Public Sub _Enter(ByVal key As Char, ByVal hIMC As Integer, ByVal clear As Boolean) Implements IEditor.Enter Me.Focus() End Sub
Controlの実装は以下のようになります。
Public Function Control() As System.Windows.Forms.Control Implements systembase.table.IEditor.Control Return Me End Function
Valueの実装は以下のようになります。範囲外の値や不正な型の値が代入 されたときに(剣呑なやりかたですが)無視するようにしています。
Public Property _Value() As Object Implements IEditor.Value Get Return Me.Value End Get Set(ByVal value As Object) Try Me.Value = value Catch ex As Exception End Try End Set End Property
ProcessDialogKeyイベントとValueChangedイベントは次のように実装し ます。
Public Event _ProcessDialogKey(ByVal sender As Object, ByVal keyData As System.Windows.Forms.Keys, ByRef handled As Boolean) Implements IEditor.ProcessDialogKey Public Event _Changed(ByVal sender As Object) Implements IEditor.Changed Protected Overrides Function ProcessDialogKey(ByVal keyData As System.Windows.Forms.Keys) As Boolean Dim handled As Boolean = False RaiseEvent _ProcessDialogKey(Me, keyData, handled) If handled Then Return True Else Return MyBase.ProcessDialogKey(keyData) End If End Function Private Sub CDateEditor_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.TextChanged RaiseEvent _Changed(Me) End Sub
RaiseValidateの実装は以下のようになります。このコードはほとんどの エディタでそのまま利用できます。
Public Function RaiseValidate() As Boolean Implements IEditor.RaiseValidate Dim e As New System.ComponentModel.CancelEventArgs Me.OnValidating(e) If Not e.Cancel Then Me.OnValidated(System.EventArgs.Empty) Return True End If Return False End Function
Leaveの実装は以下のようになります。このエディタからは、Leaveイベ ントは発生しないものとします。
Public Event _Leave(ByVal sender As Object, ByVal direction As String) Implements IEditor.Leave
エディタの準備が整ったので、これを利用するフィールドプロバイダを 作成しましょう。コードは以下のようになります。
Class CDateFieldProvider Inherits CFieldProvider Public Overrides Function Editor() As systembase.table.IEditor Return New CDateEditor End Function Public Overrides Function ValueFormat(ByVal v As Object) As String If TypeOf v Is DateTime Then Return CType(v, DateTime).ToString("yyyy/MM/dd") Else Return Nothing End If End Function End Class
Editorメソッドで、エディタのインスタンスを作成します。 ValueFormatメソッドで、フィールド上に値が表示される際のフォーマッ トを制御します。
これで、全ての準備が整いました。日付入力フィールドを利用するコー ドは次のようになります。
Dim rp As New UTable.CRecordProvider With New CFieldBuilder rp.AddField(0, New CDateFieldProvider, .Next) End With Me.Table.Content.SetRecordProvider(rp) Me.Table.CreateCaption() Using Me.Table.UpdateBufferBlock For i As Integer = 0 To 10 Me.Table.Content.AddRecord() Next End Using
コンボボックスフィールドを作成してみます。
ComboBoxを継承して、IEditorを実装します。
Class CComboEditor Inherits ComboBox Implements IEditor ... End Class
実装すべき内容は、先ほど作成したCDateEditorとほぼ同じなので、異な る部分を中心に見ていきましょう。
Enterメソッドでは、clearが真ならばいったん内容を消します。また、 最初に入力された文字を設定します。
Public Sub _Enter(ByVal key As Char, ByVal hIMC As Integer, ByVal clear As Boolean) Implements IEditor.Enter If clear Then Me.Text = Nothing End If Me.Focus() If key <> Nothing Then Me.Text = key Me.SelectionStart = 1 End If End Sub
Valueプロパティは、Textに対する読み書きを行います。
Public Property _Value() As Object Implements IEditor.Value Get Return Me.Text End Get Set(ByVal value As Object) Me.Text = value End Set End Property
左右両隣のフィールドへフォーカス移動できるように、以下の実装を行 います。カーソルが入力文字列の端にあるとき、隣のフィールドへ向か うカーソルキーが押された場合、Leaveイベントを発生することで編集モー ドを終了し、フォーカス移動を行います。Leaveイベントの引数として、 フォーカスの移動先を決めるコマンドを指定します。デフォルトでは、 [NEXT], [PREV], [UP], [DOWN], [LEFT], [RIGHT]が指定可能です。詳し くはキーボードオペレーションの作成 をご覧下さい。
Protected Overrides Function IsInputKey(ByVal keyData As System.Windows.Forms.Keys) As Boolean Select Case keyData Case Keys.Left If Me.SelectionLength = 0 And Me.SelectionStart = 0 Then RaiseEvent _Leave(Me, "LEFT") Return True End If Case Keys.Right If Me.SelectionLength = 0 And Me.SelectionStart = Me.Text.Length Then RaiseEvent _Leave(Me, "RIGHT") Return True End If End Select Return MyBase.IsInputKey(keyData) End Function
フィールドプロバイダの実装は以下のようになります。エディタが作成 された直後に呼ばれるEditorInitializeでデータソースの設定を行って います。
Class CComboFieldProvider Inherits CFieldProvider Public DataSource As Object Public Sub New(ByVal dataSource As Object) MyBase.New() Me.DataSource = dataSource End Sub Public Overrides Function Editor() As systembase.table.IEditor Return New CComboEditor End Function Public Overrides Sub EditorInitialize(ByVal field As systembase.table.UTable.CField, ByVal editor As systembase.table.IEditor) With CType(editor, ComboBox) .DataSource = Me.DataSource End With End Sub End Class
利用するコードは以下のようになります。
Dim rp As New UTable.CRecordProvider With New CFieldBuilder rp.AddField(0, New CComboFieldProvider(New String() {"AAA", "BBB", "CCC"}), .Next) End With Me.Table.Content.SetRecordProvider(rp) Me.Table.CreateCaption() Using Me.Table.UpdateBufferBlock For i As Integer = 0 To 10 Me.Table.Content.AddRecord() Next End Using
これまでの例で、フィールドプロバイダのいくつかのオーバーライド可 能なメソッドについて断片的な説明を行ってきました。ここでは包括的 な説明を行います。
フィールドプロバイダを作るには、IFieldProviderインターフェースを 満たすクラスを作成すればよいのですが、特に問題がなければ、常に代 表的な実装であるCFieldProviderクラスを継承してください。
CFieldProviderが持つメンバについて、カテゴリごとに簡単に説明しま す。
CreateFieldメソッドは、その名のとおりフィールドのインスタンスを生 成するメソッドです。デフォルトではCField型のオブジェクトを生成し ますが、何か特殊なプロパティをフィールドに持たせたい場合などは、 フィールドのクラスを自分で作成して利用することができます。Editor メソッドはエディタのインスタンスを生成します。このメソッドが Nothingを返した場合、このフィールドを編集することはできなくなりま す。詳しくは前述の例を参照してください。
レコードプロバイダをコンテントに設定したとき、またはレコードプロ バイダを使わずにフィールドを作成したときにApplyTableが呼ばれます。 フィールドを作成したときはFieldInitialize、エディタを作成したとき は、EditorInitializeメソッドが呼ばれます。
Settingメソッドが返した設定内容は、このフィールドのデフォルトの設 定となります。Captionメソッドの戻り値は、UTable.CreateCaptionで作 成される見出しフィールドに利用されます。Enableが真のフィールドだ けが、フォーカスを得ることができます。IMEの入力を行う場合は、 ImeModeを必ずDisable以外に設定してください。BorderLineメンバによっ て、フィールドの枠線を描画するかどうかを制御できます。
Renderメソッドはフィールドの描画を行うメソッドで、デフォルトでは 次のことを行います。まず、RenderBackgroundメソッド(見出しならば CaptionBackGroundメソッド)で背景を描きます。次に、フィールドの Value値をFormatValueで表示形式の文字列に変換し、RenderValueメソッ ドで描画します。フィールドの描画を完全に制御したければRenderをオー バーライドしてください。それ以外の場合は、必要なメソッドだけをオー バーライドしてください。
GetAdjustsizeメソッドは、ユーザが見出しフィールドの境界線をダブル クリックしたときに行われる自動サイズ調整で使用されるサイズを返し ます。デフォルトでは、FormatValueで返された文字列を描画するのに必 要なサイズを返すようになっています。