none
VB、テキストボックスやコンボボックスへの指示を一括ですませたいのですが、うまくいきません。 RRS feed

  • 質問

  • 使用VBは、VB2010 Expressです。
    現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(A・B・C)を書いています。
    各十数個になります。煩雑ですし、第一エレガントでない。 それに、コード・チェックの邪魔です。 ですから、これを、一括の指示で済ませたいのです。

    (A)文字型のTextBox
           Private Sub TX_BranchNo_TextChanged(sender As System.Object, e As System.EventArgs) Handles TX_BranchNo.TextChanged
             If Len(TX_BranchNo.Text) > 50 Then
                MessageBox.Show("50文字を超える入力は、できません。 ")
                TX_BranchNo.Text = ""
                Exit Sub
             End If
         End Sub

    (B) コンボボックス
        Private Sub CB_S_TaxType_Validating(ByVal sender As Object, _
                                         ByVal e As System.ComponentModel.CancelEventArgs) Handles CB_S_TaxType.Validating
             If Me.CB_S_TaxType.SelectedIndex = -1 Or Me.CB_S_TaxType.Text = "" Then
                MessageBox.Show("コンボボックスのデータ選択で、入力してください。 直接入力はできません。 移動するには、まず選択してください。 ")
                e.Cancel = True
                Me.CB_S_TaxType.Select()
             End If
         End Sub

    (C) 数値型のTextBox
            Private Sub TX_S_TrustPeriod_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TX_S_TrustPeriod.TextChanged
              If Not IsNumeric(Me.TX_S_TrustPeriod.Text) And Me.TX_S_TrustPeriod.Text <> "" Then
                MessageBox.Show("  ここは、数字以外のものは入力できません。  ")
              End If

              If Len(TX_S_TrustPeriod.Text) > 10 Then
                MessageBox.Show("10 桁(文字)を超える入力は、できません。 ")
                TX_S_TrustPeriod.Text = ""
                Exit Sub
              End If
            End Sub


    <私の対策1> Form_Load(フォームを開く) に、下記のコードを書いたのですが、うまくいきません(予測通り?)。
           どこかに、他の方法で書けば、うまくいきますか?  または、コード自体に誤りがありますか? Form_Load に書いても意味がないようにも感じられるのですが。

        'TextBox入力規制
            Dim CTRL As Control
            For Each CTRL In Me.Controls
                If CTRL.GetType Is GetType(TextBox) Then
                    If Len(CTRL.Text) > 150 And CTRL.Name Like "%""Memo""%" Then
                        MessageBox.Show("150文字を超える入力は、できません。 ")
                        CTRL.Text = ""
                        Exit Sub

                    ElseIf Len(CTRL.Text) > 50 Then
                        MessageBox.Show("50文字を超える入力は、できません。 ")
                        CTRL.Text = ""
                        Exit Sub

                    ElseIf IsNumeric(CTRL.Text) And Len(CTRL.Text) > 10 Then
                        MessageBox.Show("10文字(桁)を超える入力は、できません。 ")
                        CTRL.Text = ""
                        Exit Sub
                    End If

                End If
            Next


    <私の対策2>イベント自作で対処しようとおもい下記のコードを書き、これが成功すれば次の段階へ行く予定でいたのですが、この最初の段階でうまくいきません(’**印のところです)。
           このイベントは、初期段階なので、これが成功すれば次の段階へ行き、対策を進める予定でした。 
            実験用に、フォーム内にテキストボックス(Text1)・コマンドボタン(Command1)を追加。

    1.クラス作成します。
    Public Class AlarmInteger

        Public Event Over(ByVal OverValue As Integer, Cancel As Boolean)
        Public Max As Integer
        'Private m_CurrentValue As Integer = 0
        Dim m_CurrentValue As Integer

        Public Property CurrentValue(NewValue As Integer) As Integer
            Get
                Return m_CurrentValue
            End Get

            Set(ByVal Value As Integer)
                Dim Cancel As Boolean
                If NewValue > Max Then
                    RaiseEvent Over(NewValue, Cancel)
                    If Cancel Then Exit Property
                End If
                m_CurrentValue = NewValue
            End Set
        End Property
      
    End Class


    2.フォーム内に、コードを追加します。
    (A) Dim WithEvents X As AlarmInteger

    (B)  Private Sub F_M_Client_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load  ----フォームを開くとき
           ----省略
        X = New AlarmInteger
            X.Max = 100

          ----省略
         End Sub

     


     (C) Private Sub Command1_Click(sender As System.Object, e As System.EventArgs) Handles Command1.Click  -----コマンドボタン
     '**下の行で、エラーがでます。
            X = Me.Text1.Text  -----Text1.Textにエラー表示 (型 'String' の値を 'JDB_Main_Menu.AlarmInteger' に変換できません。)
              MsgBox(X)
         End Sub


         Private Sub X_Over(ByVal OverValue As Integer, Cancel As Boolean)     '③

            Dim Answer As MsgBoxResult

            Answer = MsgBox(OverValue & "を代入しようとしました。キャンセルしますか。", vbQuestion Or vbYesNo)

            If Answer = vbYes Then Cancel = True

         End Sub


    以上です、長くなりましたが、よろしくお願いします。
    YKsaila

    2012年6月5日 4:35

回答

  • > 現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(A・B・C)を書いています。
    > 各十数個になります。煩雑ですし、第一エレガントでない。 それに、コード・チェックの邪魔です。 ですから、これを、一括の指示で済ませたいのです。

    Handles 句に複数のコントロールのイベントを指定できます。以下 6つのTextBox で同じイベントハンドラを共有する例です。他 AddHandler を使って制御する方法もあります。

    Private Sub TX_BranchNo_TextChanged(sender As System.Object, e As System.EventArgs) _
    	Handles txtBox1.TextChanged, txtBox2.TextChanged, 
    		txtBox3.TextChanged, txtBox4.TextChanged,
    		txtBox5.TextChanged, txtBox6.TextChanged
    
    	Dim textBox = TryCast(sender, TextBox)
    	If Len(textBox.Text) > 50 Then
    		MessageBox.Show("50文字を超える入力は、できません。 ")
    		textBox.Text = ""
    	End If
    End Sub


    ひらぽん http://d.hatena.ne.jp/hilapon/



    • 編集済み ひらぽんModerator 2012年6月5日 5:49 カンマが一つ余計でした
    • 回答としてマーク trapemiyaModerator 2012年6月15日 6:43
    • 回答としてマークされていない yksaila 2012年6月16日 6:00
    • 回答としてマーク yksaila 2012年7月23日 15:02
    2012年6月5日 5:48
    モデレータ
  • > mars12さんも触れているカスタムコントロール(ユーザーコントロール, 継承コントロール)を作成するべきだと思います。

    複数の画面で同じ処理を行うなら継承コントロールを作るのも一つの手ですが、必ずしも最適解とは言い切れないと思います。拡張メソッドを使う方法もありますし。もし一つの画面しか入力検証を行わない場合、わざわざ継承コントロールを増やすのもいかがなものかと。ケースバイケースだと思います。

    以下、拡張メソッドを使う例です。

    Imports System.Runtime.CompilerServices

    Public Module ComboBoxValidator
        <Extension()>
        Public Sub ValidateMaxLength(cb As ComboBox, maxLength As Int32)
            AddHandler cb.TextChanged,
                Sub(sender, e)
                    If cb.Text.Length > maxLength Then
                        MessageBox.Show(String.Format("{0}文字を超える入力できません。", maxLength))
                        cb.Text = ""
                    End If
                End Sub
        End Sub
    End Module

    Public Class Form1
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            Me.ComboBox1.ValidateMaxLength(50)
            Me.ComboBox2.ValidateMaxLength(30)
            Me.ComboBox3.ValidateMaxLength(50)
        End Sub
    End Class



    ひらぽん http://d.hatena.ne.jp/hilapon/




    • 編集済み ひらぽんModerator 2012年6月5日 8:30 モジュールの名称を変えました
    • 回答としてマーク yksaila 2012年6月15日 8:22
    • 回答としてマークされていない yksaila 2012年6月16日 6:00
    • 回答としてマーク yksaila 2012年7月23日 15:03
    2012年6月5日 8:20
    モデレータ
  • > Validatingも(A)の中にあれば、メソッドとして使用できますよね。 いろいろやったのですが、ダメでした。

    こちらでは実装および動作してますが、どこが問題だったのでしょうか?

    Option Explicit On
    Option Strict On
    
    Imports System.Runtime.CompilerServices
    
    Public Module TextBoxValidator
        ''' <summary>
        ''' 入力文字が数値であることと文字数を検証します。
        ''' </summary>
        ''' <remarks>メソッド名を意味のある名前に変えてます。</remarks>
        <Extension()>
        Public Sub ValidateMaxLengthAndNumeric(textBox As TextBox, maxLength As Int32)
            AddHandler textBox.TextChanged,
            Sub(sender, e)
                If Not IsNumeric(textBox.Text) And textBox.Text <> "" Then
                    MessageBox.Show("数字以外の文字は入力できません。")
                End If
    
                If textBox.Text.Length > maxLength Then
                    MessageBox.Show(String.Format("{0}文字を超える入力はできません。", maxLength))
                    textBox.Text = ""
                End If
            End Sub
        End Sub
    
        ''' <summary>
        ''' 文字数を検証します。
        ''' </summary>
        <Extension()>
        Public Sub ValidateMaxLength(textBox As TextBox, maxLength As Int32)
            AddHandler textBox.TextChanged,
            Sub(sender, e)
                If textBox.Text.Length > maxLength Then
                    MessageBox.Show(String.Format("{0}文字を超える入力はできません。", maxLength))
                    textBox.Text = Mid(textBox.Text, 1, maxLength) & "-"
                End If
            End Sub
        End Sub
    
        ''' <summary>
        ''' 数値か検証します。
        ''' </summary>
        <Extension()>
        Public Sub ValidateNumeric(textBox As TextBox)
            AddHandler textBox.Validating,
                Sub(sender, e)
                    If textBox.Text = "" Then
                        Return
                    End If
    
                    If Not IsNumeric(textBox.Text) Then
                        MessageBox.Show("数値を入力してください。入力しない場合 0 にしてください。")
                        e.Cancel = True
                        textBox.Select()
                    End If
                End Sub
        End Sub
    End Module
    
    Public Class Form1
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Me.TextBox1.ValidateMaxLengthAndNumeric(10) ' 数値および文字数制限 
            Me.TextBox2.ValidateMaxLength(50)           ' 文字文字数制限
            Me.TextBox3.ValidateNumeric()               ' 数値
        End Sub
    End Class


    ひらぽん http://d.hatena.ne.jp/hilapon/

    • 回答としてマーク yksaila 2012年7月23日 15:05
    2012年6月8日 8:27
    モデレータ
  •  ひらぽんさんへ

    できました、ありがとうございました。

    以前の私のコード(下記記載)は、(1)と(2)がいけなかったようです。

    (1)--- ByVal e As System.ComponentModel.CancelEventArgsが不要?

    (2)---AddHandler cb.Validating,← 末尾の「,」に波線がでます。

    お手本のように、(1)でByVal e As System.ComponentModel.CancelEventArgsを除き、 Sub(sender, e)--End Subを加えるべきだったのですね。

    YKsaila

    <下記、間違いコード>

    Private Sub textBoxN_Validating(cb As TextBox, ByVal e As System.ComponentModel.CancelEventArgs) ----(1)
             AddHandler cb.Validating,----(2)

            If cb.Text = "" Then
                Exit Sub
            End If

            If Not IsNumeric(cb.Text) Then
                MessageBox.Show("数値を入力してください。  入力しないなら、0 にしてくださいよ。")
                e.Cancel = True
                cb.Select()
            End If
        End Sub

    • 回答としてマーク yksaila 2012年7月23日 15:04
    2012年6月8日 10:09
  • 現在までの解決策の状況をまとめます。

    1.イベントハンドラーの共用 ----同じ内容のPrivate Subを、一つにまとめます。

    2.Addhanndler の使用 --- Form_Load に書き込みます。

    3.拡張メソッドの使用 --- 拡張メソッドを作成しておき、Form_Load に書き込みます。

    4.カスタムコントロールの作成・使用--- この場合は、拡張メソッドも含めた複合使用が便利でした。

    5.自作関数(?)の応用-- これを、For ~ Next で回す? --- これは、未です。 数日後になります。

    どれも、もちろん有益ですが、(3)と(4)がすっきりして、かつForm_Load の中で見られるので、個人的には好みです。

    (5)が面白そうです、でも少しエラーがでますので、もう少しやってから(他もありますので)質問します。

    YKsaila

    H24/06/18記 -- (5)が、80~90%完成しています。 あとは、残りの10~20%! それと、For ~ Next ? 

    で、これが終わったら感想です!


    • 回答としてマーク yksaila 2012年6月16日 4:10
    • 回答としてマークされていない yksaila 2012年6月16日 6:01
    • 編集済み yksaila 2012年6月18日 14:12
    • 回答としてマーク yksaila 2012年7月23日 15:06
    2012年6月16日 4:10
  • 配列の各要素にインスタンスを作成する必要があります。
    Dim Multi(1) As MultiEventで配列が作成されていますが、その要素はNothingなのでNullReferenceExceptionが発生してしまいます。

    Multi(0) = New MultiEvent
    Multi(1) = New MultiEvent

    • 回答としてマーク yksaila 2012年7月23日 15:06
    2012年6月16日 6:42
  • NF64さんへ

    なんとか、Bind 関数の利用ができたようです。 いやあ、相当な時間がかかりました!

    テキストボックスのほうは、Bind は完成しています。 欲張って、コンボボックスのValidatingまで手をのばしたのですが、これが現在止まっています。

    コンボまでBind が使えるようになれば、この先便利なのですが。 エラー修正のご指導をいただければ幸いです。 エラー個所は、(A),(B)です。

    よろしく、お願いします。

    YKsaila

    以下、手順です。 

    (1)Dim Multi(2) As MultiEvent  --- Form_Loadの外に書く

    (2) Form_Loadの中に書く
         Multi(0) = New MultiEvent
            Multi(1) = New MultiEvent

            Multi(0).Bind(TX_S_BillSight, 6)      ’テキストボックス、成功   TX_S_BillSight---テキストボックス名 制限入力字数=6
            Multi(1).Bind(TX_S_TrustPeriod, 6)  ’テキストボックス、成功  TX_S_TrustPeriod---テキストボックス名 制限入力字数=6

           Multi(2) = New MultiEvent

         Multi(2).Bind(CB_S_Payment, 0)    ’ (A) コンボボックス、エラー、意図的に Bind(----,0)として、コンボボックスのValidatingを図ります。

                                      ’(3)の最後のほうを見てください。

    (3)クラス作成
    Public Class MultiEvent
        Public SlaveControl As Control
        Public WithEvents SlaveTextBox As TextBox
        Public WithEvents SlaveComboBox As ComboBox
        Public Multi_MaxLength As Integer
        Public Sub Bind(SlaveControl As Control, Multi_MaxLength As Integer)
            If TypeOf SlaveControl Is TextBox Then
                Me.SlaveTextBox = DirectCast(Me.SlaveTextBox, TextBox)
                AddHandler SlaveControl.TextChanged,
                Sub(sender, e)
                    If Not IsNumeric(SlaveControl.Text) And SlaveControl.Text <> "" Then
                        MessageBox.Show("ここは、数字以外のものは入力できません。 ")
                        Exit Sub
                    End If

                    If SlaveControl.Text.Length > Multi_MaxLength Then
                        MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", Multi_MaxLength))
                        SlaveControl.Text = ""   ’この辺は、あとでもっと良いものに変更します。
                    End If
                End Sub

                AddHandler SlaveControl.Validating,
                Sub(sender, e)
                    If SlaveControl.Text = "" Then
                        Return
                    End If

                    If Not IsNumeric(SlaveControl.Text) Then
                        MessageBox.Show("数値を入力してください。 入力しない場合、0 にしてください。")
                        e.Cancel = True
                        SlaveControl.Select()
                        Exit Sub
                    End If
                End Sub

            ElseIf TypeOf SlaveControl Is ComboBox Then
                Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)
                If Multi_MaxLength = 0 Then
                    AddHandler SlaveControl.Validating,
                     Sub(sender, e)
                         If Me.SlaveComboBox.SelectedIndex = -1 Or Me.SlaveComboBox.Text = "" Then    ’(B) ここで、エラー表示でます。
                             MessageBox.Show("コンボボックスのデータ選択で、入力してください。 直接入力はできません。 移動するには、まず選択してください。 ")
                             e.Cancel = True
                             Me.SlaveComboBox.Select()
                         End If
                     End Sub
                End If
            End If

        End Sub
     End Class




    • 編集済み yksaila 2012年6月18日 14:07
    • 回答としてマーク yksaila 2012年7月23日 15:08
    2012年6月18日 13:54
  • 起きているエラーというのはNullReferenceExceptionですか?
    どのような状況で、どのようなエラーが発生しているのか内容を明記しましょう。

    Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)

    「Me.SlaveComboBoxにパラメーターとして与えられたComboBox(つまるところSlaveControl)を保持させたい。」という意図があると思いますが、上記のコードではそのようになっていません。
    TextBoxの方も同じ状態になっていますから、修正が必要でしょう。

    既にアドバイスもありましたが、やはりこのようなコードを書くよりも何とかしてComboBoxをDropDownListにする方が良い気がしてなりません。


    • 編集済み NF64 2012年6月20日 2:53 文章が分かりにくかったので修正しました。
    • 回答としてマーク yksaila 2012年7月23日 15:08
    2012年6月20日 1:29
  • 変わってないデス。

    Me.SlaveComboBox = DirectCast(Me.SlaveControl, ComboBox)
    かと。
    • 回答としてマーク yksaila 2012年7月23日 15:09
    2012年7月5日 4:39
  • 気付いてませんでしたorz
    ご指摘ありがとうございます。

    しかし、SlaveControlはパラメーターなのでMe要らないですね。
    正しくは

    Me.SlaveComboBox = DirectCast(SlaveControl, ComboBox)

    でした。

    • 回答としてマーク yksaila 2012年7月23日 15:09
    2012年7月5日 5:23
  • NF64さんへ

    >既にアドバイスもありましたが、やはりこのようなコードを書くよりも何とかしてComboBoxをDropDownListにする方が良い気がしてなりません。

    ただのListのときは、DropDownListでうまくいくのですが、コンボボックスのデータがサーバーデータのときはうまくいかないのです。

    例:CB_Country_ID -- コンボボックスで国を表示します。 サーバーデータ自体は、数値(Integer: Country_ID)です。 サーバーには別のテーブル(T_M_Country)があり、このテーブルにCoutry_ID(数値)やCountryShortName(文字)が格納されています。 フォームに表示する際は、一旦受け取ったサーバーデータ(数値:Country_ID)をCountryShortNameに変換しています。

    使用者がコンボボックスで選択したときは、選択したデータ(CountryShortName)は文字型なわけです(例:USA、HongKong)。 DropdownStyle=DropDownListのときは、これが数値にうまく変換できないようです。 ですから、コンボボックスの表示データがサーバーデータのときはうまくいかない、と考えているのですが?

    私の間違いだと、ありがたいのですが(こんなややこしいことを、しなくて済みます)。

    YKsaila

    下記に、関連コードを添付します。

    Me.CB_Country_ID.DropDownStyle = ComboBoxStyle.DropDownList   --- 今回、試験的に追加したコードです。 もともとは、ないものです。

    1.データ表示時

      (1) 'コンボボックス:CB_Country_ID(T_M_Country)----コンボボックス表示準備
            Dim dsCombo1 = New DataSet
            Dim sqCombo1 As String
            sqCombo1 = "SELECT T_M_Country.Country_ID, T_M_Country.CountryShortName, T_M_Country.CountryCode FROM T_M_Country"
            adapter.SelectCommand = New SqlClient.SqlCommand(sqCombo1, Con)
            adapter.SelectCommand.CommandType = CommandType.Text
            adapter.Fill(dsCombo1)
            Dim dtCombo1 As New DataTable
            dtCombo1 = dsCombo1.Tables(0)
            Me.CB_Country_ID.DataSource = dtCombo1
            Me.CB_Country_ID.DisplayMember = "CountryShortName"
            Me.CB_Country_ID.ValueMember = "Country_ID"

        Me.CB_Country_ID.DropDownStyle = ComboBoxStyle.DropDownList   --- 今回追加コード、 ここが(問題点)


     (2)コンボ表示
        Dim CTRYID As Integer = CInt(Me.CB_Country_ID.Text)---エラー表示(例:HongkongをIntegerに変換不能とでます、 ここが (問題点)です。

         Me.CB_Country_ID.Text =dt.Rows(n).Item("Country_ID").ToString
         Dim CTRYID As Integer = CInt(Me.CB_Country_ID.Text)
         foundrows = dtCombo1.Select("Country_ID = " & CTRYID & "")
         Me.CB_Country_ID.SelectedValue = dt.Rows(n).Item("Country_ID").ToString

    こうしたらどうなりますか?

    'Me.CB_Country_ID.Text =dt.Rows(n).Item("Country_ID").ToString
    'Dim CTRYID As Integer = CInt(Me.CB_Country_ID.Text)
    Me.CB_Country_ID.SelectedValue = dt.Rows(n).Item("Country_ID").ToString
    Dim CTRYID As Integer = CInt(Me.CB_Country_ID.SelectedValue)
    

    ComboBoxのTextプロパティの値は、あくまで表示内容です。

    値の制御はSelectedValueプロパティで行って下さい。

    ※他にも気になりますがとりあえず・・・

    • 回答としてマーク yksaila 2012年7月23日 15:10
    2012年7月9日 3:40
  • aviator_さんへ

    できました! やっとです。

    これで、Validatingがなしで、済ませます。 CB_Country_ID.Validatingのコードです。 余計なコメントもでないので、このほうがいいです。 DropDownlistの灰色の少し立体的な表示が嫌なので、ここの表示スタイルは、PopUp か Flat にしました。

    また、関連で横にテキストボックスがあります(TX_CountryCode)。 コンボで選択すると、横のテキストボックスに「CountryCode」を表示します。

    CountryCodeは、コンボボックスのDisplayMemberでも ValueMemberでもないので、下記のようなコードを追加しました。

    全てのコンボボックスをこのタイプに変更するつもりです。 手間が大変ですが、ま、自分の責任だからしようがないです。

    ありがとうございました。

    YKsaila

    以下は、参考コード:

          Private Sub CB_Country_ID_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles CB_Country_ID.SelectedIndexChanged
            'SQL認証接続
            省略
            Con.Open()
            Dim adapter = New SqlClient.SqlDataAdapter()

            'コンボボックス:CB_Country_ID(T_M_Country)----コンボボックス表示準備
            Dim dsCombo1 = New DataSet
            Dim sqCombo1 As String
            sqCombo1 = "SELECT T_M_Country.Country_ID, T_M_Country.CountryShortName, T_M_Country.CountryCode FROM T_M_Country"
            adapter.SelectCommand = New SqlClient.SqlCommand(sqCombo1, Con)
            adapter.SelectCommand.CommandType = CommandType.Text
            adapter.Fill(dsCombo1)

            Dim dtCombo1 As New DataTable
            dtCombo1 = dsCombo1.Tables(0)
            Dim foundrows() As Data.DataRow
            Dim S_INDX As Integer = Me.CB_Country_ID.SelectedIndex
            foundrows = dtCombo1.Select("Country_ID=" & S_INDX & "")
           Me.TX_CountryCode.Text = foundrows(0).Item("CountryCode").ToString

        End Sub

    • 回答としてマーク yksaila 2012年7月23日 15:10
    2012年7月9日 8:50
  •  はぁ。これの方が、はるかに簡単でしたね。

    dtCombo1 = dsCombo1.Tables(0)
    Me.CB_Country_ID.DataSource = dtCombo1
    Me.CB_Country_ID.DisplayMember = "CountryShortName"
    Me.CB_Country_ID.ValueMember = "Country_ID"
    
    Me.CB_Country_ID.SelectedValue = dt.Rows(n).Item("Country_ID").ToString
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    

     ここです。dt.Rows(n).Item("Country_ID") の Type を調べてください。デバッガでとめて、クイック ウォッチに「GetType(dt.Rows(n).Item("Country_ID"))」かな?おそらく、Integer か、Decimal あたりの「数値型」になっていると思います。それを ToString() で「文字列型」にするもんだから、一致しないよ、と。

     ComboBox を1つ配置して、DropDownList にしました。次のコードで「英語」が選択されます。

    Public Class Form1
    
        Dim items As DataTable
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            Me.items = New DataTable("ComboItems")
            ' Adapter で SELECT すると、データベースの型によって自動的に設定されます。
            Me.items.Columns.Add("LocaleID", GetType(Integer))
            Me.items.Columns.Add("LocaleName", GetType(String))
            Me.items.Rows.Add(1041, "日本語")
            Me.items.Rows.Add(1033, "英語")
            Me.items.Rows.Add(1042, "韓国語")
    
            Me.ComboBox1.DataSource = Me.items
            Me.ComboBox1.DisplayMember = "LocaleName"
            Me.ComboBox1.ValueMember = "LocaleID"
    
            Me.ComboBox1.SelectedValue = Integer.Parse("1033")
        End Sub
    End Class
    

     Country_ID の型が Decimal だった場合、暗黙では Integer への縮小変換ができないので、Option Strict On だとエラーが出るかもしれません。この場合、明示的に Integer に縮小変換するか、関連する変数の型を Decimal に変更します。

     「型を意識する」とは、こういうことです。専門のソフトウェア開発者ではないということなので、「Option Strict Off」での使用を薦めます。あるいは、C# を使用される事を薦めます。今のような、「中途半端に型を意識する状態」は、よくないです。


    Jitta@わんくま同盟

    • 回答としてマーク yksaila 2012年7月23日 15:10
    2012年7月9日 14:16
  • 先の(直前の)書き込みと重複しますが、途中なので、再度まとめます。

    ”コンボボックスとテキストボックスへの共通指示を一般化する”、これがこのスレッドの課題でした!


    コンボボックスについて、現在までに、判明したこと:
    コンボ・ボックスのDropDownStyleは、必ず(ComboBoxStyle.)DropDownListにする。 Dropdownには、しない!
    FlatStyleは、Standardでなく、Flatか PopUpにする(これは、好みの問題)。

    (A)--DropDownListで、コンボボックスへの指定外の入力が防げます。
    コメントも不要だし、簡潔です。 使用者にとっても、このほうが良い。

    ということで、コンボ・ボックスの内容指定(設定)で、DropDownList を選択しますから、コンボへの共通のコード指定は全く不要となりました。
    コンボボックスへの指定外の入力を防ぐことが目的ですから、自作メソッドも、自作コンボボックスも不要、Addhandlerも不要となります。

    コンボボックスでは、だいぶ遠回りをしました。 ま、私本人には、勉強になったので良かったのですが。 いったい、何をしていたんでしょうね?
    結局、何人かの方々が言われたように「DropDownList」に落ち着き、まとめてみると意外と簡潔になりました。

    今まで何をやってきたのかと、自問自答したくなります。 基本が分かっていないということは、こういうことかもしれないですね。

    残るは、TextBoxのみとなり、作業は簡単になりました。 TextBoxへの共通指示は残りますので、この一括処理(指示)が最後の課題となります。


    以下、参考コード(例):コンボボックスについて、です。
    1.Form_Load時:
      ①コンボ・データ=表形式
      Form_Loadの前に、  Dim items as DataTable を書いておく。

       'コンボボックス CB_B_TaxFractionType設定
            Me.items = New DataTable("B_TaxFractionType")
            Me.items.Columns.Add("B_TaxFractionType_No", GetType(Integer))
            Me.items.Columns.Add("B_TaxFractionType", GetType(String))
            Me.items.Rows.Add(0, "切捨")
            Me.items.Rows.Add(1, "四捨五入")
            Me.items.Rows.Add(2, "切上")
            Me.CB_B_TaxFractionType.DataSource = Me.items
            Me.CB_B_TaxFractionType.DisplayMember = "B_TaxFractionType"
            Me.CB_B_TaxFractionType.ValueMember = "B_TaxFractionType_No"

      ②コンボ・データ=サーバー内の他テーブルからのデータ---ここでは、なし。 データ呼び出し時に設定、(2)で。


    2.データ(レコード)呼び出し時:
           ①(1-①)--表形式
        Me.CB_B_TaxFractionType.SelectedValue = dt.Rows(n).Item("B_TaxFractionType")
        Me.CB_B_TaxFractionType.Text = Me.CB_B_TaxFractionType.DisplayMember
        (注):dt.Rows(n)--- サーバーからの呼び出しレコード、”n”は行番号、dtはデータテーブル

        ②(1-②)コンボ設定--サーバーからのデータ
        'コンボボックス:CB_Country_ID(T_M_Country)----(サーバー内の他テーブルからのデータ)
            Dim dsCombo1 = New DataSet
            Dim sqCombo1 As String
            sqCombo1 = "SELECT T_M_Country.Country_ID, T_M_Country.CountryShortName, T_M_Country.CountryCode FROM T_M_Country"
            adapter.SelectCommand = New SqlClient.SqlCommand(sqCombo1, Con)
            adapter.SelectCommand.CommandType = CommandType.Text
            adapter.Fill(dsCombo1)
            Dim dtCombo1 As New DataTable
            dtCombo1 = dsCombo1.Tables(0)
            Me.CB_Country_ID.DataSource = dtCombo1
            Me.CB_Country_ID.DisplayMember = "CountryShortName"
            Me.CB_Country_ID.ValueMember = "Country_ID"
            Me.CB_Country_ID.DropDownStyle = ComboBoxStyle.DropDownList

            --
         --
         Me.CB_Country_ID.SelectedValue = dt.Rows(n).Item("Country_ID") 
            Me.CB_Country_ID.Text = Me.CB_Country_ID.DisplayMember

           << データ呼び出し表示時は、①②ともに同じで、SelectedValue・DisplayMemberを使用する。>>


    3.書き込み
     sql.Parameters.AddWithValue("@B_TaxFractionType", Me.CB_B_TaxFractionType.SelectedValue)

    以上です、TextBoxについての最後の質問は、今日中にします(7/19)。

    YKsaila 


    • 編集済み yksaila 2012年7月18日 16:23
    • 回答としてマーク yksaila 2012年7月23日 15:13
    2012年7月18日 15:58
  • 私には、遠大な計画があって、VBでまともに使える 「業務ソフト」 を自力で作りたいのです。 その意味では、専門職並みの知識が必要です。

     了解。新しいものを作る楽しみを知る機会へようこそ!!


    ”コンボボックスとテキストボックスへの共通指示を一般化する”、これがこのスレッドの課題でした!

     次の様なコード例が挙がっています。(主観的優しい順)

    1. Handles 句の後にずらずら並べる(2012年6月5日 5:48 ひらぽんさん)
    2. AddHandler ステートメントをずらずら並べる(2012年6月7日 7:14 yksailaさん)
    3. 拡張メソッドを使う(2012年6月5日 8:20 ひらぽんさん)
    4. Partial を利用して拡張する(2012年6月5日 10:48 aviator__さん)
    5. コントロールを検索して AddHandler で登録する(2012年6月11日 14:02 Jitta)
    6. TextBox を継承したコントロールを作る(2012年6月5日 7:22 aviator__さん)

    If TypeOf CtrlItem Is TextBox And CtrlItem.Tag Is "N" Then ---(A)この行が有効でないようです。

     次のように、同じオブジェクトではないので、「CtrlItem.Tag Is "N"」が否を返しています。一般的に、文字列の比較には string.Equals, string.Compare, string.CompareTo, == を使います。また、If ステートメントの比較式における「かつ」には、「AndAlso」を使います。理由は、調べて下さい。

    Is 演算子 (Visual Basic) より:

    Is 演算子は、2 つのオブジェクト参照が同じオブジェクトを参照しているかどうかを判定します。ただし、値の比較は行われません。object1 と object2 の両方がまったく同じオブジェクト インスタンスを参照している場合、result は True になります。それ以外の場合は、result は False です。


    Dim CtrlItem As Control   ’(B)ここが、おかしい?

     入れ物を宣言していますが、中に何も入れていません。従って、「変数 'CtrlItem' は、値が割り当てられる前に使用されています。Null 参照の例外が実行時に発生する可能性があります。」


    Jitta@わんくま同盟

    • 回答としてマーク yksaila 2012年7月23日 15:14
    2012年7月19日 12:58

すべての返信

  • > <私の対策1>

    なぜイベントハンドラに書かないのでしょうか。

    > <私の対策2>

    何をやっているかわかりませんでしたが、普通は個々のコントロールにイベントハンドラ設定します。

    大まかに書くと
    1. コントロールを入れた配列を作る
    2. それをループ処理でイベントハンドラ設定する
    でいいんじゃないでしょうか。

    もうちょっとできるなら、そういうカスタムコントロールつくる、とかになるんじゃないでしょうか。

    # VBはあまり読めないので見落としてることがあるかもしれません。

    2012年6月5日 4:58
  • > 現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(A・B・C)を書いています。
    > 各十数個になります。煩雑ですし、第一エレガントでない。 それに、コード・チェックの邪魔です。 ですから、これを、一括の指示で済ませたいのです。

    Handles 句に複数のコントロールのイベントを指定できます。以下 6つのTextBox で同じイベントハンドラを共有する例です。他 AddHandler を使って制御する方法もあります。

    Private Sub TX_BranchNo_TextChanged(sender As System.Object, e As System.EventArgs) _
    	Handles txtBox1.TextChanged, txtBox2.TextChanged, 
    		txtBox3.TextChanged, txtBox4.TextChanged,
    		txtBox5.TextChanged, txtBox6.TextChanged
    
    	Dim textBox = TryCast(sender, TextBox)
    	If Len(textBox.Text) > 50 Then
    		MessageBox.Show("50文字を超える入力は、できません。 ")
    		textBox.Text = ""
    	End If
    End Sub


    ひらぽん http://d.hatena.ne.jp/hilapon/



    • 編集済み ひらぽんModerator 2012年6月5日 5:49 カンマが一つ余計でした
    • 回答としてマーク trapemiyaModerator 2012年6月15日 6:43
    • 回答としてマークされていない yksaila 2012年6月16日 6:00
    • 回答としてマーク yksaila 2012年7月23日 15:02
    2012年6月5日 5:48
    モデレータ
  • 確かに共通のイベントハンドラを作成して個別にハンドリングしてあげても良いですが、
    mars12さんも触れているカスタムコントロール(ユーザーコントロール, 継承コントロール)を作成するべきだと思います。

    100%完全に共通な処理であれば、共通イベントハンドラを作成しても良いですが、
    例えば桁数制御が個々に微妙に異なっていたりした場合、結局扱いづらくなってしまいます。

    その様な時でも、例えばSystem.Windows.Forms.TextBoxを継承したコントロールを作成し、
    InputableLengthプロパティの様な物を定義してBrowsableに設定します。
    この上でOnTextChangedイベントをオーバーライドしてあげれば、用途毎に条件を切替えての制御も可能です。

    namespace 適当なネームスペース名
    {
    
    	public partial class TextBox : System.Windows.Forms.TextBox
    	{
    
    		private int m_inputableLength = 0;
    
    		[Browsable(false), Category("拡張"), DefaultValue(0), ReadOnlyAttribute(false), Description("入力可能桁長です。")]
    		public int InputableLength
    		{
    			get { return m_inputableLength; }
    			set { m_inputableLength = value; }
    		}
    
    		protected override void OnTextChanged(EventArgs e)
    		{
    			if (m_inputableLength > 0 && this.Text.Length > m_inputableLength)
    			{
    				MessageBox.Show(m_inputableLength.ToString() + "文字を超える入力は、できません。");
    				this.Clear();
    			}
    			else
    			{
    				base.OnTextChanged(e);
    			}
    		}
    
    	}
    }
    
    ・・・C#で書いちゃった。

    とにかく、こんな風にして作成したクラスをライブラリ化(dll)にし、それを参照してあげれば通常のTextBoxと同様に
    ツールボックスからFormに追加した上で、プロパティでInputableLengthが指定出来る様に出来ます。

    長々とスイマセン・・・
    VB化は時間が取れたらという事でご了承下さい。

    2012年6月5日 7:22
  • > mars12さんも触れているカスタムコントロール(ユーザーコントロール, 継承コントロール)を作成するべきだと思います。

    複数の画面で同じ処理を行うなら継承コントロールを作るのも一つの手ですが、必ずしも最適解とは言い切れないと思います。拡張メソッドを使う方法もありますし。もし一つの画面しか入力検証を行わない場合、わざわざ継承コントロールを増やすのもいかがなものかと。ケースバイケースだと思います。

    以下、拡張メソッドを使う例です。

    Imports System.Runtime.CompilerServices

    Public Module ComboBoxValidator
        <Extension()>
        Public Sub ValidateMaxLength(cb As ComboBox, maxLength As Int32)
            AddHandler cb.TextChanged,
                Sub(sender, e)
                    If cb.Text.Length > maxLength Then
                        MessageBox.Show(String.Format("{0}文字を超える入力できません。", maxLength))
                        cb.Text = ""
                    End If
                End Sub
        End Sub
    End Module

    Public Class Form1
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            Me.ComboBox1.ValidateMaxLength(50)
            Me.ComboBox2.ValidateMaxLength(30)
            Me.ComboBox3.ValidateMaxLength(50)
        End Sub
    End Class



    ひらぽん http://d.hatena.ne.jp/hilapon/




    • 編集済み ひらぽんModerator 2012年6月5日 8:30 モジュールの名称を変えました
    • 回答としてマーク yksaila 2012年6月15日 8:22
    • 回答としてマークされていない yksaila 2012年6月16日 6:00
    • 回答としてマーク yksaila 2012年7月23日 15:03
    2012年6月5日 8:20
    モデレータ
  • 拡張メソッドも出ていますが一応・・・

    Namespace 適当なネームスペース名
    
    	Public Partial Class TextBox
    		Inherits System.Windows.Forms.TextBox
    
    		Private m_inputableLength As Integer = 0
    
    		<Browsable(False), Category("拡張"), DefaultValue(0), ReadOnlyAttribute(False), Description("入力可能桁長です。")> _
    		Public Property InputableLength() As Integer
    			Get
    				Return m_inputableLength
    			End Get
    			Set
    				m_inputableLength = value
    			End Set
    		End Property
    
    		Protected Overrides Sub OnTextChanged(e As EventArgs)
    			If m_inputableLength > 0 AndAlso Me.Text.Length > m_inputableLength Then
    				MessageBox.Show(m_inputableLength.ToString() & "文字を超える入力は、できません。")
    				Me.Clear()
    			Else
    				MyBase.OnTextChanged(e)
    			End If
    		End Sub
    
    	End Class
    End Namespace
    

    2012年6月5日 10:48
  • 現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(A・B・C)を書いています。

     ここから、A,B,C はまったく同じコードだと思ったのですが、TextBox, ComboBox, NumericUpDown で違うコードのように思います。複数の TextBox に対して同じコードを書いている、ということでしょうか。

     複数のイベントを一つのメソッドに結び付けるには、後に Handles を延々と書いていく方法もありますが、AddHandler ステートメントを使う方法もあります。こちらも AddHandler ステートメントを延々と書く方法の他に、Form から Controls を列挙して特定の条件に一致するか調べ、一致するなら登録していく、という方法もあります。


     ComboBox についてですが、『コンボボックスのデータ選択で、入力してください。直接入力はできません。』というメッセージから想像すると、DropDownStyle プロパティを DoropDownList にすれば不要になるのではないでしょうか。


     「数字以外は入力できない」から NumericUpDown コントロールだと思っていましたが、TX_S_TrustPeriod は、TextBox ですか?そうであれば、NumericUpDown コントロールを検討してみて下さい。これだと「数字以外か」のチェックも「桁数を超えるか」のチェックも不要です。値は Decimal 型なので、若干注意が必要です。


    Jitta@わんくま同盟

    2012年6月5日 13:22
  • > 現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(A・B・C)を書いています。
    > 各十数個になります。煩雑ですし、第一エレガントでない。 それに、コード・チェックの邪魔です。 ですから、これを、一括の指示で済ませたいのです。

    Handles 句に複数のコントロールのイベントを指定できます。以下 6つのTextBox で同じイベントハンドラを共有する例です。他 AddHandler を使って制御する方法もあります。

    Private Sub TX_BranchNo_TextChanged(sender As System.Object, e As System.EventArgs) _
    	Handles txtBox1.TextChanged, txtBox2.TextChanged, 
    		txtBox3.TextChanged, txtBox4.TextChanged,
    		txtBox5.TextChanged, txtBox6.TextChanged
    
    	Dim textBox = TryCast(sender, TextBox)
    	If Len(textBox.Text) > 50 Then
    		MessageBox.Show("50文字を超える入力は、できません。 ")
    		textBox.Text = ""
    	End If
    End Sub


    ひらぽん http://d.hatena.ne.jp/hilapon/



    ひらぽんさんへ

    参考コード、ありがとうございました。 早速やってみました。 うまくいきました。 数えてみたら、TextBOX,ComboBox等々で、関連コード(同じコード)が計90個前後(!)ありました。 「Dim textBox = TryCast(sender, TextBox)」の使い方を、よく知らなかったみたいです。 今回で学習でき、TryCastも理解できました。

    TextBOx--文字型(3)、数値型(3);ComboBOx--(2)に収まりました。 (括弧内の数字は、コードの個数「Sub---End Sub」)、もっと少なく収められるかもしれませんが、今のところこれでOKとしておきます。 AddHandlerも少しは勉強したので、これからやってみます。

    カスタムコントロール(ユーザーコントロール, 継承コントロール)作成も、これからです。 

    というわけで、このスレッドの回答は未としておきます。 Addhanndler,コントロールの作成ができたら、完了です。

    YKsaila

    2012年6月7日 2:13
  • この例では問題はでないと思いますが念のため。

    TryCastで変換した場合は、変換後の値がNothingであないか必ず確認するようにしましょう。Nothingであるか確認せずに利用すると、どこかでNullReferenceExceptionが発生することになり、例外の原因を突き止めにくくなってしまいます。

    キャストできない=例外という場合は、DirectCastを利用したほうが良いです。この場合、変換に失敗するとInvalidCastExceptionが発生してくれます。

    2012年6月7日 2:28
  • 現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(A・B・C)を書いています。

     ここから、A,B,C はまったく同じコードだと思ったのですが、TextBox, ComboBox, NumericUpDown で違うコードのように思います。複数の TextBox に対して同じコードを書いている、ということでしょうか。

     複数のイベントを一つのメソッドに結び付けるには、後に Handles を延々と書いていく方法もありますが、AddHandler ステートメントを使う方法もあります。こちらも AddHandler ステートメントを延々と書く方法の他に、Form から Controls を列挙して特定の条件に一致するか調べ、一致するなら登録していく、という方法もあります。


     ComboBox についてですが、『コンボボックスのデータ選択で、入力してください。直接入力はできません。』というメッセージから想像すると、DropDownStyle プロパティを DoropDownList にすれば不要になるのではないでしょうか。


     「数字以外は入力できない」から NumericUpDown コントロールだと思っていましたが、TX_S_TrustPeriod は、TextBox ですか?そうであれば、NumericUpDown コントロールを検討してみて下さい。これだと「数字以外か」のチェックも「桁数を超えるか」のチェックも不要です。値は Decimal 型なので、若干注意が必要です。


    Jitta@わんくま同盟

    Jittaさんへ

    回答ありがとうございます。

    >複数の TextBox に対して同じコードを書いている、ということでしょうか。

    まったく同じコードの羅列もありますが、微妙に違うものもあります。 Handlesのあとに列挙する方法では、2、3種類にわけて書くことで解決しました(少し、コード群がすっきりしてきました。 まだ、不十分ですが)。 AddHandler と コントロールの作成は、これから取り組みます。

    > ComboBox についてですが、『コンボボックスのデータ選択で、入力してください。直接入力はできません。』というメッセージから想像すると、DropDownStyle プロパティを DoropDownList にすれば不要になるのではないでしょうか。

    実は以前これをやったのですが、サーバーデータの表示ができなくなったのです。 それと、コンボボックスのデータソースがサーバーからのデータテーブルの場合は、型の相違のエラーコメントがでたりして、かえってややこしくなりました(私のやり方が間違えていたのかもしれませんが)。

    サーバーデータの表示でないところも、コンボ自体はDropDownList で表示しますが、他の関連表示が変化しません、たぶんサーバーデータと連結しているところがあるからかもしれません。

    >「数字以外は入力できない」から NumericUpDown コントロールだと思っていましたが、TX_S_TrustPeriod は、TextBox ですか?そうであれば、NumericUpDown コントロールを検討してみ>て下さい。これだと「数字以外か」のチェックも「桁数を超えるか」のチェックも不要です。値は Decimal 型なので、若干注意が必要です。

    TX_S_TrustPeriod は、TextBoxです。 期間(日数)を数字で入力します。 100とか、300とかです。 NumericUpDown コントロールは、現在作成中のフォームでは出てこないの(不要)ですが、他のフォームの作成で使用しようと思います。

    いやあ、先は長そうです! みなさん、親切に教えてくださるので、本当に助かっています。

    YKsaila




    • 編集済み yksaila 2012年6月7日 3:49
    2012年6月7日 2:36
  • かるあさんへ

    ありがとうございます。 Nothingの場合もあるかもしれませんので、用心のため ”DirectCast” を使用することにします。

    YKsaila

    2012年6月7日 3:44
  • ひらぽんさんへ

    AddHandlerも、やってみました。

    残るは、カスタムコントロールの作成です。 忙しいので、これは来週取り組みます(6/11以降)

    (A)
        Private Sub textBoxStr_TextChanged(sender As System.Object, e As System.EventArgs)
            Dim textBoxStr = DirectCast(sender, TextBox)
            If Len(textBoxStr.Text) > 51 And textBoxStr.Name <> "TX_S_InvoiceMemo" And textBoxStr.Name <> "TX_S_CloseDayMemo" Then
                MessageBox.Show("50文字を超える入力は、できません。 ")
                textBoxStr.Text = Mid(textBoxStr.Text, 1, 50) & "-"
                Exit Sub
            ElseIf Len(textBoxStr.Text) > 101 Then
                MessageBox.Show("100文字を超える入力は、できません。 ")
                textBoxStr.Text = Mid(textBoxStr.Text, 1, 100) & "-"
                Exit Sub
            End If

        End Sub


    (B) 'Form_Load(--フォームを開く)に、以下のコードを追加
        (例)
         AddHandler TX_KanaCode.TextChanged, AddressOf textBoxStr_TextChanged

            -----
         AddHandler TX_S_InvoiceMemo.TextChanged, AddressOf textBoxStr_TextChanged
            -----

    YKsaila

    2012年6月7日 7:14
  • ひらぽんさんへ

    拡張メソッドを作ってやってみました。 うまくいっています。

    ただ下記の(C)---Validatingを、Moduleの中に組み込めなかったので、Validatingは、AddHandler使用となりました。

    --Validatingも(A)の中にあれば、メソッドとして使用できますよね。 いろいろやったのですが、ダメでした。

    (C)のコードを、Module(A)に組み込む方法を教えてくださいませんか? 

    以下(AとB)、作成コードです:

    (A)Module:TextBoxValidator作成
    Imports System.Runtime.CompilerServices
    Public Module TextBoxValidator
        ’数値用
      <Extension()>
        Public Sub textBoxN_MaxLength(cb As TextBox, maxLength As Int32)
            AddHandler cb.TextChanged,
            Sub(sender, e)
                If Not IsNumeric(cb.Text) And cb.Text <> "" Then
                    MessageBox.Show("ここは、数字以外のものは入力できません。 ")
                End If

                If cb.Text.Length > maxLength Then
                    MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", maxLength))
                    cb.Text = ""
                 End If
            End Sub
        End Sub

      ’文字用
       <Extension()>
        Public Sub textBoxStr_MaxLength(cb As TextBox, maxLength As Int32)
            AddHandler cb.TextChanged,
            Sub(sender, e)
               If cb.Text.Length > maxLength + 1 Then
                    MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", maxLength))
                    cb.Text = Mid(cb.Text, 1, maxLength) & "-"
               End If
            End Sub

        End Sub

    End Module


    (B)Class Form1の先頭に、”Imports System.Runtime.CompilerServices”をいれる(ないと,エラーでる)。
    Imports System.Runtime.CompilerServices
    Public Class Form1
         -----略
      Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load 
         -----略
        Me.TextBox1.textBoxN_MaxLength(10)      '数値
        Me.TextBox2.textBoxStr_MaxLength(50)    '文字   
        
          ------略
       End Sub

    End Class

    (C)--以下を、(A)の中に入れたいのです。 微妙に、うまくいきません。
       Private Sub textBoxN_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs)
            Dim textBoxN = DirectCast(sender, TextBox)
            If textBoxN.Text = "" Then
                Exit Sub
            End If

            If Not IsNumeric(textBoxN.Text) Then
                MessageBox.Show("数値を入力してください。 入力しないなら、0 にしてください。")
                e.Cancel = True
                textBoxN.Select()
            End If
        End Sub

     よろしく、お願いします。

    YKsaila



    • 編集済み yksaila 2012年6月8日 7:12
    2012年6月8日 7:11
  • > Validatingも(A)の中にあれば、メソッドとして使用できますよね。 いろいろやったのですが、ダメでした。

    こちらでは実装および動作してますが、どこが問題だったのでしょうか?

    Option Explicit On
    Option Strict On
    
    Imports System.Runtime.CompilerServices
    
    Public Module TextBoxValidator
        ''' <summary>
        ''' 入力文字が数値であることと文字数を検証します。
        ''' </summary>
        ''' <remarks>メソッド名を意味のある名前に変えてます。</remarks>
        <Extension()>
        Public Sub ValidateMaxLengthAndNumeric(textBox As TextBox, maxLength As Int32)
            AddHandler textBox.TextChanged,
            Sub(sender, e)
                If Not IsNumeric(textBox.Text) And textBox.Text <> "" Then
                    MessageBox.Show("数字以外の文字は入力できません。")
                End If
    
                If textBox.Text.Length > maxLength Then
                    MessageBox.Show(String.Format("{0}文字を超える入力はできません。", maxLength))
                    textBox.Text = ""
                End If
            End Sub
        End Sub
    
        ''' <summary>
        ''' 文字数を検証します。
        ''' </summary>
        <Extension()>
        Public Sub ValidateMaxLength(textBox As TextBox, maxLength As Int32)
            AddHandler textBox.TextChanged,
            Sub(sender, e)
                If textBox.Text.Length > maxLength Then
                    MessageBox.Show(String.Format("{0}文字を超える入力はできません。", maxLength))
                    textBox.Text = Mid(textBox.Text, 1, maxLength) & "-"
                End If
            End Sub
        End Sub
    
        ''' <summary>
        ''' 数値か検証します。
        ''' </summary>
        <Extension()>
        Public Sub ValidateNumeric(textBox As TextBox)
            AddHandler textBox.Validating,
                Sub(sender, e)
                    If textBox.Text = "" Then
                        Return
                    End If
    
                    If Not IsNumeric(textBox.Text) Then
                        MessageBox.Show("数値を入力してください。入力しない場合 0 にしてください。")
                        e.Cancel = True
                        textBox.Select()
                    End If
                End Sub
        End Sub
    End Module
    
    Public Class Form1
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Me.TextBox1.ValidateMaxLengthAndNumeric(10) ' 数値および文字数制限 
            Me.TextBox2.ValidateMaxLength(50)           ' 文字文字数制限
            Me.TextBox3.ValidateNumeric()               ' 数値
        End Sub
    End Class


    ひらぽん http://d.hatena.ne.jp/hilapon/

    • 回答としてマーク yksaila 2012年7月23日 15:05
    2012年6月8日 8:27
    モデレータ
  •  ひらぽんさんへ

    できました、ありがとうございました。

    以前の私のコード(下記記載)は、(1)と(2)がいけなかったようです。

    (1)--- ByVal e As System.ComponentModel.CancelEventArgsが不要?

    (2)---AddHandler cb.Validating,← 末尾の「,」に波線がでます。

    お手本のように、(1)でByVal e As System.ComponentModel.CancelEventArgsを除き、 Sub(sender, e)--End Subを加えるべきだったのですね。

    YKsaila

    <下記、間違いコード>

    Private Sub textBoxN_Validating(cb As TextBox, ByVal e As System.ComponentModel.CancelEventArgs) ----(1)
             AddHandler cb.Validating,----(2)

            If cb.Text = "" Then
                Exit Sub
            End If

            If Not IsNumeric(cb.Text) Then
                MessageBox.Show("数値を入力してください。  入力しないなら、0 にしてくださいよ。")
                e.Cancel = True
                cb.Select()
            End If
        End Sub

    • 回答としてマーク yksaila 2012年7月23日 15:04
    2012年6月8日 10:09
  • 拡張メソッドも出ていますが一応・・・

    Namespace 適当なネームスペース名
    
    	Public Partial Class TextBox
    		Inherits System.Windows.Forms.TextBox
    
    		Private m_inputableLength As Integer = 0
    
    		<Browsable(False), Category("拡張"), DefaultValue(0), ReadOnlyAttribute(False), Description("入力可能桁長です。")> _
    		Public Property InputableLength() As Integer
    			Get
    				Return m_inputableLength
    			End Get
    			Set
    				m_inputableLength = value
    			End Set
    		End Property
    
    		Protected Overrides Sub OnTextChanged(e As EventArgs)
    			If m_inputableLength > 0 AndAlso Me.Text.Length > m_inputableLength Then
    				MessageBox.Show(m_inputableLength.ToString() & "文字を超える入力は、できません。")
    				Me.Clear()
    			Else
    				MyBase.OnTextChanged(e)
    			End If
    		End Sub
    
    	End Class
    End Namespace

    aviator_さんへ

    ありがとうございます。 やっと、カスタム・コントロールまで来ました。

    さて、質問です。

    1.上記のNamespace ~ End Namespace は、どこへいれればよいですか? 私は、すでにあるClass1(Form1ではないです)やModule1の先頭に入れたのですが。 どこでも、先頭であれば、Class Form1の先頭でもよいのでしょうか?

    2.<Browsable(False), Category("拡張"), DefaultValue(0), ReadOnlyAttribute(False), Description("入力可能桁長です。")> _の行に波線(エラー表示)がでます。 行の初めに「'」を書いたのですが、当然おかしくなりますよね?

    3.うまくいきませんので(カスタム・コントロールが使用できません)、もう少し具体的な例をいただけないでしょうか?

    ここは、ぜひマスターしたいところなので、ご指導をお願いします。

    YKsaila

    2012年6月8日 10:21
  • 1. 
    FormやControlのようなSystem.ComponentModel.Componentを継承するクラスは一つのファイルに一つのクラスのみ記述した方が良いでしょう。
    Visual Studioのデザイナーはファイルの最も先頭にあるSystem.ComponentModel.Component.Componentを継承するクラスを表示しますから、一つのファイルにFormや自作のコントロールが定義されているとややこしくなります。(場合によってはデザイナーでエラーになります)
    また、ファイル名はクラス名と同じにした方がわかりやすいでしょう。

    なので今回の場合だとTextBox.vbというファイルをプロジェクトに追加して、そこにコードを貼り付けてみてください。


    2. 
    ファイルの先頭にでもImports System.ComponentModelを追加してください。


    3.
    上記の修正を適用してビルドすれば、普通のテキストボックスを使うのと同じ感覚でツールボックスの一覧から選択してフォーム上に配置できるはずです。

    2012年6月8日 12:23
  • NF64さんへ、ありがとうございます。 

    新しいクラスを追加作成してやってみました(名称:MyTextBox)。 で、下記のとおりに、書きました。

    「<Browsable(False), Category("拡張"),DefaultValue(0), ReadOnlyAttribute(False), Description("入力可能桁長です。")> 」 は、正常になりました。

    しかし、ツールボックスには、なにも追加表示されません(フォームのデザイナー画面で)。 どうしてでしょうか? InPutableLengthは出てきますが、先には進めません。

    実は、今気付いたのですが、別件でMyComboBoxというクラスを追加しそこで自作のコンボボックス(二列表示)を作成する予定でした。 すぐには使用しないので気にしていなかったのですが、ツールボックスにMyComboBoxという自作のコンボボックスが追加されているはずなのに、ないのです(これは、別スレッドで質問します)。 これと関係あるかもしれません。

    使用しているVB は、VB2010Expressです。 なにか、設定・コードが不足していますか?

    YKsaila

    以下は、MYtextBox(追加クラス)に記載したコードです;

    Imports System.ComponentModel

    Namespace MyTextBox '適当なネームスペース名

        Partial Public Class MyTextBox
            Inherits System.Windows.Forms.TextBox

            Private m_inputableLength As Integer = 0

            <Browsable(False), Category("拡張"), DefaultValue(0), ReadOnlyAttribute(False), Description("入力可能桁長です。")> _
            Public Property InputableLength() As Integer
                Get
                    Return m_inputableLength
                End Get
                Set(value As Integer)
                    m_inputableLength = value
                End Set
            End Property

            Protected Overrides Sub OnTextChanged(e As EventArgs)
                If m_inputableLength > 0 AndAlso Me.Text.Length > m_inputableLength Then
                    MessageBox.Show(m_inputableLength.ToString() & "文字を超える入力は、できません。")
                    Me.Clear()
                Else
                    MyBase.OnTextChanged(e)
                End If
            End Sub

        End Class
    End Namespace


    • 編集済み yksaila 2012年6月11日 0:04
    2012年6月11日 0:04
  •  フォーム上のコントロールにイベントを結び付けていく方法ですが、こんな方法もあります。
     まず、名前付けのルールを決めます。最大長チェックをする TextBox には、「CheckLength」というプリフィックスを付ける、等です。このルールに沿った名前を持つコントロールを探し、見つかったものを登録していきます。………ぶいびぃむずかしぃ

    Dim stack = New Stack(Of Control)() ' Stack に特に意味なし。Queue でもその他でも
    stack.Push(Me) ' フォームから検索を始める
    While (stack.Count > 0) ' リストを操作するので、for や Foreach は使わない
    	Dim cntrl = stack.Pop() ' 一つ取り出して、
    	If (cntrl.Name.IndexOf("CheckLength") >= 0) Then ' 検索しているものか調べる
    		' イベント ハンドラを登録
    		Dim tb = DirectCast(cntrl, TextBox)
    		AddHandler tb.TextChanged, AddressOf Me.TextChangedHandler
    	Else
    		' 違っていれば、子コントロールを検索リストに登録
    		For idx As Integer = 0 To cntrl.Controls.Count - 1
    			stack.Push(cntrl.Controls(idx))
    		Next
    	End If
    End While

    Jitta@わんくま同盟

    2012年6月11日 14:02
  • yksailaさん、こんにちは。

    ひらぽんさんの回答を有益だと判断しましたので、私の方で「回答としてマーク」を付けさせていただきました。yksailaさんの意に反している場合は、yksailaさんがこの回答マークを外すことが可能です。

    このスレッドのタイトル、および主旨から判断し、それに直接合致する回答のみに「回答としてマーク」を付けさせていただきましたが、その他でyksailaさんが有益であると判断された回答がある場合は、「回答としてマーク」や「回答の候補に設定」を付けることが可能です。
    このスレッドを後日訪れた人が、素早く有益な回答に辿り着けるという側面から、もう一度見直していただければ幸いです。なお、yksailaさんに、「回答としてマーク」や「回答の候補に設定」を付けることを強要するものではありません。

    以上、よろしくお願いいたします。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年6月15日 6:55
    モデレータ
  • trapemiyaさんへ

    了解しました。 お手数を、おかけしました。

    現在、このスレッドの中の拡張メソッドに集中しています。 このスレッドの解答が数種類ありますので、全部完了した時点で、(後日訪れる人のために)まとめるつもりで、そのままにしていました。

    まとめが、できましたら、書き込みます。

    YKsaila

    2012年6月15日 7:45
  • YKsailaさんへ

    了解しました。少々、出しゃばった感も感じており、不慣れで至らないところもあると思いますが、寛大な返信をいただき、ありがとうございます。
    私もいろいろと模索中ですが、フォーラムは機械的な質問と回答が並ぶだけではなく、その中で人と人とのやり取りがあるわけですから、誰もが気持ちよく利用できるようにコミニュケーションを取っていけたらと思っています。
    今後ともよろしくお願いいたします。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年6月15日 8:44
    モデレータ
  • 現在までの解決策の状況をまとめます。

    1.イベントハンドラーの共用 ----同じ内容のPrivate Subを、一つにまとめます。

    2.Addhanndler の使用 --- Form_Load に書き込みます。

    3.拡張メソッドの使用 --- 拡張メソッドを作成しておき、Form_Load に書き込みます。

    4.カスタムコントロールの作成・使用--- この場合は、拡張メソッドも含めた複合使用が便利でした。

    5.自作関数(?)の応用-- これを、For ~ Next で回す? --- これは、未です。 数日後になります。

    どれも、もちろん有益ですが、(3)と(4)がすっきりして、かつForm_Load の中で見られるので、個人的には好みです。

    (5)が面白そうです、でも少しエラーがでますので、もう少しやってから(他もありますので)質問します。

    YKsaila

    H24/06/18記 -- (5)が、80~90%完成しています。 あとは、残りの10~20%! それと、For ~ Next ? 

    で、これが終わったら感想です!


    • 回答としてマーク yksaila 2012年6月16日 4:10
    • 回答としてマークされていない yksaila 2012年6月16日 6:01
    • 編集済み yksaila 2012年6月18日 14:12
    • 回答としてマーク yksaila 2012年7月23日 15:06
    2012年6月16日 4:10
  • 現在までの解決策の状況をまとめます。

    1.イベントハンドラーの共用 ----同じ内容のPrivate Subを、一つにまとめます。

    2.Addhanndler の使用 --- Form_Load に書き込みます。

    3.拡張メソッドの使用 --- 拡張メソッドを作成しておき、Form_Load に書き込みます。

    4.カスタムコントロールの作成・使用--- この場合は、拡張メソッドも含めた複合使用が便利でした。

    5.自作関数(?)の応用-- これを、For ~ Next で回す? --- これは、未です。 数日後になります。

    どれも、もちろん有益ですが、(3)と(4)がすっきりして、かつForm_Load の中で見られるので、個人的には好みです。

    (5)が面白そうです、でも少しエラーがでますので、もう少しやってから(他もありますので)質問します。

    YKsaila

    上記(5)の関連質問です。 

    MultiEventとういうクラスを作成し、Bind 関数(?MultiEvent内 )を使用して、複数のテキストボックス(コンボボックスも)に指示をしようと考えています。

    F_M_ClientというフォームのForm_Load 内に、以下のコードを書いたのですが、下記のエラー表示がでます。 (Form_Load の前に、Dim Multi(1) As MultiEventを書き込みます。)
    エラー内容:オブジェクト参照がオブジェクト インスタンスに設定されていません。
    Multi(0).Bind(TX_S_BillSight, 8)     ’(注) TX_S_BillSightは、テキストボックス名
    Multi(1).Bind(TX_S_TrustPeriod, 8)   ’(注) TX_S_TrustPeriodは、テキストボックス名

    どのように解決したら良いでしょうか?

    YKsaila

    以下は、MultiEventクラスのコードです:

    Public Class MultiEvent
        Public WithEvents SlaveTextBox As TextBox
        Public WithEvents MaxLength As Object '--ここでは、Integerにはできない。
        Public MaxByte As Integer

        Public Sub Bind(SlaveTextBox As TextBox, MaxLength As Integer)

            Me.SlaveTextBox = SlaveTextBox
            Me.MaxLength = MaxLength

        End Sub

        Private Sub SlaveTextBox_TextChanged()
            AddHandler SlaveTextBox.TextChanged,
            Sub(sender, e)
                If Not IsNumeric(SlaveTextBox.Text) And SlaveTextBox.Text <> "" Then
                    MessageBox.Show("ここは、数字以外のものは入力できません。 ")
                    Exit Sub
                End If

                If SlaveTextBox.Text.Length > DirectCast(MaxLength, Integer) Then
                    MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", MaxLength))
                    SlaveTextBox.Text = ""
                End If

            End Sub

        End Sub

    End Class



    • 編集済み yksaila 2012年6月16日 10:01
    2012年6月16日 5:59
  • 配列の各要素にインスタンスを作成する必要があります。
    Dim Multi(1) As MultiEventで配列が作成されていますが、その要素はNothingなのでNullReferenceExceptionが発生してしまいます。

    Multi(0) = New MultiEvent
    Multi(1) = New MultiEvent

    • 回答としてマーク yksaila 2012年7月23日 15:06
    2012年6月16日 6:42
  • 配列の各要素にインスタンスを作成する必要があります。
    Dim Multi(1) As MultiEventで配列が作成されていますが、その要素はNothingなのでNullReferenceExceptionが発生してしまいます。

    Multi(0) = New MultiEvent
    Multi(1) = New MultiEvent

    NF64さんへ

    ありがとうございました。 F_M_ClientというフォームのForm_Load 内を、以下のようにしました。 エラーは、でなくなりました。

         Multi(1) = New MultiEvent
            Multi(0).Bind(TX_S_BillSight, 8)
            Multi(1).Bind(TX_S_TrustPeriod, 8)

    しかし、テキストボックス(TX_S_BillSight、TX_S_TrustPeriod)に、数字は無制限にはいります。 目的は、8桁の入力制限なのですが。

    Private Sub SlaveTextBox_TextChanged()以下のコードに問題ありですね。 ()の中にいろいろ入れてみたり、コードを書き換えたりしているのですが、何も変化がありません。

    ようするに、無限に桁数がはいります。

    どうしたら、よいのでしょうか? お手数をおかけして、もうしわけありません。

    YKsaila

    2012年6月16日 10:00
  • > Private Sub SlaveTextBox_TextChanged()

    何か抜けていませんか?

    追加:あれ?TextBox には、MaxLength という、入力文字数の最大値を決めるプロパティがありませんでしたか?


    Jitta@わんくま同盟


    • 編集済み Jitta 2012年6月16日 11:11
    2012年6月16日 10:49
  • > Private Sub SlaveTextBox_TextChanged()

    何か抜けていませんか?

    追加:あれ?TextBox には、MaxLength という、入力文字数の最大値を決めるプロパティがありませんでしたか?


    Jitta@わんくま同盟


    >何か抜けていませんか?
    そうです、それが分からないのです。 ()の中にいろいろ入れたり、追加したり。 試行錯誤しています。 なにか、適切なコードがあるはずなのですが?

    Form_Loadの中に、Multi(0).Bind(TX_S_BillSight, 8) を書きます → テキストボックス(TX_S_BillSight) に、9文字以上入力したときにコメントをだして、特定の操作ができれば良いのです。 これが出来れば次の段階へ進めます。 数値か文字かで、コメントをを分けたり、最終的にはFOR ~Next で回して、一行で済ませるなどを考えています。

    >:あれ?TextBox には、MaxLength という、入力文字数の最大値を決めるプロパティがありませんでしたか?

    知っています。 これだけでは、不足なのです。 コメントをだしたり、入力データを消したり、または入力数超過を示す痕跡を残したり、または数値限定個所に文字入力したときの処置、等々です。

    それに、これができれば、他への応用も可能だと思います。

    YKsaila

    2012年6月16日 14:21
  • フォームにテキストボックスを置き、ダブルクリックすると以下のようなイベントハンドラーが自動的に記述されます。

    Private Sub TextBox1_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBox1.TextChanged End Sub

    さて、あなたのコードに何が足りないでしょうか。

    SlaveTextBox_TextChanged()をFormなどのイベントと同じように呼び出される事を期待しているのなら、上記と見比べて、"足りていない文"を探し出してください。
    ただ、そのままSlaveTextBox_TextChanged()に"足りていない文"を追加すると変な事になるでしょう。
    イベントハンドラーとして使用されるであろうSlaveTextBox_TextChanged()の中で、AddHandler文を用いて匿名関数をTextChangedイベントに紐付けているので。

    しかし、8文字以上入力したら、入力が全部消えちゃうってどうなんでしょう。
    正しく8文字の入力をしたつもりでも、間違えて隣のキーを押してしまう事もあるでしょうし。

    2012年6月17日 1:45
  • フォームにテキストボックスを置き、ダブルクリックすると以下のようなイベントハンドラーが自動的に記述されます。

    Private Sub TextBox1_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBox1.TextChanged End Sub

    さて、あなたのコードに何が足りないでしょうか。

    SlaveTextBox_TextChanged()をFormなどのイベントと同じように呼び出される事を期待しているのなら、上記と見比べて、"足りていない文"を探し出してください。
    ただ、そのままSlaveTextBox_TextChanged()に"足りていない文"を追加すると変な事になるでしょう。
    イベントハンドラーとして使用されるであろうSlaveTextBox_TextChanged()の中で、AddHandler文を用いて匿名関数をTextChangedイベントに紐付けているので。

    しかし、8文字以上入力したら、入力が全部消えちゃうってどうなんでしょう。
    正しく8文字の入力をしたつもりでも、間違えて隣のキーを押してしまう事もあるでしょうし。

    >しかし、8文字以上入力したら、入力が全部消えちゃうってどうなんでしょう。 正しく8文字の入力をしたつもりでも、間違えて隣のキーを押してしまう事もあるでしょうし。

    その通りですね。 「入力が全部消えちゃう」というのは、一応便宜的にやっているだけなのです。

    発端は、MaxLengthに最大入力文字数を入れるだけだと、入力した人が最大入力数を超えて入力した場合に、それに気づかずに通り過ぎてしまう点だったのです。

    実際の入力は、慣れた人だと相当な速さでしますので、制限を超えたのに気付かないこともあり得る、と想像したのです。 

    つまり、Maxlength=10 として、本人は11文字入力したつもりで、そのまま次の入力へ行く、ということが考えられたのです。 これを防ぐために、コメントを出すことから始めました。

    TextBoxの入力の型:

    文字の場合:コメントをだし、入力文字の最後に印(たとえば、”-”)をつけて置く(あとで見たとき、入力文字数不足だったことが分かります)。

    数字のみの場合:これがむずかしい。 数字以外にはいれられないので、いまのところ消すしかない? 

    もうひとつのやり方は、制限を超えた入力の場合は、コメントをだし、そこから抜けられないようにする手があるかも? これは、まだ試していませんが、良い方法かもしれません。

    今やっていることは、アイデア倒れになるかもしれませんが、ゆくゆくは自分のためになると思っています。

    しかし、だんだん面倒に感じてきました。 技術的に、私には、少しむずかしい。 でも、ここで諦めずに、もうひと踏ん張りでしょうか?

    YKsaila


    • 編集済み yksaila 2012年6月17日 2:23
    2012年6月17日 2:15
  • 発端は、MaxLengthに最大入力文字数を入れるだけだと、入力した人が最大入力数を超えて入力した場合に、それに気づかずに通り過ぎてしまう点だったのです。

    実際の入力は、慣れた人だと相当な速さでしますので、制限を超えたのに気付かないこともあり得る、と想像したのです。 

    つまり、Maxlength=10 として、本人は11文字入力したつもりで、そのまま次の入力へ行く、ということが考えられたのです。 これを防ぐために、コメントを出すことから始めました。

     私の従業員番号は8桁なのですが、ゼロでリードして10桁入力しなければならない場合があります。ところで、あまり入力する機会がないので覚えておらず、IME に「ばんごう」の読みで10桁登録しています。もし、8桁入力のところで変換で入力すると、必要なところが削られてしまうのですね。。。


    そうです、それが分からないのです。 ()の中にいろいろ入れたり、追加したり。 試行錯誤しています。 なにか、適切なコードがあるはずなのですが?

     なぜ「Bind」というメソッドを用意したのでしょう?何かと何かを結びつけるためですよね。では、SlaveTextBox_TextChanged が、イベント条件が整ったときに実行するように結びつけるコード(VB の場合は句もあり)は、何でしょう?


    Jitta@わんくま同盟

    2012年6月17日 8:15
  • Jittaさんへ

    >私の従業員番号は8桁なのですが、ゼロでリードして10桁入力しなければならない場合があります。ところで、あまり入力する機会がないので覚えておらず、IME に「ばんごう」の読みで10桁登録してい>ます。もし、8桁入力のところで変換で入力すると、必要なところが削られてしまうのですね。。。

    状況が良く分かりませんが、私の個人的な意見としては:

    あるテキストボックスの入力値が、数字で8桁であるなら、それ以外は入力を受けないようにするのが、親切なソフトだとおもっています。 7桁以下、9桁以上は入力不可にします(もちろん、文字も不可)。

    できれば、コメントもだす(コメントがうるさいのなら、ださない)。

    YKsaila

    2012年6月18日 13:32
  • NF64さんへ

    なんとか、Bind 関数の利用ができたようです。 いやあ、相当な時間がかかりました!

    テキストボックスのほうは、Bind は完成しています。 欲張って、コンボボックスのValidatingまで手をのばしたのですが、これが現在止まっています。

    コンボまでBind が使えるようになれば、この先便利なのですが。 エラー修正のご指導をいただければ幸いです。 エラー個所は、(A),(B)です。

    よろしく、お願いします。

    YKsaila

    以下、手順です。 

    (1)Dim Multi(2) As MultiEvent  --- Form_Loadの外に書く

    (2) Form_Loadの中に書く
         Multi(0) = New MultiEvent
            Multi(1) = New MultiEvent

            Multi(0).Bind(TX_S_BillSight, 6)      ’テキストボックス、成功   TX_S_BillSight---テキストボックス名 制限入力字数=6
            Multi(1).Bind(TX_S_TrustPeriod, 6)  ’テキストボックス、成功  TX_S_TrustPeriod---テキストボックス名 制限入力字数=6

           Multi(2) = New MultiEvent

         Multi(2).Bind(CB_S_Payment, 0)    ’ (A) コンボボックス、エラー、意図的に Bind(----,0)として、コンボボックスのValidatingを図ります。

                                      ’(3)の最後のほうを見てください。

    (3)クラス作成
    Public Class MultiEvent
        Public SlaveControl As Control
        Public WithEvents SlaveTextBox As TextBox
        Public WithEvents SlaveComboBox As ComboBox
        Public Multi_MaxLength As Integer
        Public Sub Bind(SlaveControl As Control, Multi_MaxLength As Integer)
            If TypeOf SlaveControl Is TextBox Then
                Me.SlaveTextBox = DirectCast(Me.SlaveTextBox, TextBox)
                AddHandler SlaveControl.TextChanged,
                Sub(sender, e)
                    If Not IsNumeric(SlaveControl.Text) And SlaveControl.Text <> "" Then
                        MessageBox.Show("ここは、数字以外のものは入力できません。 ")
                        Exit Sub
                    End If

                    If SlaveControl.Text.Length > Multi_MaxLength Then
                        MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", Multi_MaxLength))
                        SlaveControl.Text = ""   ’この辺は、あとでもっと良いものに変更します。
                    End If
                End Sub

                AddHandler SlaveControl.Validating,
                Sub(sender, e)
                    If SlaveControl.Text = "" Then
                        Return
                    End If

                    If Not IsNumeric(SlaveControl.Text) Then
                        MessageBox.Show("数値を入力してください。 入力しない場合、0 にしてください。")
                        e.Cancel = True
                        SlaveControl.Select()
                        Exit Sub
                    End If
                End Sub

            ElseIf TypeOf SlaveControl Is ComboBox Then
                Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)
                If Multi_MaxLength = 0 Then
                    AddHandler SlaveControl.Validating,
                     Sub(sender, e)
                         If Me.SlaveComboBox.SelectedIndex = -1 Or Me.SlaveComboBox.Text = "" Then    ’(B) ここで、エラー表示でます。
                             MessageBox.Show("コンボボックスのデータ選択で、入力してください。 直接入力はできません。 移動するには、まず選択してください。 ")
                             e.Cancel = True
                             Me.SlaveComboBox.Select()
                         End If
                     End Sub
                End If
            End If

        End Sub
     End Class




    • 編集済み yksaila 2012年6月18日 14:07
    • 回答としてマーク yksaila 2012年7月23日 15:08
    2012年6月18日 13:54
  • 起きているエラーというのはNullReferenceExceptionですか?
    どのような状況で、どのようなエラーが発生しているのか内容を明記しましょう。

    Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)

    「Me.SlaveComboBoxにパラメーターとして与えられたComboBox(つまるところSlaveControl)を保持させたい。」という意図があると思いますが、上記のコードではそのようになっていません。
    TextBoxの方も同じ状態になっていますから、修正が必要でしょう。

    既にアドバイスもありましたが、やはりこのようなコードを書くよりも何とかしてComboBoxをDropDownListにする方が良い気がしてなりません。


    • 編集済み NF64 2012年6月20日 2:53 文章が分かりにくかったので修正しました。
    • 回答としてマーク yksaila 2012年7月23日 15:08
    2012年6月20日 1:29
  • エラーの原因に関してはNF64さんの回答で合ってると思いますので別の話を・・・

    「Public SlaveControl As Control」 と 「Public Sub Bind(SlaveControl As Control, Multi_MaxLength As Integer)」 において、
    変数とパラメータの定義名が重複しています。

    合わせて、Public変数に不要な物がいませんか?
    コンパイルエラーにはなりませんが、バグや誤読に繋がるので気を付けた方が良いと思います。

    変数定義はスコープを意識した定義を心がけた方が良いです。
    ※クラスのメンバ変数にするのであればPublicにするかPrivateにするか、
      メソッド内変数にしても、If文の中でしか使わない変数はIf文の中で定義する等。
    そうする事で、参照出来るべきでない所から見えてしまうといった事が防げます。

    あとこれは好みですが、メンバ変数とパラメータ、メソッド内変数については、
    名前の付け方にルールを設けておくと誤読が防げます。(プレフィックスを付ける等)
    ※社内開発であれば開発規約等ありませんか?

    質問と関係無い話なので、興味が無ければスルーして下さい。

    2012年6月20日 2:49
  • NF64さんへ

    回答ありがとうございます。

    急に忙しくなり、中断しています。 ここを開くと、集中してしまい、他の仕事ができなくなります(おろそかになります)ので、開けていませんでした。

    今も、深く読んでいない状況です。 読むと、他へいけなくなりますから。 今やっていることが一段落しましたら、集中して取り組みます。

    復帰は、来週の後半か7月初めに、なるかもしれません。

    その時は、よろしくお願いします。

    YKsaila

    2012年6月24日 2:56
  • aviator_さんへ

    回答ありがとうございます。

    急に忙しくなり、中断しています。 ここを開くと、集中してしまい、他の仕事ができなくなります(おろそかになります)ので、開けていませんでした。

    今も、深く読んでいない状況です。 読むと、他へいけなくなりますから。 今やっていることが一段落しましたら、集中して取り組みます。

    復帰は、来週の後半か7月初めに、なるかもしれません。

    その時は、よろしくお願いします。

    YKsaila

    2012年6月24日 2:57
  • 起きているエラーというのはNullReferenceExceptionですか?
    どのような状況で、どのようなエラーが発生しているのか内容を明記しましょう。

    Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)

    「Me.SlaveComboBoxにパラメーターとして与えられたComboBox(つまるところSlaveControl)を保持させたい。」という意図があると思いますが、上記のコードではそのようになっていません。
    TextBoxの方も同じ状態になっていますから、修正が必要でしょう。

    既にアドバイスもありましたが、やはりこのようなコードを書くよりも何とかしてComboBoxをDropDownListにする方が良い気がしてなりません。


    NF64さんへ、

    やっと、ここへ戻ってこれました。 半月ぶりです。 

    >起きているエラーというのはNullReferenceExceptionですか?

    そうです。 

    コンボ内をわざと空白にしたり、余計な文字を入力するとなります。 ”オブジェクト参照がオブジェクト インスタンスに設定されていません。” の表示が出ます。

    ほぼ同じ内容でModule内に書いたコードでは、問題なく使えたのですが(別のプログラムで、です)。

    また、Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)は、どのように修正したらよいですか、すいません教えていただけますか?

    >既にアドバイスもありましたが、やはりこのようなコードを書くよりも何とかしてComboBoxをDropDownListにする方が良い気がしてなりません。

    それも、もう一回やってみますが、ComboBoxのデータがサーバーから引き出してくるときはうまくいかなかったのです(たんに私のやり方が間違っているだけなのかもしれませんが)。

    それに、コメントも出したいし、今やっていることは、なにより私の勉強のためでもあります。

    やっていたことを思い出すのに、少し時間がかかります。

    YKsaila

    2012年7月4日 14:37
  • エラーの原因に関してはNF64さんの回答で合ってると思いますので別の話を・・・

    「Public SlaveControl As Control」 と 「Public Sub Bind(SlaveControl As Control, Multi_MaxLength As Integer)」 において、
    変数とパラメータの定義名が重複しています。

    合わせて、Public変数に不要な物がいませんか?
    コンパイルエラーにはなりませんが、バグや誤読に繋がるので気を付けた方が良いと思います。

    変数定義はスコープを意識した定義を心がけた方が良いです。
    ※クラスのメンバ変数にするのであればPublicにするかPrivateにするか、
      メソッド内変数にしても、If文の中でしか使わない変数はIf文の中で定義する等。
    そうする事で、参照出来るべきでない所から見えてしまうといった事が防げます。

    あとこれは好みですが、メンバ変数とパラメータ、メソッド内変数については、
    名前の付け方にルールを設けておくと誤読が防げます。(プレフィックスを付ける等)
    ※社内開発であれば開発規約等ありませんか?

    質問と関係無い話なので、興味が無ければスルーして下さい。

    AVIATOR_さんへ

    おひさしぶりです、半月ぶりにここを開けました(仕事で忙しかったのです)。 

    下記のような変更でよいでしょうか?

     Public SlaveControl As Control → これは、省く
     Public WithEvents SlaveTextBox As TextBox → 先頭のPublicを、Privateにする? Private で、いいのでは? ダメですか?
     Public WithEvents SlaveComboBox As ComboBox → 先頭のPublicを、Privateにする?
     Public Multi_MaxLength As Integer  → これは、省く

    >変数定義はスコープを意識した定義を心がけた方が良いです。

    おっしゃるとおりです。 もっと気をつけるべきですね。

    >あとこれは好みですが、メンバ変数とパラメータ、メソッド内変数については、
    >名前の付け方にルールを設けておくと誤読が防げます。(プレフィックスを付ける等)
    そのとおりですが、つい夢中になり忘れてしまいます。 あとから、やりなおそうとか。 初めに規則を作って参照しながらやるべきかもしれないですね。

    >※社内開発であれば開発規約等ありませんか?

    本職ではありませんので、社内開発ではありません。 自己挑戦のために、頑張っています。 VBでちゃんとしたソフトをつくりたいのです。

    YKsaila

    2012年7月4日 14:50
  • SlaveComboBoxはBind()を呼び出した際に与えられたコントロールを保持しておくための変数ですよね。
    初めてBind()が呼び出された時にはSlaveComboBoxはNull(Nothing)です。
    なので、Bind()の引数であるSlaveControlをSlaveComboBoxに代入する必要があるでしょう。

    しかし、あなたのコードでは

    Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)

    となっています。
    これでは「ComboBoxクラスであるSlaveComboBoxにSlaveComboBoxをComboBoxクラスにキャストして代入する」という意味のない事を行っています。
    しかし、SlaveComboBoxはNothingなので結果もやはりNothingです。SlaveComboBoxは永久にNothingのままになっています。

    あなたが期待する動作としては

    Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)

    これで実現できるでしょう。

    折角Visual Studioを使っているのですから、デバッガーを使用しましょう。
    この規模なら十分に目視で動作を追うことができますが、もっと規模が大きくなってくると難しくなってきます。
    デバッガーを使用すれば1ステップずつ動作を追っていく事ができますし、その時点で変数の値がどうなっているのかも確認できます。

    使用方法などはWebで検索すれば出てくるでしょう。

    2012年7月5日 1:24
  • 変わってないデス。

    Me.SlaveComboBox = DirectCast(Me.SlaveControl, ComboBox)
    かと。
    • 回答としてマーク yksaila 2012年7月23日 15:09
    2012年7月5日 4:39
  • 気付いてませんでしたorz
    ご指摘ありがとうございます。

    しかし、SlaveControlはパラメーターなのでMe要らないですね。
    正しくは

    Me.SlaveComboBox = DirectCast(SlaveControl, ComboBox)

    でした。

    • 回答としてマーク yksaila 2012年7月23日 15:09
    2012年7月5日 5:23
  • 消した気でいました(汗

    にしても・・・

    やっぱり誤読しましたね・・・

    2012年7月5日 6:04
  • NF64さんへ

    丁寧な解説ありがとうございます。 私が、基本的なことが分かっていなかったのですね。

    良く理解しました。 これで、問題なく動きました。  勉強になりました。 デバッガー使用もできないといけないですね。 

    少し、やる気が増してきました。

    本文のコードの間違いも、Me.SlaveComboBox = DirectCast(SlaveControl, ComboBox)としておきました。

    Aviator_さん、ありがとうございます。

    YKsaila

    2012年7月9日 0:19
  • NF64さんへ

    >既にアドバイスもありましたが、やはりこのようなコードを書くよりも何とかしてComboBoxをDropDownListにする方が良い気がしてなりません。

    ただのListのときは、DropDownListでうまくいくのですが、コンボボックスのデータがサーバーデータのときはうまくいかないのです。

    例:CB_Country_ID -- コンボボックスで国を表示します。 サーバーデータ自体は、数値(Integer: Country_ID)です。 サーバーには別のテーブル(T_M_Country)があり、このテーブルにCoutry_ID(数値)やCountryShortName(文字)が格納されています。 フォームに表示する際は、一旦受け取ったサーバーデータ(数値:Country_ID)をCountryShortNameに変換しています。

    使用者がコンボボックスで選択したときは、選択したデータ(CountryShortName)は文字型なわけです(例:USA、HongKong)。 DropdownStyle=DropDownListのときは、これが数値にうまく変換できないようです。 ですから、コンボボックスの表示データがサーバーデータのときはうまくいかない、と考えているのですが?

    私の間違いだと、ありがたいのですが(こんなややこしいことを、しなくて済みます)。

    YKsaila

    下記に、関連コードを添付します。

    Me.CB_Country_ID.DropDownStyle = ComboBoxStyle.DropDownList   --- 今回、試験的に追加したコードです。 もともとは、ないものです。

    1.データ表示時

      (1) 'コンボボックス:CB_Country_ID(T_M_Country)----コンボボックス表示準備
            Dim dsCombo1 = New DataSet
            Dim sqCombo1 As String
            sqCombo1 = "SELECT T_M_Country.Country_ID, T_M_Country.CountryShortName, T_M_Country.CountryCode FROM T_M_Country"
            adapter.SelectCommand = New SqlClient.SqlCommand(sqCombo1, Con)
            adapter.SelectCommand.CommandType = CommandType.Text
            adapter.Fill(dsCombo1)
            Dim dtCombo1 As New DataTable
            dtCombo1 = dsCombo1.Tables(0)
            Me.CB_Country_ID.DataSource = dtCombo1
            Me.CB_Country_ID.DisplayMember = "CountryShortName"
            Me.CB_Country_ID.ValueMember = "Country_ID"

        Me.CB_Country_ID.DropDownStyle = ComboBoxStyle.DropDownList   --- 今回追加コード、 ここが(問題点)


     (2)コンボ表示
        Dim CTRYID As Integer = CInt(Me.CB_Country_ID.Text)---エラー表示(例:HongkongをIntegerに変換不能とでます、 ここが (問題点)です。

         Me.CB_Country_ID.Text =dt.Rows(n).Item("Country_ID").ToString
         Dim CTRYID As Integer = CInt(Me.CB_Country_ID.Text)
         foundrows = dtCombo1.Select("Country_ID = " & CTRYID & "")
         Me.CB_Country_ID.SelectedValue = dt.Rows(n).Item("Country_ID").ToString


    • 編集済み yksaila 2012年7月9日 0:50
    2012年7月9日 0:49
  • BIND関連で、修正しました。 「'」が行頭にある個所が修正個所です。 不要ですね、これで正常動作します。

    なお、ComboBoxのほうではSelectedIndexが使用できないので下記のとおり(SlaveComboBox使用)です。 TextBoxのほうは、SlaveTextBox にする必要がないので、SlaveControlのままです。

    これで、ほぼ、このスレッドも、完成(?)に近づきつつあります。 あとは、一括表示です。 このスレッドの内容は、各フォームのTextBox・ComboBoxに共通する指示ですので、各行でTextBox・ComboBoxの名前ごとに指示を書くのは非効率です。 一括指示ができれば便利です。 BIND関数でも、拡張メソッド使用でも良いのですが、FOR--Next等で書ければ最善と考えています。

    これにかんしては、再度質問します(数時間後?)

    以下、BINDの修正・変更コードです。

    Public Class MultiEvent
        'Public SlaveControl As Control
        'Public WithEvents SlaveTextBox As TextBox
        'Public WithEvents SlaveComboBox As ComboBox
        'Public Multi_MaxLength As Integer
        'Private WithEvents SlaveTextBox As TextBox
        Private WithEvents SlaveComboBox As ComboBox

        Public Sub Bind(SlaveControl As Control, Multi_MaxLength As Integer)
            If TypeOf SlaveControl Is TextBox Then
                'Me.SlaveTextBox = DirectCast(SlaveControl, TextBox)
                AddHandler SlaveControl.TextChanged,
                Sub(sender, e)
                    If Not IsNumeric(SlaveControl.Text) And SlaveControl.Text <> "" Then
                        MessageBox.Show("ここは、数字以外のものは入力できません。 ")
                        Exit Sub
                    End If

                    If SlaveControl.Text.Length > Multi_MaxLength Then
                        MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", Multi_MaxLength))
                        SlaveControl.Text = ""
                    End If
                End Sub

                AddHandler SlaveControl.Validating,
                Sub(sender, e)
                    If SlaveControl.Text = "" Then
                        Return
                    End If

                    If Not IsNumeric(SlaveControl.Text) Then
                        MessageBox.Show("数値を入力してください。 入力しない場合、0 にしてください。")
                        e.Cancel = True
                        SlaveControl.Select()
                        Exit Sub
                    End If
                End Sub

            ElseIf TypeOf SlaveControl Is ComboBox Then
                Me.SlaveComboBox = DirectCast(SlaveControl, ComboBox)
                If Multi_MaxLength = 0 Then
                    AddHandler Me.SlaveComboBox.Validating,
                     Sub(sender, e)
                         If Me.SlaveComboBox.SelectedIndex = -1 Or Me.SlaveComboBox.Text = "" Then
                             MessageBox.Show("コンボボックスのデータ選択で、入力してください。 直接入力はできません。 移動するには、まず選択してください。 ")
                             e.Cancel = True
                             Me.SlaveComboBox.Select()
                         End If
                     End Sub
                End If
            End If

        End Sub
     
    End Class

    2012年7月9日 1:21
  • NF64さんへ

    >既にアドバイスもありましたが、やはりこのようなコードを書くよりも何とかしてComboBoxをDropDownListにする方が良い気がしてなりません。

    ただのListのときは、DropDownListでうまくいくのですが、コンボボックスのデータがサーバーデータのときはうまくいかないのです。

    例:CB_Country_ID -- コンボボックスで国を表示します。 サーバーデータ自体は、数値(Integer: Country_ID)です。 サーバーには別のテーブル(T_M_Country)があり、このテーブルにCoutry_ID(数値)やCountryShortName(文字)が格納されています。 フォームに表示する際は、一旦受け取ったサーバーデータ(数値:Country_ID)をCountryShortNameに変換しています。

    使用者がコンボボックスで選択したときは、選択したデータ(CountryShortName)は文字型なわけです(例:USA、HongKong)。 DropdownStyle=DropDownListのときは、これが数値にうまく変換できないようです。 ですから、コンボボックスの表示データがサーバーデータのときはうまくいかない、と考えているのですが?

    私の間違いだと、ありがたいのですが(こんなややこしいことを、しなくて済みます)。

    YKsaila

    下記に、関連コードを添付します。

    Me.CB_Country_ID.DropDownStyle = ComboBoxStyle.DropDownList   --- 今回、試験的に追加したコードです。 もともとは、ないものです。

    1.データ表示時

      (1) 'コンボボックス:CB_Country_ID(T_M_Country)----コンボボックス表示準備
            Dim dsCombo1 = New DataSet
            Dim sqCombo1 As String
            sqCombo1 = "SELECT T_M_Country.Country_ID, T_M_Country.CountryShortName, T_M_Country.CountryCode FROM T_M_Country"
            adapter.SelectCommand = New SqlClient.SqlCommand(sqCombo1, Con)
            adapter.SelectCommand.CommandType = CommandType.Text
            adapter.Fill(dsCombo1)
            Dim dtCombo1 As New DataTable
            dtCombo1 = dsCombo1.Tables(0)
            Me.CB_Country_ID.DataSource = dtCombo1
            Me.CB_Country_ID.DisplayMember = "CountryShortName"
            Me.CB_Country_ID.ValueMember = "Country_ID"

        Me.CB_Country_ID.DropDownStyle = ComboBoxStyle.DropDownList   --- 今回追加コード、 ここが(問題点)


     (2)コンボ表示
        Dim CTRYID As Integer = CInt(Me.CB_Country_ID.Text)---エラー表示(例:HongkongをIntegerに変換不能とでます、 ここが (問題点)です。

         Me.CB_Country_ID.Text =dt.Rows(n).Item("Country_ID").ToString
         Dim CTRYID As Integer = CInt(Me.CB_Country_ID.Text)
         foundrows = dtCombo1.Select("Country_ID = " & CTRYID & "")
         Me.CB_Country_ID.SelectedValue = dt.Rows(n).Item("Country_ID").ToString

    こうしたらどうなりますか?

    'Me.CB_Country_ID.Text =dt.Rows(n).Item("Country_ID").ToString
    'Dim CTRYID As Integer = CInt(Me.CB_Country_ID.Text)
    Me.CB_Country_ID.SelectedValue = dt.Rows(n).Item("Country_ID").ToString
    Dim CTRYID As Integer = CInt(Me.CB_Country_ID.SelectedValue)
    

    ComboBoxのTextプロパティの値は、あくまで表示内容です。

    値の制御はSelectedValueプロパティで行って下さい。

    ※他にも気になりますがとりあえず・・・

    • 回答としてマーク yksaila 2012年7月23日 15:10
    2012年7月9日 3:40
  • aviator_さんへ

    できました! やっとです。

    これで、Validatingがなしで、済ませます。 CB_Country_ID.Validatingのコードです。 余計なコメントもでないので、このほうがいいです。 DropDownlistの灰色の少し立体的な表示が嫌なので、ここの表示スタイルは、PopUp か Flat にしました。

    また、関連で横にテキストボックスがあります(TX_CountryCode)。 コンボで選択すると、横のテキストボックスに「CountryCode」を表示します。

    CountryCodeは、コンボボックスのDisplayMemberでも ValueMemberでもないので、下記のようなコードを追加しました。

    全てのコンボボックスをこのタイプに変更するつもりです。 手間が大変ですが、ま、自分の責任だからしようがないです。

    ありがとうございました。

    YKsaila

    以下は、参考コード:

          Private Sub CB_Country_ID_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles CB_Country_ID.SelectedIndexChanged
            'SQL認証接続
            省略
            Con.Open()
            Dim adapter = New SqlClient.SqlDataAdapter()

            'コンボボックス:CB_Country_ID(T_M_Country)----コンボボックス表示準備
            Dim dsCombo1 = New DataSet
            Dim sqCombo1 As String
            sqCombo1 = "SELECT T_M_Country.Country_ID, T_M_Country.CountryShortName, T_M_Country.CountryCode FROM T_M_Country"
            adapter.SelectCommand = New SqlClient.SqlCommand(sqCombo1, Con)
            adapter.SelectCommand.CommandType = CommandType.Text
            adapter.Fill(dsCombo1)

            Dim dtCombo1 As New DataTable
            dtCombo1 = dsCombo1.Tables(0)
            Dim foundrows() As Data.DataRow
            Dim S_INDX As Integer = Me.CB_Country_ID.SelectedIndex
            foundrows = dtCombo1.Select("Country_ID=" & S_INDX & "")
           Me.TX_CountryCode.Text = foundrows(0).Item("CountryCode").ToString

        End Sub

    • 回答としてマーク yksaila 2012年7月23日 15:10
    2012年7月9日 8:50
  • 横から失礼します。すごく内容のこーいスレッドだなあと拝見しています。
    ちょっと、気になったので、投稿させてください。
     
    Private Sub CB_Country_ID_SelectedIndexChanged で DataBase に接続して取得されていますが、
    動作は遅くありませんか?
    それと、CountryCode と CountryShortName の関係は Form が Load されてからずっと変わりませんよね。
    取得し直さなくとも良いのでは。
     
    ひとつの案として、CountryCode を表示する Textbox を作っておき、これも DataBase に Bind したら如何でしょうか。
    つまり、Combobox はデータのポインタを動かすためだけに用いて、データの取得は Textbox から行う、と。
    adapter.Fill は Form の Load 時に行い、CountryCode と CountryShortName とを含む DataTable に取得。

    こうすれば、CB_Country_ID.SelectedIndexChanged で index が変わるたびに取得せずに済みます。

    もし、この取得用の Textbox を 表示させたくないなら、

    #If DEBUG Then
       Me.xxxTextbox.visible = True
    #Else
       Me.xxxTextbox.visible = False
    #End If

    私は Font を小さく、色を薄くして、確認用に表示させています。

    2012年7月9日 10:09
  •  はぁ。これの方が、はるかに簡単でしたね。

    dtCombo1 = dsCombo1.Tables(0)
    Me.CB_Country_ID.DataSource = dtCombo1
    Me.CB_Country_ID.DisplayMember = "CountryShortName"
    Me.CB_Country_ID.ValueMember = "Country_ID"
    
    Me.CB_Country_ID.SelectedValue = dt.Rows(n).Item("Country_ID").ToString
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    

     ここです。dt.Rows(n).Item("Country_ID") の Type を調べてください。デバッガでとめて、クイック ウォッチに「GetType(dt.Rows(n).Item("Country_ID"))」かな?おそらく、Integer か、Decimal あたりの「数値型」になっていると思います。それを ToString() で「文字列型」にするもんだから、一致しないよ、と。

     ComboBox を1つ配置して、DropDownList にしました。次のコードで「英語」が選択されます。

    Public Class Form1
    
        Dim items As DataTable
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            Me.items = New DataTable("ComboItems")
            ' Adapter で SELECT すると、データベースの型によって自動的に設定されます。
            Me.items.Columns.Add("LocaleID", GetType(Integer))
            Me.items.Columns.Add("LocaleName", GetType(String))
            Me.items.Rows.Add(1041, "日本語")
            Me.items.Rows.Add(1033, "英語")
            Me.items.Rows.Add(1042, "韓国語")
    
            Me.ComboBox1.DataSource = Me.items
            Me.ComboBox1.DisplayMember = "LocaleName"
            Me.ComboBox1.ValueMember = "LocaleID"
    
            Me.ComboBox1.SelectedValue = Integer.Parse("1033")
        End Sub
    End Class
    

     Country_ID の型が Decimal だった場合、暗黙では Integer への縮小変換ができないので、Option Strict On だとエラーが出るかもしれません。この場合、明示的に Integer に縮小変換するか、関連する変数の型を Decimal に変更します。

     「型を意識する」とは、こういうことです。専門のソフトウェア開発者ではないということなので、「Option Strict Off」での使用を薦めます。あるいは、C# を使用される事を薦めます。今のような、「中途半端に型を意識する状態」は、よくないです。


    Jitta@わんくま同盟

    • 回答としてマーク yksaila 2012年7月23日 15:10
    2012年7月9日 14:16
  • 横から失礼します。すごく内容のこーいスレッドだなあと拝見しています。
    ちょっと、気になったので、投稿させてください。
     
    Private Sub CB_Country_ID_SelectedIndexChanged で DataBase に接続して取得されていますが、
    動作は遅くありませんか?
    それと、CountryCode と CountryShortName の関係は Form が Load されてからずっと変わりませんよね。
    取得し直さなくとも良いのでは。
     
    ひとつの案として、CountryCode を表示する Textbox を作っておき、これも DataBase に Bind したら如何でしょうか。
    つまり、Combobox はデータのポインタを動かすためだけに用いて、データの取得は Textbox から行う、と。
    adapter.Fill は Form の Load 時に行い、CountryCode と CountryShortName とを含む DataTable に取得。

    こうすれば、CB_Country_ID.SelectedIndexChanged で index が変わるたびに取得せずに済みます。

    もし、この取得用の Textbox を 表示させたくないなら、

    #If DEBUG Then
       Me.xxxTextbox.visible = True
    #Else
       Me.xxxTextbox.visible = False
    #End If

    私は Font を小さく、色を薄くして、確認用に表示させています。

    Shiroyuki_Motoさんへ

    ご教示、ありがとうございます。

    >Private Sub CB_Country_ID_SelectedIndexChanged で DataBase に接続して取得されていますが、動作は遅くありませんか?

    いいえ、全く遅くありません。 一瞬です。 Datasource(datatable)が必要なので、そうしているのですが。 今は、夢中・必死でやっていますので、無駄な個所もあるかもしれません。 

    すでに、TextBox(TX_CountryCode) と ComboBox(CB_Country_ID) の両方があります。 片方が変われば、もう片方も自動で変わる関係にあります。

    TextBox(TX_CountryCode) ⇔ ComboBox(CB_Country_ID) の相互依存関係です。

    表示は、Form_Load時、レコード移動時(その画面内でのデータ・レコード移動時)、コンボボックス選択時、テキストボックス入力時の4種類になります。最初の二つは、まったく別のもの、最後の二つは同じ画面内で発生するもの。 うーん、難しい! コンボの選択内容が変われば、当然テキストボックスの表示もそれに呼応して変化しなければならない。 それが、CB_Country_ID_SelectedIndexChanged そのものと考えたのですが? ようするに必要と思うのですが。 いまひとつ、おっしゃることが理解できていないのかもしれません。

    もっと、具体的に書いていただければ、分かるかもしれません。 このスレッドでの私の課題も、80%くらい解決してきました。 あと、もう少しで完成です! 

    YKsaila

    2012年7月9日 14:17
  • yksaila さま

    まず、先の投稿の注釈を
    Bind と書いたのは yksaila さまの ' Public Sub Bind ' のことではなく、
    一般的な DataSource と Control を結び付ける意味の Bind のことです。
    また、Textbox で説明しましたが、 Label で置き換えてください。
      ( Textbox で .ReadOnly = True にして表示したものから引用したもので )
    紛らわしい説明になり、済みませんでした。

    スレッド全体の流れを見てみたのですが、ご質問のタイトルからコントロールの種類に依存した入力チェックの構成になりますよね。
    Jitta さまが初期に書き込まれたように、コントロールの種類を変えたりした時、コーディングが大変になりませんか?
    80% 以上完成とありますから、一旦完成させてください。
    次期のバージョンアップの折には、以下の点も検討なさっては如何でしょうか。
     
    1. 入力チェックをひとつの Function にまとめる
      以下に例示

    2. 連動して表示したいものはひとつの DataTable に設定し、複数の Control で DataSource に指定する
       連動した表示は VB (言語) が自動的にサポートしてくれる筈
      取得したデータが動的に変わらないのなら、Fill は 一回でよい

    3. DataBase からデータを持ってくる時は、型に気を付ける
      Jitta さまも書かれていますが、Short なんて型もあります

    Function HandleInputError() As Boolean

    ' Control 毎に条件比較

    If Me.Textbox1.Text = "" Msgbox("未入力です!") Me.Textbox1.Focus() Return False End If ' 以下省略 Retutn True End Function 'Msgboxが共通化できる場合や複雑な処理をする場合 > Sub や Function を増やす

    • 編集済み ShiroYuki_Mot 2012年7月10日 2:00 コメント行訂正
    2012年7月10日 1:51
  • Jittaさんへ

    >ここです。dt.Rows(n).Item("Country_ID") の Type を調べてください。デバッガでとめて、クイック ウォッチに「GetType(dt.Rows(n).Item("Country_ID"))」かな?おそらく、Integer か、>decimal あたりの「数値型」になっていると思います。それを ToString() で「文字列型」にするもんだから、一致しないよ、と。

    型は当然Integerです、最後の.ToStringは何かの間違いでしょう。 書き間違い? .ToStringなしで、動きます。

    >「型を意識する」とは、こういうことです。専門のソフトウェア開発者ではないということなので、「Option Strict Off」での使用を薦めます。あるいは、C# を使用される事を薦めます。今のような、「中>途半端に型を意識する状態」は、よくないです。

    型については、散々苦労しましたので、かなり意識しているつもりです。 少しずつ、良くなるのではないでしょうか?

    Option Strict On に先日変更しましたので、いまさら止めるのは無理です。 このまま、続けたいと思いますが。

    専門のソフトウェア開発者ではないのですが、まともなソフトを作ることにかんしては同じなので頑張ります。 

    YKsaila

    2012年7月11日 6:31
  • Jittaさんへ

    ヒントをありがとうございました。 実は、質問があったのですが、このヒントで解決しました。 非常に、ありがたかったです!

    するはずだった質問内容:

    DropDownStyleをDropDownListにしたときに、サーバー内のテーブルデータからの呼び出しでのコンボボックスは解決したのですが、普通のコンボがこのDropDownListにすると正常でなくなったのです。 DropDownでは、うまくいっていたのですが。

    後半に、うまくいかなかった例を書きます(質問するつもりだったものです。 DropDownでは、うまくいっていました )。 これは、なぜうまくいかないのでしょうか?

    下記の(A)は成功したので、質問はいちおう解決したのですが、(B)がなぜダメなのか疑問が残ります。

    (A)成功例

    コンボボックス--CB_CustomerType、  ---DropDownStyleは、DropDownListです。

    サーバーには、数値のみ保管です。 対応するテーブルはサーバーにはありません。 数値との対応は、ソフト上で示します。

    下記のコードでうまくいきました。

    1)Form_Load時:
            Me.items = New DataTable("ComboItems")
            ' Adapter で SELECT すると、データベースの型によって自動的に設定されます。
            Me.items.Columns.Add("Customer_No", GetType(Integer))
            Me.items.Columns.Add("CustomerType", GetType(String))
            Me.items.Rows.Add(0, "なし")
            Me.items.Rows.Add(1, "通常")
            Me.items.Rows.Add(2, "催事")
            Me.items.Rows.Add(3, "小売")
            Me.items.Rows.Add(4, "小売諸口")
            Me.items.Rows.Add(9, "取引停止")
            Me.CB_CustomerType.DataSource = Me.items
            Me.CB_CustomerType.DisplayMember = "CustomerType"
            Me.CB_CustomerType.ValueMember = "Customer_No"

     

    2)データ呼び出し時:
            Me.CB_CustomerType.SelectedValue = dt.Rows(n).Item("CustomerType")  '必要
            Me.CB_CustomerType.Text = Me.CB_CustomerType.DisplayMember

    (B) 以下は、失敗例です。 

    コンボボックス--CB_CustomerType、  ---DropDownStyleは、DropDownListにする(DropDownのときは正常)。

    サーバーには、数値のみ保管です。 対応するテーブルはサーバーにはありません。 数値との対応は、ソフト上で示します。

    ”この場合は、全てのレコードで同じ表示でサーバーデータに対応しません。”

    DropDownだと、サーバーデータに応じて正しく表示します。

    DropDownStyleを、DropDownListにせずDropDownで続けたのは、ここが発端だったかもしれません。

    1)F_M_Client_load  ’フォームを開く時(Form_Load)

            'コンボボックス CB_CustomerType設定
            With Me.CB_CustomerType
                .Items.Add("なし") '0
                .Items.Add("通常") '1
                .Items.Add("催事") '2
                .Items.Add("小売") '3
                .Items.Add("小売諸口") '4
                .Items.Add("取引停止") '9
            End With


     2)Public Sub db_dataset(ByVal n As Integer)  '各レコード呼び出し表示

       Me.CB_CustomerType.Text = dt.Rows(n).Item("CustomerType")   '必要 dt--データテーブル(サーバーから呼び出し)
         Me.CB_CustomerType.Text = FUNC_CustomerType_Name(NZint(Me.CB_CustomerType.Text)) ’NZint /FUNC_CustomerType_Name --自作関数(下記4参照)
     
      End Sub


    3)変更・書き込み時
          'コンボボックス--CB_CustomerType
                sql.Parameters.AddWithValue("@CustomerType", FUNC_CustomerType_Int(Me.CB_CustomerType.Text)) ’FUNC_CustomerType_Int --自作関数(下記4参照)


    4)Module内:自作関数
      '得意先分類--表示
        Public Function FUNC_CustomerType_Name(a As Integer) As String
            Dim W_Name As String

            Select Case a
                Case 0 'NUM_CustomerType_None
                    W_Name = "なし"
                Case 1 'NUM_CustomerType_Normal
                    W_Name = "通常"
                Case 2 'NUM_CustomerType_Event
                    W_Name = "催事"
                    '----2006/3/30 IJssel----
                Case 3 'NUM_CustomerType_Retail
                    W_Name = "小売"
                Case 4 'NUM_CustomerType_Retail_Others
                    W_Name = "小売諸口"
                    '----End 2006/3/30 IJssel----
                Case 9 'NUM_CustomerType_Stop
                    W_Name = "取引停止"
                Case Else
                    W_Name = "?"
            End Select

            FUNC_CustomerType_Name = W_Name
        End Function


        '得意先分類---サーバー書き込み
        Public Function FUNC_CustomerType_Int(a As String) As Integer
            Dim W_Int As Integer

            Select Case a
                Case "なし" 'NUM_CustomerType_None
                    W_Int = 0
                Case "通常" 'NUM_CustomerType_Normal
                    W_Int = 1
                Case "催事" 'NUM_CustomerType_Event
                    W_Int = 2
                    '----2006/3/30 IJssel----
                Case "小売" 'NUM_CustomerType_Retail
                    W_Int = 3
                Case "小売諸口" 'NUM_CustomerType_Retail_Others
                    W_Int = 4
                    '----End 2006/3/30 IJssel----
                Case "取引停止" 'NUM_CustomerType_Stop
                    W_Int = 9
                Case Else
                    W_Int = 10
            End Select

            FUNC_CustomerType_Int = W_Int
        End Function

    以上です、よろしくお願いします。

    専門家に近づくように努力しますので、よろしくお願いします。 本職でなくとも、可能では? 

    YKsaila

    2012年7月11日 7:31
  • Shiroyuki_Motoさんへ

    >Private Sub CB_Country_ID_SelectedIndexChanged で DataBase に接続して取得されていますが、動作は遅くありませんか?
    >それと、CountryCode と CountryShortName の関係は Form が Load されてからずっと変わりませんよね。 取得し直さなくとも良いのでは。                                 私の考え:サーバーへの接続は不要になったら、即切る。 必要なときのみ接続し、不要になったらすぐに切断、 という方針でやってきました。 コンボボックスを選択したとき接続し、選択が終わったら即切断です。  数十台のクライアントを抱えるようなネットワークだと、これが重要と考えているのですが? どうなんでしょうか? サーバーの容量・能力、回線を考えて無駄な接続はしないほうが良い、と考えています。 サーバーの能力をフルに活用するためです。 コンボボックスを選択したとき、新たにサーバーに接続すれば、最新情報を取得できますし、こうあるべきでは? 1台のクライアント(A)がフォームを開きコンボボックス(CB_Country_ID)を操作する前に、誰かが他のクライアントPC(B)で、この間に国情報を変更しているかもしれません(国情報--T_M_Country:サーバー上のテーブルで、国名、国コード等を収めてある)。

    国コード(CountryCode)は国際的に決められているものだから変更はないでしょうが、これも絶対ではないし(ソフト側からすればこれをあてにするのは間違っています)、(B)によって新たに国が追加されているかもしれません。  CB_Country_ID_SelectedIndexChanged のとき、サーバーに新たに接続しなければ、この新情報はPC(A)では古いままです。 ですから、SelectedIndexChangedの時に、新たにサーバーに接続するのが望ましいと思ったのです。

    私の接続法は、初期のADO接続方式らしく最新の接続方式は詳しくないです。 ただ、クライアントとサーバーの関係は変わってないと思うのですが? どうなんでしょうか? サーバーがクライアントにデータを投げるのは、接続時の一回だけであって、それ以降は投げない、断続的にデータ更新しているのではない、と。 データを更新するには、新たに接続するしかないと、勉強したのですが。

    YKsaila

              

    2012年7月11日 13:30
  •  うまくいかなかったのは、おそらく、データの追加を Items.Add() で行っているからです。Items を直接操作すると、DataSource が使えません。このあたり、イベントやインターフェイスで細々しているので、DataSource に指定するクラスによっても、期待する動作をしません。

     ここで一番簡単なのは、DataTable です。このクラスはデータ バインドを行うために必要な要素をすべて持っているので、これを派生して特定の型を作るか、私の先の投稿の「Columns.Add()」のようにカラムのデータ型を指定します。

     別に専門職に近づかなくてもいいのではないですか?たとえば、私のうちでは、万能包丁で刺身を切ります。もし、寿司屋が万能包丁で切っていたら、二度と行くことはないでしょう。たとえば、MSDN にはデータ バインドに関する項目があります。プロには、紹介すると同時に一度は目を通すことを期待します。そうでない人には紹介だけします。要求されるものが違うのです。それだけのことです。


    Jitta@わんくま同盟

    2012年7月11日 14:14
  •  yksaila さま ShiroYuki_Mot です。
      
     DataBase の接続方針に関しては全く yksaila さまの仰るとおりです。
     接続は必要な時のみ最短で、... で良いと思います。
     一般的に、参照系は当初に1回で、更新系は随時です。
      
     CB_Country_ID という名称と内容から参照系と判断して投稿しました。
     更新系として扱われているのですね。
     ただし、先のご投稿で示された「国情報を変更」が発生した場合、選択項目が削除されていたら、思うような結果は得られませんよね。  (過去の例では、国がドイツのようにひとつになるとか、ソ連のように分裂するとか。)
     選んだ筈なのに空白が帰ってきた ... て。  (空白が帰る?)
     Transaction を導入して対応したり通知の仕組みを組み込んだりが基本でしょうが、その前に、システム設計でどう対応するか検討した方が良いかも知れません。 既に、ご対応中のようですが。
     色々な方法があると思います。  例外処理って本当に難しいですよね。  例外がどんどん増えていく!
      
     スレッドの内容から、かなり脱線気味なので、私はこの辺で筆を置きます。
     お騒がせしました。


    2012年7月11日 15:59
  • jittaさんへ

    ありがとうございます。 下記記載の件のご説明、理解しました。

    >うまくいかなかったのは、おそらく、データの追加を Items.Add() で行っているからです。Items を直接操作すると、DataSource が使えません-------

    ----

    >別に専門職に近づかなくてもいいのではないですか?

    専門職の定義にもよりますね。 私には、遠大な計画があって、VBでまともに使える 「業務ソフト」 を自力で作りたいのです。 その意味では、専門職並みの知識が必要です。

    ということで、プロとまではいかなくても、その予備軍扱いでいいのですが? 専門職(ま、予備軍、二軍?)扱いで、お願いします。 野球も二軍選手もプロですから、そのうち一軍に昇格できるかもしれないです、努力しだいで。

    データ バインドに関する項目は重要ですよね、できればこの種の指導は、ぜひ、お願いします。

    ちなみに、寿司屋のたとえは、少しおかしくありません? 私は料理人ではないですが、料理人の本当の価値は、しっかりした材料を選ぶこと、が基本だとおもいます。

    最近、まともなレストランが少なくなってきました。 少し悲しい。

    YKsaila

    2012年7月18日 15:44
  • Shiroyuki_Motoさんへ

    いえいえ、いろんな人の、ご意見・ご指導は、ありがたいです。

    これからも、よろしく、ご指導をお願いします。

    YKsaila

    2012年7月18日 15:47
  • 先の(直前の)書き込みと重複しますが、途中なので、再度まとめます。

    ”コンボボックスとテキストボックスへの共通指示を一般化する”、これがこのスレッドの課題でした!


    コンボボックスについて、現在までに、判明したこと:
    コンボ・ボックスのDropDownStyleは、必ず(ComboBoxStyle.)DropDownListにする。 Dropdownには、しない!
    FlatStyleは、Standardでなく、Flatか PopUpにする(これは、好みの問題)。

    (A)--DropDownListで、コンボボックスへの指定外の入力が防げます。
    コメントも不要だし、簡潔です。 使用者にとっても、このほうが良い。

    ということで、コンボ・ボックスの内容指定(設定)で、DropDownList を選択しますから、コンボへの共通のコード指定は全く不要となりました。
    コンボボックスへの指定外の入力を防ぐことが目的ですから、自作メソッドも、自作コンボボックスも不要、Addhandlerも不要となります。

    コンボボックスでは、だいぶ遠回りをしました。 ま、私本人には、勉強になったので良かったのですが。 いったい、何をしていたんでしょうね?
    結局、何人かの方々が言われたように「DropDownList」に落ち着き、まとめてみると意外と簡潔になりました。

    今まで何をやってきたのかと、自問自答したくなります。 基本が分かっていないということは、こういうことかもしれないですね。

    残るは、TextBoxのみとなり、作業は簡単になりました。 TextBoxへの共通指示は残りますので、この一括処理(指示)が最後の課題となります。


    以下、参考コード(例):コンボボックスについて、です。
    1.Form_Load時:
      ①コンボ・データ=表形式
      Form_Loadの前に、  Dim items as DataTable を書いておく。

       'コンボボックス CB_B_TaxFractionType設定
            Me.items = New DataTable("B_TaxFractionType")
            Me.items.Columns.Add("B_TaxFractionType_No", GetType(Integer))
            Me.items.Columns.Add("B_TaxFractionType", GetType(String))
            Me.items.Rows.Add(0, "切捨")
            Me.items.Rows.Add(1, "四捨五入")
            Me.items.Rows.Add(2, "切上")
            Me.CB_B_TaxFractionType.DataSource = Me.items
            Me.CB_B_TaxFractionType.DisplayMember = "B_TaxFractionType"
            Me.CB_B_TaxFractionType.ValueMember = "B_TaxFractionType_No"

      ②コンボ・データ=サーバー内の他テーブルからのデータ---ここでは、なし。 データ呼び出し時に設定、(2)で。


    2.データ(レコード)呼び出し時:
           ①(1-①)--表形式
        Me.CB_B_TaxFractionType.SelectedValue = dt.Rows(n).Item("B_TaxFractionType")
        Me.CB_B_TaxFractionType.Text = Me.CB_B_TaxFractionType.DisplayMember
        (注):dt.Rows(n)--- サーバーからの呼び出しレコード、”n”は行番号、dtはデータテーブル

        ②(1-②)コンボ設定--サーバーからのデータ
        'コンボボックス:CB_Country_ID(T_M_Country)----(サーバー内の他テーブルからのデータ)
            Dim dsCombo1 = New DataSet
            Dim sqCombo1 As String
            sqCombo1 = "SELECT T_M_Country.Country_ID, T_M_Country.CountryShortName, T_M_Country.CountryCode FROM T_M_Country"
            adapter.SelectCommand = New SqlClient.SqlCommand(sqCombo1, Con)
            adapter.SelectCommand.CommandType = CommandType.Text
            adapter.Fill(dsCombo1)
            Dim dtCombo1 As New DataTable
            dtCombo1 = dsCombo1.Tables(0)
            Me.CB_Country_ID.DataSource = dtCombo1
            Me.CB_Country_ID.DisplayMember = "CountryShortName"
            Me.CB_Country_ID.ValueMember = "Country_ID"
            Me.CB_Country_ID.DropDownStyle = ComboBoxStyle.DropDownList

            --
         --
         Me.CB_Country_ID.SelectedValue = dt.Rows(n).Item("Country_ID") 
            Me.CB_Country_ID.Text = Me.CB_Country_ID.DisplayMember

           << データ呼び出し表示時は、①②ともに同じで、SelectedValue・DisplayMemberを使用する。>>


    3.書き込み
     sql.Parameters.AddWithValue("@B_TaxFractionType", Me.CB_B_TaxFractionType.SelectedValue)

    以上です、TextBoxについての最後の質問は、今日中にします(7/19)。

    YKsaila 


    • 編集済み yksaila 2012年7月18日 16:23
    • 回答としてマーク yksaila 2012年7月23日 15:13
    2012年7月18日 15:58
  • このスレッドでの、最後の質問になります(うまく行けばですが)。

    ようするに、やりたいことは、ただ一つ → 数値専用・文字入力可・メモ型を含めて、テキストボックスに共通指示をだしたい。

    これができれば、個々のテキストボックスへのコードをTextBoxごとに書く必要がなくなります。 Form_Load の中に、数行書くだけで、数値専用のテキストボックスの場合、文字入力可のボックス、メモ型ボックス等も、このコードで仕分けして、全てここで一括処理できます。 これが、最終目標です。

    下記のようにしてみたのですが、うまくいきません。1と2の、二方法で試みましたが、ダメでした。

    どなたか、ご教示をお願いします。

    YKsaila

    以下、参考コードです、どこかが間違っています(だから、動かない)。

    <数値専用TextBox>
    0.事前に、テキストボックスのTagに”N”を入力、数値専用ボックス指定のつもり、です。

    1.BIND---Moduleで定義(クラスMultiEvent内での定義をやめて、モヂュール内で定義)
    で、下記のようにしました。
     Bind(Me.TX_S_Billsight, 6)の説明---テキストボックス(TX_S_Billsight)の入力制限文字数が6文字、その他いろいろ。自作関数

           
        'フォームの全コントロールを取得する
            For Each CtrlItem As Control In Me.Controls
          ’テキストボックス(数値専用)仕様
                If TypeOf CtrlItem Is TextBox And CtrlItem.Tag Is "N" Then ---(A)この行が有効でないようです。
                    Bind(CtrlItem, 6)  ’---BIND関数、テキストボックスへの文字数入力制限が、6文字
          
                End If
            Next

     Bind(Me.TX_S_Billsight, 6)は、有効に働きます。これを、For Each CtrlItem As Control In Me.Controlsの真下に持ってきても有効です。
    ですから、行(A)が、おかしい? または、Tagの使い方がおかしいですか?

    2.BIND関数-- クラスMultiEvent内で定義(前掲のとおりのもの)
      下記のコードを、Form_Load(F_M_Client)の中に、書きます。  

        Dim K As Integer
            K = Me.Controls.Count
            Dim Multi(K) As MultiEvent

            For j = 0 To K - 1
                Multi(j) = New MultiEvent
                Dim CtrlItem As Control   ’(B)ここが、おかしい?
                If TypeOf CtrlItem Is TextBox And CtrlItem.Tag Is "N" Then  ’(B)ここの ”CtrlItem”で、下記の警告が出ます。
                    Multi(j).Bind(CtrlItem, 6)
               
                End If
            Next

    警告内容:変数 'CtrlItem' は、値が割り当てられる前に使用されています。Null 参照の例外が実行時に発生する可能性があります。
        CtrlItem.Tagが、おかしいと思います。


    • 編集済み yksaila 2012年7月18日 16:39
    2012年7月18日 16:34
  • (A)について

    ×
    If TypeOf CtrlItem Is TextBox And CtrlItem.Tag Is "N" Then

    If TypeOf CtrlItem Is TextBox AndAlso CtrlItem.Tag IsNot Nothing AndAlso CtrlItem.Tag.ToString().Equals("N") Then

    かと・・・

    (B)について

    ×
    Dim CtrlItem As Control


    Dim CtrlItem As Control = Me.Controls(j)

    にしてみて下さい。
    ※その下の行の条件は(A)と一緒です。

    2012年7月19日 2:16
  • 私には、遠大な計画があって、VBでまともに使える 「業務ソフト」 を自力で作りたいのです。 その意味では、専門職並みの知識が必要です。

     了解。新しいものを作る楽しみを知る機会へようこそ!!


    ”コンボボックスとテキストボックスへの共通指示を一般化する”、これがこのスレッドの課題でした!

     次の様なコード例が挙がっています。(主観的優しい順)

    1. Handles 句の後にずらずら並べる(2012年6月5日 5:48 ひらぽんさん)
    2. AddHandler ステートメントをずらずら並べる(2012年6月7日 7:14 yksailaさん)
    3. 拡張メソッドを使う(2012年6月5日 8:20 ひらぽんさん)
    4. Partial を利用して拡張する(2012年6月5日 10:48 aviator__さん)
    5. コントロールを検索して AddHandler で登録する(2012年6月11日 14:02 Jitta)
    6. TextBox を継承したコントロールを作る(2012年6月5日 7:22 aviator__さん)

    If TypeOf CtrlItem Is TextBox And CtrlItem.Tag Is "N" Then ---(A)この行が有効でないようです。

     次のように、同じオブジェクトではないので、「CtrlItem.Tag Is "N"」が否を返しています。一般的に、文字列の比較には string.Equals, string.Compare, string.CompareTo, == を使います。また、If ステートメントの比較式における「かつ」には、「AndAlso」を使います。理由は、調べて下さい。

    Is 演算子 (Visual Basic) より:

    Is 演算子は、2 つのオブジェクト参照が同じオブジェクトを参照しているかどうかを判定します。ただし、値の比較は行われません。object1 と object2 の両方がまったく同じオブジェクト インスタンスを参照している場合、result は True になります。それ以外の場合は、result は False です。


    Dim CtrlItem As Control   ’(B)ここが、おかしい?

     入れ物を宣言していますが、中に何も入れていません。従って、「変数 'CtrlItem' は、値が割り当てられる前に使用されています。Null 参照の例外が実行時に発生する可能性があります。」


    Jitta@わんくま同盟

    • 回答としてマーク yksaila 2012年7月23日 15:14
    2012年7月19日 12:58
  • こんにちは、yksaila さん。

    MSDNフォーラムのご利用ありがとうございます。オペレーターの山本です。
    こちらのスレッドはかなり長くなってますよね。
    #表示完了までに時間がかかるので、環境によってはストレスになるかなと思います。

    恐れ入りますが、こちらのスレッドは一旦終了していただき、新規にスレッドを立てて続けていただいてもよろしいでしょうか。
    両方のスレッドに相互のリンクを記載してくださいね。
    お手数ですが、ご協力のほどよろしくお願いいたします。
    ___________________________________
    日本マイクロソフト株式会社 フォーラム オペレーター 山本 春海

    2012年7月20日 6:16
  • 山本晴海さんへ、および訪問者の皆様へ

    了解しました。 確かに、長くなりました。 最後尾に辿りつくのに時間がかかりますね。

    続きは、「VB、テキストボックスやコンボボックスへの指示を一括ですませたいのですが、うまくいきません_その2(続き)」という標題で、新規にスレッドを立てました。

    よろしく、お願いします。

    YKsaila

    2012年7月20日 7:31
  • Aviator_さんへ

    ご回答、ありがとうございます。

    スレッドが長くなっていますので、下記スレッドへ続編を移しました。

    「VB、テキストボックスやコンボボックスへの指示を一括ですませたいのですが、うまくいきません_その2(続き)」

    質問も、続編のほうへ記入いたしました。

    YKsaila

    2012年7月20日 7:56
  • Jittaさんへ

    ご回答、ありがとうございます。

    スレッドが長くなっていますので、下記スレッドへ続編を移しました。

    「VB、テキストボックスやコンボボックスへの指示を一括ですませたいのですが、うまくいきません_その2(続き)」

    解決策をまとめていただいて、ありがとうございます。 終わりましたら、整理をして回答マークをつけるつもりでいます。 マークが多くなるかも?

    YKsaila

    2012年7月20日 8:01