プログラム

125件の投稿

次のコントロールに移動するイベントハンドラ

テキストボックスにデータを入力し、[Enter]キーを押すと次のコントロールに移動する。という仕組みは頻繁に使います。

VB6(Visual Studio 6)では、フォームのデザイン時にこの動作を設定することができましたが、.NETではデザイン時でのこの設定がなくなっています(タブキーでの移動はあります)。

(以下のコードは実際に動作していますが、説明の邪魔になる部分を削除したり手を加えています。従ってこのままでは正しく動作しないことがあります。当ブログのコードは今後とも同様です。
掲載しますコードはベストだと思っているわけではありません。私たちの試行錯誤の結果、ともかく要求機能を満足するものです。
コードがもし読者の皆さんのお役に立つのであれば、コードのダウンロードを別途検討したいと思います。)

プログラムでコントロールの[KeyUp]イベントハンドラーで、[Enter]キーが押されたら、次のコントロールに移動するコードを書きます。

    Public Shared Sub SimpleKeyUp(ByVal sender As Object, _
                  ByVal e As System.Windows.Forms.KeyEventArgs)
        If e.KeyValue = Keys.Enter Then
	 Dim frm As Form = sender.findform
          Dim ctrl As Control = CType(sender, Control)
          Dim blnForwd As Boolean = True

          frm.SelectNextControl(ctrl, blnForwd, True, True, True)
        End If
    End Sub

このプログラムで注目すべきは、[sender](実はコントロール)から、それが配置されている[Form]を補足でき、このフォームで次の[TabIndex]のコントロールにフォーカスを移すことができることです(TabIndexの順番はデザイン時に設定します)。

そしてこのコードは、下のように当該コントロールに[KeyUp]イベントハンドラーとして登録します。

    Public Shared Sub SetSimpleEnterKeyHandler(ByRef ctrl As Control)

        If TypeOf (ctrl) Is TextBoxBase Then
            RemoveHandler CType(ctrl, TextBoxBase).KeyUp, AddressOf SimpleKeyUp
            AddHandler CType(ctrl, TextBoxBase).KeyUp, AddressOf SimpleKeyUp
        ElseIf TypeOf (ctrl) Is ButtonBase Then
            RemoveHandler CType(ctrl, ButtonBase).KeyUp, AddressOf SimpleKeyUp
            AddHandler CType(ctrl, ButtonBase).KeyUp, AddressOf SimpleKeyUp
        ElseIf TypeOf (ctrl) Is ListControl Then
            RemoveHandler CType(ctrl, ListControl).KeyUp, AddressOf SimpleKeyUp
            AddHandler CType(ctrl, ListControl).KeyUp, AddressOf SimpleKeyUp
        End If

        If TypeOf (ctrl) Is CheckBox Then
            RemoveHandler CType(ctrl, CheckBox).KeyUp, AddressOf ChkInstKeyUp
            AddHandler CType(ctrl, CheckBox).KeyUp, AddressOf ChkInstKeyUp
        End If
    End Sub

さらにこのコードは、前回ご紹介しました、[CtrlBindingCollection]クラスの[Cell]サブクラスのコンストラクターでコールされています。

その結果、

Dim CBC As New CtrlBindingCollection
With CBC
  .Bind(TextBox1, “氏名”, “String”, True)
  .Bind(TextBox2, “郵便番号”, “String”, True)
  .Bind(TextBox3, “住所”, “String”, True)
  .Bind(TextBox4, “年齢”, “int”, False)
End With
CBC.テキストクリア
CBC.Display(Datarow)

で[TextBox1]、[TextBox2]、[TextBox3]で[Enter]キーを押すと次のコントロールに移動するイベントハンドラーを割り当て、全コントロールをクリアし、それらのコントロールに[Datarow]の値をデータ型を考慮しながら表示することができます(もちろん別途Displayメソッドと、インスタンスDatarowを準備しておきます)。

ところで、日本語入力しているとき[Enter]キーを押すと直ちに次のコントロールに移動されては困ります。
日本語入力の途中では、通常ひらがなで入力して漢字変換が正しく表示されたところで、確定の[Enter]を押します。漢字入力はまだ続きますので、ここで次に移動したのではいけません。

次回はこれを正しく処理するコードをご紹介します。

コレクション

考えてみれば私がもっとも重宝しているのは、Collectionかもしれません。最初はCollectionBaseの継承をつかっていましたが、今はList(Of T)をメンバー変数に持つクラスです。

最も基本的な使用法は、画面で使うADO.NETのDataTable一つに対してこのクラスのインスタンスを一つ作ります。メソッドBindでは、画面のコントロールとテーブルの列名と列のデータ型を関連付けています。

Public Class CtrlBindingCollection
    Private m_lst As List(Of Cell)
    Sub New()
        m_lst = New List(Of Cell)
    End Sub

    Public Sub Bind(ByRef ctrl As Control, ByVal colName As String, ByVal DataType As Integer, ByVal blnNext As Boolean)
        m_lst.Add(New Cell(ctrl, colName, DataType, blnNext))
    End Sub

    Public Sub モード設定(ByVal blnEdit As Boolean)
   ' 全Cellのモード設定
    End Sub

    Public Sub テキストクリア()
   ' 全Cellのテキストクリア
    End Sub

    Class Cell
        Private m_Control As Control
        Private m_列名 As String
        Private m_データ型 As Integer

        Sub New(ByRef ctrl As Control, ByVal colName As String, ByVal intDataType As Integer, ByVal blnNext As Boolean)

            Me.m_Control = ctrl
            Me.m_列名 = colName
            Me.m_データ型 = intDataType

            If blnNext Then
                If Me.m_データ型 = DT.Str OrElse Me.m_データ型 = DT.Dte Then
                    SubFuncs.SetEnterKeyHandler(ctrl)
                Else
                    SubFuncs.SetSimpleEnterKeyHandler(ctrl)
                End If
            End If
   End Sub

        Sub モード設定(ByVal blnEdit As Boolean)
     .....
        End Sub

        Sub テキストクリア()
     .....
   End Sub
    End Class
End Class

今たとえば、Form コードの中で、
Private instCtrlBindingCollection As New CtrlBindingCollection

が宣言されているとして、[instCtrlBindingCollection.テキストクリア]でコントロールをすべてクリアすることができますし、[instCtrlBindingCollection.モード設定]では、画面コントロールのEnableの可能/不可能の切り替えができます。

すなわち、このコレクションに登録されたコントロールをいっせいに「どうこう」したいときにこのクラスのメソッドを一回コールすることで事足ります。

次回は実際何をしているかご紹介します。

これから

何人かの方がつたない私のブログを読んでくださっています。

ところが私はLINQと格闘していて、ブログを書く余裕がありません。

LINQについては、もう少し習熟が進んでから私なりに内容のあるお話を書きたいと思います。

VS2002から10年近く、.NETの開発を続け、その過程で様々なプログラムの工夫をしてきました。
ベストというほどの自信家ではありません。
ご批判をいただければ、また多少の参考になればと思い、少しずつ皆様にご披露していきたいと思っています。

LINQ を使う

その後、[LINQ to Entity Framework]を使ってプロジェクトを進めています。

まだ、小学生程度の習熟度ですが、「総じてLINQはいい」というのが感想です(複雑なビジネスロジックの場合はどうなのか分かりません)。

私は、マイクロソフトが用意するデータバインドが気に入りません。
一言で言えば、カスタマイズについての柔軟性に欠けると思います。

具体的にいえば、データベースとコントロールでの表示の間に関数を入れたいのですが、どうすればいいのかよく分からない。たぶんできない(と思います)(もちろん、ストアドプロシージャを使うとかまったく方法がない訳ではありません。データバインドでのカスタマイズができません)。

したがって、データバインドを一切つかわないで、コントロールへの表示と、コントロールデータのデータベースへの格納の部分をすべてコーディングしています。

これを統一的に書くために、ADO.NETではコントロールとデータベースの列名のマップを定義していますが、LINQを使うと厳密に型づけされていますので、列名とEntity Objectとのマップをどう定義すればいいのかわかりません。

やむなくその都度、コントロールへの表示とデータベースへの保存をいちいち書いているというわけです。
ここが一番気にいらないのですが、Entity Frameworkをもっと研究すれば、解決策があるのではないかと思っています。

LINQのいいところは、先にも書きましたが、ADO.NETではデータベースへのコネクションを書き(忘れず終了し)、トランザクションを書き、複数テーブルを保存するときは、Insert、Update、Deleteとテーブルの依存関係を考慮したコードを書かなければいけません。

これらをLINQが全部やってくれるので、LINQのプログラミングはADO.NETに比べずいぶん楽です。

まだまだ研究中ですが、研究する価値が十分あると思います。

セットアッププログラム

5月だったか[ソフトウェアの開発 Etc]で触れましたが、
パッケージソフトであれ受託ソフトであれ、開発が完了したら、
最終的にはそれを顧客のコンピュータにインストールしなければいけません。

そのためには開発とは別にインストールプログラムを作成しなければいけません。
Visual Studio 6では(およそ10年前)、
ディストリビューションプロジェクトというのがあって、これでインストールメディアを作っていましたが、
この仕組みは先にも書きましたように、
必要とする関連ソフトを闇雲にターゲットコンピュータにインストールするので、[Dllの地獄]という状態を作り出していました。

それを解消すべく、マイクロソフトはMicrosoft Installer(MSI)を開発し、あらかじめターゲットコンピュータにこのMSIをインストールしておいて、個別のソフトのインストールはMSIがすべて一元管理するようにしました。今ではMicrosoft InstallerがWindowsでのインストール標準になっています。

Windowsの開発環境であるVisual Studioでは、標準でmsi準拠のセットアッププロジェクトを作成する仕組みが提供されています。ただインストールする条件もいろいろあるため、いわばアドホック(場当たり的)な部分があります。
近年、インストール条件をXMLで記述する技術がオープンソースとしてMicrosoftから発表されました(WIXといいます)。
これによってインストール条件の設定についてはだいぶ透過性がよくなったと思います。がこれも発展途上というところだと思います。

インストールプロジェクト作成ソフトとして日本で最も有名なのは、[Install Sheild]ですが、このソフトは何故か年々高額になって、現在では一本数十万円から100万円を超える価格になっています。

高額なソフトや大量に販売が見込めるソフトのインストールプログラムの作成であれば、[Install Sheild]はベストチョイスでしょうが、そうでなければなかなか手が出ません。

この様な状況の中で、できるだけお金をかけないで、いまセットアッププログラムを作成するのにどのようにすればいいか。私たちのケースをご紹介いたします。

インストール時にユーザにシリアル番号等特に厳密はチェックを必要としない場合は、Visual Studioのセットアッププロジェクトを使うのが便利です。Visual Studio 6で開発したソフト(.NETを使っていない)であれば、Visual Studio 2003のセットアッププロジェクトが使えます。残念なことは、Visual Studioのシリアル番号のチェックは甘いので、厳密なチェックはできません。

あるプロ不ラムをインストールし使うには、別のソフトが必要な場合があります。たとえば.NET Frameworkやクリスタルレポートが必要な場合は、それらのソフトがターゲットコンピュータにすでにインストールされているかどうか調べ、なければこれらをインストールしなければいけません(ランタイムのプレインストール)。

Visual Studioは、このあたりの処理は見事にやってくれます。
Visual Studioの短所は、こまめな処理ができないということです。たとえば、先の例で独自のシリアル番号チェックプログラムを組み込むことができません(少なくとも私たちにはわからなかった)。

 WIXはこのあたりの処理ができます。私はWIXがすきです。WixEditというWIX用IDEツールがあります(Sharp Developも要検討)。なかなかいいのですが、WIXを使いこなすには少し勉強が必要です。
それと、これはWIXの仕様だと思うのですが、ランタイムのプレインストールができないようです。
この部分は、Visual Studioと組み合わせて使うことができるのではないかと考えています。

WIXを時間をかけて勉強する時間がなかったので、私たちはInstall Aware(以下IAといいます)というソフトを使いました。理由は比較的安く、やりたい機能がそろっていたからです。ただし、ドキュメントが悪いし、サポート対応が悪い。それに日本語版(IDEでの日本語表示等)はないのです。
IAにはScriptと称するものがありますが、この言語仕様がない。チュウトリアルもない。手探りで習得するしかない。
Scriptは言語というよりいわばコマンドセットで、ともかくこのコマンドの使い方を習得するのがIA習得の手っ取り早い方法です。
機能や使い方そのものは悪くないのに、このドキュメントの悪さは何なのかと不思議に思います。