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

質問
-
使用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 IfIf 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 SubElseIf Len(CTRL.Text) > 50 Then
MessageBox.Show("50文字を超える入力は、できません。 ")
CTRL.Text = ""
Exit SubElseIf IsNumeric(CTRL.Text) And Len(CTRL.Text) > 10 Then
MessageBox.Show("10文字(桁)を超える入力は、できません。 ")
CTRL.Text = ""
Exit Sub
End IfEnd If
Next
<私の対策2>イベント自作で対処しようとおもい下記のコードを書き、これが成功すれば次の段階へ行く予定でいたのですが、この最初の段階でうまくいきません(’**印のところです)。
このイベントは、初期段階なので、これが成功すれば次の段階へ行き、対策を進める予定でした。
実験用に、フォーム内にテキストボックス(Text1)・コマンドボタン(Command1)を追加。1.クラス作成します。
Public Class AlarmIntegerPublic Event Over(ByVal OverValue As Integer, Cancel As Boolean)
Public Max As Integer
'Private m_CurrentValue As Integer = 0
Dim m_CurrentValue As IntegerPublic Property CurrentValue(NewValue As Integer) As Integer
Get
Return m_CurrentValue
End GetSet(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
回答
-
> 現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(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
-
> 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 ModulePublic 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
-
> 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
-
ひらぽんさんへ
できました、ありがとうございました。
以前の私のコード(下記記載)は、(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 IfIf Not IsNumeric(cb.Text) Then
MessageBox.Show("数値を入力してください。 入力しないなら、0 にしてくださいよ。")
e.Cancel = True
cb.Select()
End If
End Sub- 回答としてマーク yksaila 2012年7月23日 15:04
-
現在までの解決策の状況をまとめます。
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 ?
で、これが終わったら感想です!
-
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 MultiEventMulti(0).Bind(TX_S_BillSight, 6) ’テキストボックス、成功 TX_S_BillSight---テキストボックス名 制限入力字数=6
Multi(1).Bind(TX_S_TrustPeriod, 6) ’テキストボックス、成功 TX_S_TrustPeriod---テキストボックス名 制限入力字数=6Multi(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 IfIf SlaveControl.Text.Length > Multi_MaxLength Then
MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", Multi_MaxLength))
SlaveControl.Text = "" ’この辺は、あとでもっと良いものに変更します。
End If
End SubAddHandler SlaveControl.Validating,
Sub(sender, e)
If SlaveControl.Text = "" Then
Return
End IfIf Not IsNumeric(SlaveControl.Text) Then
MessageBox.Show("数値を入力してください。 入力しない場合、0 にしてください。")
e.Cancel = True
SlaveControl.Select()
Exit Sub
End If
End SubElseIf 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 IfEnd Sub
End Class
-
起きているエラーというのはNullReferenceExceptionですか?
どのような状況で、どのようなエラーが発生しているのか内容を明記しましょう。Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)
「Me.SlaveComboBoxにパラメーターとして与えられたComboBox(つまるところSlaveControl)を保持させたい。」という意図があると思いますが、上記のコードではそのようになっていません。
TextBoxの方も同じ状態になっていますから、修正が必要でしょう。既にアドバイスもありましたが、やはりこのようなコードを書くよりも何とかしてComboBoxをDropDownListにする方が良い気がしてなりません。
-
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
-
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").ToStringEnd Sub
- 回答としてマーク yksaila 2012年7月23日 15:10
-
はぁ。これの方が、はるかに簡単でしたね。
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
-
先の(直前の)書き込みと重複しますが、途中なので、再度まとめます。
”コンボボックスとテキストボックスへの共通指示を一般化する”、これがこのスレッドの課題でした!
コンボボックスについて、現在までに、判明したこと:
コンボ・ボックスの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
-
私には、遠大な計画があって、VBでまともに使える 「業務ソフト」 を自力で作りたいのです。 その意味では、専門職並みの知識が必要です。
了解。新しいものを作る楽しみを知る機会へようこそ!!
”コンボボックスとテキストボックスへの共通指示を一般化する”、これがこのスレッドの課題でした!
次の様なコード例が挙がっています。(主観的優しい順)
- Handles 句の後にずらずら並べる(2012年6月5日 5:48 ひらぽんさん)
- AddHandler ステートメントをずらずら並べる(2012年6月7日 7:14 yksailaさん)
- 拡張メソッドを使う(2012年6月5日 8:20 ひらぽんさん)
- Partial を利用して拡張する(2012年6月5日 10:48 aviator__さん)
- コントロールを検索して AddHandler で登録する(2012年6月11日 14:02 Jitta)
- 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 演算子は、2 つのオブジェクト参照が同じオブジェクトを参照しているかどうかを判定します。ただし、値の比較は行われません。object1 と object2 の両方がまったく同じオブジェクト インスタンスを参照している場合、result は True になります。それ以外の場合は、result は False です。
Dim CtrlItem As Control ’(B)ここが、おかしい?
入れ物を宣言していますが、中に何も入れていません。従って、「変数 'CtrlItem' は、値が割り当てられる前に使用されています。Null 参照の例外が実行時に発生する可能性があります。」
Jitta@わんくま同盟
- 回答としてマーク yksaila 2012年7月23日 15:14
すべての返信
-
> 現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(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
-
確かに共通のイベントハンドラを作成して個別にハンドリングしてあげても良いですが、
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化は時間が取れたらという事でご了承下さい。 -
> 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 ModulePublic 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
-
拡張メソッドも出ていますが一応・・・
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
-
現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(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@わんくま同盟
-
> 現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(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
-
現在、テキストボックスやコンボボックスに、個別に以下の同一のコード(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
-
ひらぽんさんへ
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 IfEnd Sub
(B) 'Form_Load(--フォームを開く)に、以下のコードを追加
(例)
AddHandler TX_KanaCode.TextChanged, AddressOf textBoxStr_TextChanged-----
AddHandler TX_S_InvoiceMemo.TextChanged, AddressOf textBoxStr_TextChanged
-----YKsaila
-
ひらぽんさんへ
拡張メソッドを作ってやってみました。 うまくいっています。
ただ下記の(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 IfIf 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 SubEnd 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 SubEnd 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 IfIf Not IsNumeric(textBoxN.Text) Then
MessageBox.Show("数値を入力してください。 入力しないなら、0 にしてください。")
e.Cancel = True
textBoxN.Select()
End If
End Subよろしく、お願いします。
YKsaila
- 編集済み yksaila 2012年6月8日 7:12
-
> 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
-
ひらぽんさんへ
できました、ありがとうございました。
以前の私のコード(下記記載)は、(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 IfIf Not IsNumeric(cb.Text) Then
MessageBox.Show("数値を入力してください。 入力しないなら、0 にしてくださいよ。")
e.Cancel = True
cb.Select()
End If
End Sub- 回答としてマーク yksaila 2012年7月23日 15:04
-
拡張メソッドも出ていますが一応・・・
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
-
1.
FormやControlのようなSystem.ComponentModel.Componentを継承するクラスは一つのファイルに一つのクラスのみ記述した方が良いでしょう。
Visual Studioのデザイナーはファイルの最も先頭にあるSystem.ComponentModel.Component.Componentを継承するクラスを表示しますから、一つのファイルにFormや自作のコントロールが定義されているとややこしくなります。(場合によってはデザイナーでエラーになります)
また、ファイル名はクラス名と同じにした方がわかりやすいでしょう。なので今回の場合だとTextBox.vbというファイルをプロジェクトに追加して、そこにコードを貼り付けてみてください。
2.
ファイルの先頭にでもImports System.ComponentModelを追加してください。
3.
上記の修正を適用してビルドすれば、普通のテキストボックスを使うのと同じ感覚でツールボックスの一覧から選択してフォーム上に配置できるはずです。 -
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.TextBoxPrivate 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 PropertyProtected 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 SubEnd Class
End Namespace- 編集済み yksaila 2012年6月11日 0:04
-
同じ内容だと思いますので、「VB,自作コンボボックス(2列表示)をツールボックスに追加したいのですが、うまくいきません?」の方に返信しておきました。
-
フォーム上のコントロールにイベントを結び付けていく方法ですが、こんな方法もあります。
まず、名前付けのルールを決めます。最大長チェックをする 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@わんくま同盟
-
yksailaさん、こんにちは。
ひらぽんさんの回答を有益だと判断しましたので、私の方で「回答としてマーク」を付けさせていただきました。yksailaさんの意に反している場合は、yksailaさんがこの回答マークを外すことが可能です。
このスレッドのタイトル、および主旨から判断し、それに直接合致する回答のみに「回答としてマーク」を付けさせていただきましたが、その他でyksailaさんが有益であると判断された回答がある場合は、「回答としてマーク」や「回答の候補に設定」を付けることが可能です。
このスレッドを後日訪れた人が、素早く有益な回答に辿り着けるという側面から、もう一度見直していただければ幸いです。なお、yksailaさんに、「回答としてマーク」や「回答の候補に設定」を付けることを強要するものではありません。以上、よろしくお願いいたします。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
-
YKsailaさんへ
了解しました。少々、出しゃばった感も感じており、不慣れで至らないところもあると思いますが、寛大な返信をいただき、ありがとうございます。
私もいろいろと模索中ですが、フォーラムは機械的な質問と回答が並ぶだけではなく、その中で人と人とのやり取りがあるわけですから、誰もが気持ちよく利用できるようにコミニュケーションを取っていけたらと思っています。
今後ともよろしくお願いいたします。★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
-
現在までの解決策の状況をまとめます。
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 ?
で、これが終わったら感想です!
-
現在までの解決策の状況をまとめます。
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 IntegerPublic Sub Bind(SlaveTextBox As TextBox, MaxLength As Integer)
Me.SlaveTextBox = SlaveTextBox
Me.MaxLength = MaxLengthEnd 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 IfIf SlaveTextBox.Text.Length > DirectCast(MaxLength, Integer) Then
MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", MaxLength))
SlaveTextBox.Text = ""
End IfEnd Sub
End Sub
End Class
- 編集済み yksaila 2012年6月16日 10:01
-
配列の各要素にインスタンスを作成する必要があります。
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
-
> 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
-
フォームにテキストボックスを置き、ダブルクリックすると以下のようなイベントハンドラーが自動的に記述されます。
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文字の入力をしたつもりでも、間違えて隣のキーを押してしまう事もあるでしょうし。 -
フォームにテキストボックスを置き、ダブルクリックすると以下のようなイベントハンドラーが自動的に記述されます。
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
-
発端は、MaxLengthに最大入力文字数を入れるだけだと、入力した人が最大入力数を超えて入力した場合に、それに気づかずに通り過ぎてしまう点だったのです。
実際の入力は、慣れた人だと相当な速さでしますので、制限を超えたのに気付かないこともあり得る、と想像したのです。
つまり、Maxlength=10 として、本人は11文字入力したつもりで、そのまま次の入力へ行く、ということが考えられたのです。 これを防ぐために、コメントを出すことから始めました。
私の従業員番号は8桁なのですが、ゼロでリードして10桁入力しなければならない場合があります。ところで、あまり入力する機会がないので覚えておらず、IME に「ばんごう」の読みで10桁登録しています。もし、8桁入力のところで変換で入力すると、必要なところが削られてしまうのですね。。。
そうです、それが分からないのです。 ()の中にいろいろ入れたり、追加したり。 試行錯誤しています。 なにか、適切なコードがあるはずなのですが?
なぜ「Bind」というメソッドを用意したのでしょう?何かと何かを結びつけるためですよね。では、SlaveTextBox_TextChanged が、イベント条件が整ったときに実行するように結びつけるコード(VB の場合は句もあり)は、何でしょう?
Jitta@わんくま同盟
-
Jittaさんへ
>私の従業員番号は8桁なのですが、ゼロでリードして10桁入力しなければならない場合があります。ところで、あまり入力する機会がないので覚えておらず、IME に「ばんごう」の読みで10桁登録してい>ます。もし、8桁入力のところで変換で入力すると、必要なところが削られてしまうのですね。。。
状況が良く分かりませんが、私の個人的な意見としては:
あるテキストボックスの入力値が、数字で8桁であるなら、それ以外は入力を受けないようにするのが、親切なソフトだとおもっています。 7桁以下、9桁以上は入力不可にします(もちろん、文字も不可)。
できれば、コメントもだす(コメントがうるさいのなら、ださない)。
YKsaila
-
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 MultiEventMulti(0).Bind(TX_S_BillSight, 6) ’テキストボックス、成功 TX_S_BillSight---テキストボックス名 制限入力字数=6
Multi(1).Bind(TX_S_TrustPeriod, 6) ’テキストボックス、成功 TX_S_TrustPeriod---テキストボックス名 制限入力字数=6Multi(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 IfIf SlaveControl.Text.Length > Multi_MaxLength Then
MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", Multi_MaxLength))
SlaveControl.Text = "" ’この辺は、あとでもっと良いものに変更します。
End If
End SubAddHandler SlaveControl.Validating,
Sub(sender, e)
If SlaveControl.Text = "" Then
Return
End IfIf Not IsNumeric(SlaveControl.Text) Then
MessageBox.Show("数値を入力してください。 入力しない場合、0 にしてください。")
e.Cancel = True
SlaveControl.Select()
Exit Sub
End If
End SubElseIf 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 IfEnd Sub
End Class
-
起きているエラーというのはNullReferenceExceptionですか?
どのような状況で、どのようなエラーが発生しているのか内容を明記しましょう。Me.SlaveComboBox = DirectCast(Me.SlaveComboBox, ComboBox)
「Me.SlaveComboBoxにパラメーターとして与えられたComboBox(つまるところSlaveControl)を保持させたい。」という意図があると思いますが、上記のコードではそのようになっていません。
TextBoxの方も同じ状態になっていますから、修正が必要でしょう。既にアドバイスもありましたが、やはりこのようなコードを書くよりも何とかしてComboBoxをDropDownListにする方が良い気がしてなりません。
-
エラーの原因に関してはNF64さんの回答で合ってると思いますので別の話を・・・
「Public SlaveControl As Control」 と 「Public Sub Bind(SlaveControl As Control, Multi_MaxLength As Integer)」 において、
変数とパラメータの定義名が重複しています。合わせて、Public変数に不要な物がいませんか?
コンパイルエラーにはなりませんが、バグや誤読に繋がるので気を付けた方が良いと思います。変数定義はスコープを意識した定義を心がけた方が良いです。
※クラスのメンバ変数にするのであればPublicにするかPrivateにするか、
メソッド内変数にしても、If文の中でしか使わない変数はIf文の中で定義する等。
そうする事で、参照出来るべきでない所から見えてしまうといった事が防げます。あとこれは好みですが、メンバ変数とパラメータ、メソッド内変数については、
名前の付け方にルールを設けておくと誤読が防げます。(プレフィックスを付ける等)
※社内開発であれば開発規約等ありませんか?質問と関係無い話なので、興味が無ければスルーして下さい。
-
起きているエラーというのは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
-
エラーの原因に関しては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
-
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で検索すれば出てくるでしょう。
-
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
-
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 ComboBoxPublic 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 IfIf SlaveControl.Text.Length > Multi_MaxLength Then
MessageBox.Show(String.Format("{0}文字を超える入力は、できません。", Multi_MaxLength))
SlaveControl.Text = ""
End If
End SubAddHandler SlaveControl.Validating,
Sub(sender, e)
If SlaveControl.Text = "" Then
Return
End IfIf Not IsNumeric(SlaveControl.Text) Then
MessageBox.Show("数値を入力してください。 入力しない場合、0 にしてください。")
e.Cancel = True
SlaveControl.Select()
Exit Sub
End If
End SubElseIf 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 IfEnd Sub
End Class -
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
-
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").ToStringEnd Sub
- 回答としてマーク yksaila 2012年7月23日 15:10
-
横から失礼します。すごく内容のこーいスレッドだなあと拝見しています。
ちょっと、気になったので、投稿させてください。
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_Mot 2012年7月9日 11:45 語句訂正
-
はぁ。これの方が、はるかに簡単でしたね。
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
-
横から失礼します。すごく内容のこーいスレッドだなあと拝見しています。
ちょっと、気になったので、投稿させてください。
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
-
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 コメント行訂正
-
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
-
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 StringSelect 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 SelectFUNC_CustomerType_Name = W_Name
End Function
'得意先分類---サーバー書き込み
Public Function FUNC_CustomerType_Int(a As String) As Integer
Dim W_Int As IntegerSelect 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 SelectFUNC_CustomerType_Int = W_Int
End Function以上です、よろしくお願いします。
専門家に近づくように努力しますので、よろしくお願いします。 本職でなくとも、可能では?
YKsaila
-
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
-
うまくいかなかったのは、おそらく、データの追加を Items.Add() で行っているからです。Items を直接操作すると、DataSource が使えません。このあたり、イベントやインターフェイスで細々しているので、DataSource に指定するクラスによっても、期待する動作をしません。
ここで一番簡単なのは、DataTable です。このクラスはデータ バインドを行うために必要な要素をすべて持っているので、これを派生して特定の型を作るか、私の先の投稿の「Columns.Add()」のようにカラムのデータ型を指定します。
別に専門職に近づかなくてもいいのではないですか?たとえば、私のうちでは、万能包丁で刺身を切ります。もし、寿司屋が万能包丁で切っていたら、二度と行くことはないでしょう。たとえば、MSDN にはデータ バインドに関する項目があります。プロには、紹介すると同時に一度は目を通すことを期待します。そうでない人には紹介だけします。要求されるものが違うのです。それだけのことです。
Jitta@わんくま同盟
-
yksaila さま ShiroYuki_Mot です。
DataBase の接続方針に関しては全く yksaila さまの仰るとおりです。
接続は必要な時のみ最短で、... で良いと思います。
一般的に、参照系は当初に1回で、更新系は随時です。
CB_Country_ID という名称と内容から参照系と判断して投稿しました。
更新系として扱われているのですね。
ただし、先のご投稿で示された「国情報を変更」が発生した場合、選択項目が削除されていたら、思うような結果は得られませんよね。 (過去の例では、国がドイツのようにひとつになるとか、ソ連のように分裂するとか。)
選んだ筈なのに空白が帰ってきた ... て。 (空白が帰る?)
Transaction を導入して対応したり通知の仕組みを組み込んだりが基本でしょうが、その前に、システム設計でどう対応するか検討した方が良いかも知れません。 既に、ご対応中のようですが。
色々な方法があると思います。 例外処理って本当に難しいですよね。 例外がどんどん増えていく!
スレッドの内容から、かなり脱線気味なので、私はこの辺で筆を置きます。
お騒がせしました。- 編集済み ShiroYuki_Mot 2012年7月12日 5:28 文面追加
-
jittaさんへ
ありがとうございます。 下記記載の件のご説明、理解しました。
>うまくいかなかったのは、おそらく、データの追加を Items.Add() で行っているからです。Items を直接操作すると、DataSource が使えません-------
----
>別に専門職に近づかなくてもいいのではないですか?
専門職の定義にもよりますね。 私には、遠大な計画があって、VBでまともに使える 「業務ソフト」 を自力で作りたいのです。 その意味では、専門職並みの知識が必要です。
ということで、プロとまではいかなくても、その予備軍扱いでいいのですが? 専門職(ま、予備軍、二軍?)扱いで、お願いします。 野球も二軍選手もプロですから、そのうち一軍に昇格できるかもしれないです、努力しだいで。
データ バインドに関する項目は重要ですよね、できればこの種の指導は、ぜひ、お願いします。
ちなみに、寿司屋のたとえは、少しおかしくありません? 私は料理人ではないですが、料理人の本当の価値は、しっかりした材料を選ぶこと、が基本だとおもいます。
最近、まともなレストランが少なくなってきました。 少し悲しい。
YKsaila
-
先の(直前の)書き込みと重複しますが、途中なので、再度まとめます。
”コンボボックスとテキストボックスへの共通指示を一般化する”、これがこのスレッドの課題でした!
コンボボックスについて、現在までに、判明したこと:
コンボ・ボックスの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
-
このスレッドでの、最後の質問になります(うまく行けばですが)。
ようするに、やりたいことは、ただ一つ → 数値専用・文字入力可・メモ型を含めて、テキストボックスに共通指示をだしたい。
これができれば、個々のテキストボックスへのコードを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
NextBind(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 MultiEventFor 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
-
(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)と一緒です。 -
私には、遠大な計画があって、VBでまともに使える 「業務ソフト」 を自力で作りたいのです。 その意味では、専門職並みの知識が必要です。
了解。新しいものを作る楽しみを知る機会へようこそ!!
”コンボボックスとテキストボックスへの共通指示を一般化する”、これがこのスレッドの課題でした!
次の様なコード例が挙がっています。(主観的優しい順)
- Handles 句の後にずらずら並べる(2012年6月5日 5:48 ひらぽんさん)
- AddHandler ステートメントをずらずら並べる(2012年6月7日 7:14 yksailaさん)
- 拡張メソッドを使う(2012年6月5日 8:20 ひらぽんさん)
- Partial を利用して拡張する(2012年6月5日 10:48 aviator__さん)
- コントロールを検索して AddHandler で登録する(2012年6月11日 14:02 Jitta)
- 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 演算子は、2 つのオブジェクト参照が同じオブジェクトを参照しているかどうかを判定します。ただし、値の比較は行われません。object1 と object2 の両方がまったく同じオブジェクト インスタンスを参照している場合、result は True になります。それ以外の場合は、result は False です。
Dim CtrlItem As Control ’(B)ここが、おかしい?
入れ物を宣言していますが、中に何も入れていません。従って、「変数 'CtrlItem' は、値が割り当てられる前に使用されています。Null 参照の例外が実行時に発生する可能性があります。」
Jitta@わんくま同盟
- 回答としてマーク yksaila 2012年7月23日 15:14
-
こんにちは、yksaila さん。
MSDNフォーラムのご利用ありがとうございます。オペレーターの山本です。
こちらのスレッドはかなり長くなってますよね。
#表示完了までに時間がかかるので、環境によってはストレスになるかなと思います。
恐れ入りますが、こちらのスレッドは一旦終了していただき、新規にスレッドを立てて続けていただいてもよろしいでしょうか。
両方のスレッドに相互のリンクを記載してくださいね。
お手数ですが、ご協力のほどよろしくお願いいたします。
___________________________________
日本マイクロソフト株式会社 フォーラム オペレーター 山本 春海