トップ回答者
VB,カスタム(自作)コンボボックスで一部エラーが出ます。

質問
-
VB2010Express, サーバーSQL2000使用です。 カスタムコンボボックスを作成しました、ツールボックスに追加表示され、フォームへのドラッグも正常です。
しかし、このコード内の(A)で、エラー表示がでます。
それで、(B)のように書き換えました。 すると下記のエラー表示がでます。
「'name' 引数を Null にすることはできません。警告 1 変数 'ItemString1' は、値が割り当てられる前に使用されています。Null 参照の例外が実行時に発生する可能性があります。」
どのように修正したらよいのでしょうか?
Yksaila
以下は、コードです;
Imports System.ComponentModel
Public Class MyComboBox
Inherits ComboBoxPublic ListMember1 As String
Public ListMember2 As StringPrivate Const ListWidth1 As Integer = 30
Private Const ListWidth2 As Integer = 100Public Sub New()
Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
Me.DropDownWidth = ListWidth1 + ListWidth2 + 20End Sub
Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)'▼データの内容を取得
Dim Row As DataRowView
Dim ItemString1 As String
Dim ItemString2 As StringRow = DirectCast(Me.Items(e.Index), DataRowView)
'ItemString1 = DirectCast(Row(ListMember1), String) ----(B)
'ItemString2 = DirectCast(Row(ListMember2), String) ----(B)
ItemString1 = Row(ListMember1) ’(A)------エラー 1 Option Strict On で 'Object' から 'String' への暗黙的な変換はできません。
ItemString2 = Row(ListMember2) ’(A)------エラー 1 Option Strict On で 'Object' から 'String' への暗黙的な変換はできません。
'▼項目と線を描画
Dim Rect As RectangleF
Dim myBrush As Brush
Dim LineLeft As Integer = e.Bounds.X + ListWidth1If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
myBrush = New SolidBrush(Color.White)
Else
myBrush = New SolidBrush(Me.ForeColor)
End If'背景
e.DrawBackground()'コード
Rect = New RectangleF(e.Bounds.X, e.Bounds.Y, LineLeft, e.Bounds.Height)
e.Graphics.DrawString(ItemString1, e.Font, myBrush, Rect)'区切り線
e.Graphics.DrawLine(Pens.Black, LineLeft, e.Bounds.Y, LineLeft, e.Bounds.Y + e.Bounds.Height)'名前
Rect = New RectangleF(LineLeft + 1, e.Bounds.Y, e.Bounds.Width - LineLeft - 1, e.Bounds.Height)
e.Graphics.DrawString(ItemString2, e.Font, myBrush, Rect)
e.DrawFocusRectangle()
e.Graphics.Flush()End Sub
End Class
回答
-
Jittaさんへ
ありがとうございます。 "後回しにしないで、まず解決したらどうですか !" という意味に、私は理解しましたので、やってみました。
最後に、うまく作動したコードを載せます。 欠点・問題点は、あるかもしれませんが。
>「作る」という作業を何か勘違いされていませんか? ソフトウェアというのは実体がないからか、とても簡単に作れると思っている方が多いように思います。しかし、本当のところは、>とても難しいものなのです。
いえ、大丈夫です。 勘違いはしていないと、思います。 作成が難しく大変な作業なのは、承知しています。 これを、一般(?)の人が誤解しているのも承知しています。 「普通、これは簡単だろう、2、3行書けば、終わるだろう」 と、一般人(?)が感じることが、逆にソフト作成では非常に大変だということも、経験しています。 私は、本職ではないですが、会社で使用するソフト作成が未経験なわけではありません。 少しは作成しましたので、ソフト作成の大変さが普通の人には理解されにくいのも、経験済みです。 この理解されないという事実は、ソフト作成者にとっては、理不尽と言えるかもしれないですね。
>この説明書を作る作業を「設計」と呼ぶ場合があります。説明書に書き出す書き出さないは別として、「設計」という作業は必ず必要です。
設計が重要・かつ不可欠なのも承知しています。 これができたら、ソフト作成はほぼ終わりだということも、どこかで聞いたことがあります。 でも、私はまだ、この段階には達していないかもしれないですが。
今後とも、よろしく、ご指導をお願いします。
Yksaila
以下は、作成コードです(一応うまくいっています):
Imports System.ComponentModel
Public Class MyComboBox
Inherits ComboBox' Public ListMember1 As String '以前は、Publicだったけど、Privateで良い? → 不要なので消去、使用しない
' Public ListMember2 As String '以前は、Publicだったけど、Privateで良い? → 不要なので消去、使用しないPrivate Const ListWidth1 As Integer = 50
Private Const ListWidth2 As Integer = 80Public Sub New()
Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
Me.DropDownWidth = ListWidth1 + ListWidth2 + 20End Sub
Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)'SQL認証接続
Dim St As String
Dim Con As New System.Data.SqlClient.SqlConnection
Dim SQL As System.Data.SqlClient.SqlCommand
Dim ServerName As String = "******" 'サーバー名(またはIPアドレス)
Dim UserID As String = "*****" 'ユーザーID
Dim Password As String = "*****" 'パスワード
Dim DatabaseName As String = "******"
St = "Server=" & ServerName & ";"
St &= "User ID=" & UserID & ";"
St &= "Password=" & Password & ";"
St &= "Initial Catalog=" & DatabaseName
Con.ConnectionString = St
SQL = Con.CreateCommand
Con.Open()Dim adapter = New SqlClient.SqlDataAdapter()
' Dim foundrows() As Data.DataRow
'コンボボックス:T_M_Area ----コンボボックス表示準備(CB_Area_ID)
Dim dsCombo4 = New DataSet
Dim sqCombo4 As String
sqCombo4 = "SELECT T_M_Area.Area_ID, T_M_Area.AreaCode, T_M_Area.AreaName FROM T_M_Area ORDER BY T_M_Area.Indication"
adapter.SelectCommand = New SqlClient.SqlCommand(sqCombo4, Con)
adapter.SelectCommand.CommandType = CommandType.Text
adapter.Fill(dsCombo4)
Dim dtCombo4 As New DataTable
dtCombo4 = dsCombo4.Tables(0)Dim MyComboBox1 As MyComboBox = New MyComboBox
'自作コンボボックス:MyComboBox1 ----コンボボックス表示準備(MyComboBox1)
MyComboBox1.DataSource = dtCombo4
MyComboBox1.DisplayMember = "AreaName"
MyComboBox1.ValueMember = "Area_ID"
MyComboBox1.DropDownStyle = ComboBoxStyle.DropDown
'
'▼データの内容を取得
Dim Row As DataRowView
Dim ItemString1 As String 'ここをIntegerにすると、下の ”ItemString1 = MyComboBox1.ValueMember”がエラーになる。
Dim ItemString2 As String'Try
Row = DirectCast(Me.Items(e.Index), DataRowView)
ItemString1 = MyComboBox1.ValueMember
ItemString2 = MyComboBox1.DisplayMemberItemString1 = Row(ItemString1).ToString '*****
'ItemString1 = DirectCast(Row(ItemString1), Integer) 'これは、エラー表示になる。
ItemString2 = DirectCast(Row(ItemString2), String)'▼項目と線を描画
Dim Rect As RectangleF
Dim myBrush As Brush
Dim LineLeft As Integer = e.Bounds.X + ListWidth1If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
myBrush = New SolidBrush(Color.White)
Else
myBrush = New SolidBrush(Me.ForeColor)
End If'背景
e.DrawBackground()'コード
Rect = New RectangleF(e.Bounds.X, e.Bounds.Y, LineLeft, e.Bounds.Height)
e.Graphics.DrawString(ItemString1, e.Font, myBrush, Rect)
'e.DrawFocusRectangle() '追加? →不要
'e.Graphics.Flush() '追加? →不要'区切り線
e.Graphics.DrawLine(Pens.Black, LineLeft, e.Bounds.Y, LineLeft, e.Bounds.Y + e.Bounds.Height)'名前
Rect = New RectangleF(LineLeft + 1, e.Bounds.Y, e.Bounds.Width - LineLeft - 1, e.Bounds.Height)
e.Graphics.DrawString(ItemString2, e.Font, myBrush, Rect)
e.DrawFocusRectangle()
e.Graphics.Flush()'Catch ex As Exception
'End TryCon.Close()
'▼後処理,追加-2012/04/16)
dtCombo4.Dispose()
adapter.Dispose()
SQL.Dispose()
Con.Dispose()
End Sub
End Class
すべての返信
-
Jittaさん、aviator_さんへ
今上記コードを読んでみますと、ListMember1 及び ListMember2 には何も指定されてないのが、分かります(なんとなく)。
では、どのようにししたら良いのかは、まだ自分には不明です。 もう少し進歩したら出来るでしょう、多分?
このコーナーは、質問スレッド ”VB、テキストボックスやコンボボックスへの指示を一括ですませたいのですが、うまくいきません” の関連で書いたものです。
先のスレッド(長くなって、前篇・後編となりました)の回答は、自分なりに得られましたので、ここでの問題解決は次の機会にします。 時間が経てば、解決できるようになるでしょう。
ありがとうございました。
YKsaila
-
ここでの問題解決は次の機会にします。 時間が経てば、解決できるようになるでしょう。
ん~~~「作る」という作業を何か勘違いされていませんか?
今のお仕事でも感じるのですが、ソフトウェアというのは実体がないからか、とても簡単に作れると思っている方が多いように思います。しかし、本当のところは、とても難しいものなのです。
プラモデルを作ったことはありますか?プラモデルには、必ず組み立て説明書があります。いや、原型を作る人にはそんなものはないですけどね。たいていの作る作業には、「説明書」があるんです。ソフトウェアの場合、その説明書は「仕様書」と呼ばれます。仕様書には、どんな入力、出力が有り、何をどうしたらどうなると言うことが書かれています。プログラムは、その説明書に沿って作ります。
この説明書を作る作業を「設計」と呼ぶ場合があります。説明書に書き出す書き出さないは別として、「設計」という作業は必ず必要です。この、「設計」という作業をせずに、プログラムを書こうとしているように思われます。
「今上記コードを読んでみますと、ListMember1 及び ListMember2 には何も指定されてないのが、分かります(なんとなく)。」ということですが、次のことを明らかにして下さい。
- ListMember1 とは何を表すものなのか。
- ListMember1 を、どの様に使うのか。
- ListMember1 が決まるのは何時か。
- ListMember1 を決めるのは誰か。
これらが決まると、どこで、どうやって設定しなければならないか、決まります。プログラムを作る作業というのは、こう言うものです。つまり、欲しい結果に対して、結果を得るために必要な項目を並べる。項目が決まる条件を調べる。条件を「欲しい結果」としてもう一度。…この繰り返しです。そしてこれは設計でもあります。つまり、アプリケーションを作る作業のほとんどは、設計することです。設計をせずに部品を並べても、それらしい形になるかもしれませんが、完成品ができるわけではありません。
なお、Visual Studio を初めとする開発用ツールのキャッチコピーに「簡単に」という言葉があります。これは、「書く作業」が簡単になると言うことです。プログラムを作ることの本質である、欲しいものを揃える作業が簡単になるわけではありません。これを間違えている人が多くて、困る。(もちろん、揃える作業と書く作業合わせて「プログラムを作る」なのですが)
Jitta@わんくま同盟
-
Jittaさんへ
ありがとうございます。 "後回しにしないで、まず解決したらどうですか !" という意味に、私は理解しましたので、やってみました。
最後に、うまく作動したコードを載せます。 欠点・問題点は、あるかもしれませんが。
>「作る」という作業を何か勘違いされていませんか? ソフトウェアというのは実体がないからか、とても簡単に作れると思っている方が多いように思います。しかし、本当のところは、>とても難しいものなのです。
いえ、大丈夫です。 勘違いはしていないと、思います。 作成が難しく大変な作業なのは、承知しています。 これを、一般(?)の人が誤解しているのも承知しています。 「普通、これは簡単だろう、2、3行書けば、終わるだろう」 と、一般人(?)が感じることが、逆にソフト作成では非常に大変だということも、経験しています。 私は、本職ではないですが、会社で使用するソフト作成が未経験なわけではありません。 少しは作成しましたので、ソフト作成の大変さが普通の人には理解されにくいのも、経験済みです。 この理解されないという事実は、ソフト作成者にとっては、理不尽と言えるかもしれないですね。
>この説明書を作る作業を「設計」と呼ぶ場合があります。説明書に書き出す書き出さないは別として、「設計」という作業は必ず必要です。
設計が重要・かつ不可欠なのも承知しています。 これができたら、ソフト作成はほぼ終わりだということも、どこかで聞いたことがあります。 でも、私はまだ、この段階には達していないかもしれないですが。
今後とも、よろしく、ご指導をお願いします。
Yksaila
以下は、作成コードです(一応うまくいっています):
Imports System.ComponentModel
Public Class MyComboBox
Inherits ComboBox' Public ListMember1 As String '以前は、Publicだったけど、Privateで良い? → 不要なので消去、使用しない
' Public ListMember2 As String '以前は、Publicだったけど、Privateで良い? → 不要なので消去、使用しないPrivate Const ListWidth1 As Integer = 50
Private Const ListWidth2 As Integer = 80Public Sub New()
Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
Me.DropDownWidth = ListWidth1 + ListWidth2 + 20End Sub
Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)'SQL認証接続
Dim St As String
Dim Con As New System.Data.SqlClient.SqlConnection
Dim SQL As System.Data.SqlClient.SqlCommand
Dim ServerName As String = "******" 'サーバー名(またはIPアドレス)
Dim UserID As String = "*****" 'ユーザーID
Dim Password As String = "*****" 'パスワード
Dim DatabaseName As String = "******"
St = "Server=" & ServerName & ";"
St &= "User ID=" & UserID & ";"
St &= "Password=" & Password & ";"
St &= "Initial Catalog=" & DatabaseName
Con.ConnectionString = St
SQL = Con.CreateCommand
Con.Open()Dim adapter = New SqlClient.SqlDataAdapter()
' Dim foundrows() As Data.DataRow
'コンボボックス:T_M_Area ----コンボボックス表示準備(CB_Area_ID)
Dim dsCombo4 = New DataSet
Dim sqCombo4 As String
sqCombo4 = "SELECT T_M_Area.Area_ID, T_M_Area.AreaCode, T_M_Area.AreaName FROM T_M_Area ORDER BY T_M_Area.Indication"
adapter.SelectCommand = New SqlClient.SqlCommand(sqCombo4, Con)
adapter.SelectCommand.CommandType = CommandType.Text
adapter.Fill(dsCombo4)
Dim dtCombo4 As New DataTable
dtCombo4 = dsCombo4.Tables(0)Dim MyComboBox1 As MyComboBox = New MyComboBox
'自作コンボボックス:MyComboBox1 ----コンボボックス表示準備(MyComboBox1)
MyComboBox1.DataSource = dtCombo4
MyComboBox1.DisplayMember = "AreaName"
MyComboBox1.ValueMember = "Area_ID"
MyComboBox1.DropDownStyle = ComboBoxStyle.DropDown
'
'▼データの内容を取得
Dim Row As DataRowView
Dim ItemString1 As String 'ここをIntegerにすると、下の ”ItemString1 = MyComboBox1.ValueMember”がエラーになる。
Dim ItemString2 As String'Try
Row = DirectCast(Me.Items(e.Index), DataRowView)
ItemString1 = MyComboBox1.ValueMember
ItemString2 = MyComboBox1.DisplayMemberItemString1 = Row(ItemString1).ToString '*****
'ItemString1 = DirectCast(Row(ItemString1), Integer) 'これは、エラー表示になる。
ItemString2 = DirectCast(Row(ItemString2), String)'▼項目と線を描画
Dim Rect As RectangleF
Dim myBrush As Brush
Dim LineLeft As Integer = e.Bounds.X + ListWidth1If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
myBrush = New SolidBrush(Color.White)
Else
myBrush = New SolidBrush(Me.ForeColor)
End If'背景
e.DrawBackground()'コード
Rect = New RectangleF(e.Bounds.X, e.Bounds.Y, LineLeft, e.Bounds.Height)
e.Graphics.DrawString(ItemString1, e.Font, myBrush, Rect)
'e.DrawFocusRectangle() '追加? →不要
'e.Graphics.Flush() '追加? →不要'区切り線
e.Graphics.DrawLine(Pens.Black, LineLeft, e.Bounds.Y, LineLeft, e.Bounds.Y + e.Bounds.Height)'名前
Rect = New RectangleF(LineLeft + 1, e.Bounds.Y, e.Bounds.Width - LineLeft - 1, e.Bounds.Height)
e.Graphics.DrawString(ItemString2, e.Font, myBrush, Rect)
e.DrawFocusRectangle()
e.Graphics.Flush()'Catch ex As Exception
'End TryCon.Close()
'▼後処理,追加-2012/04/16)
dtCombo4.Dispose()
adapter.Dispose()
SQL.Dispose()
Con.Dispose()
End Sub
End Class
-
本題とは関係ないんだけれど、
' Public ListMember1 As String '以前は、Publicだったけど、Privateで良い? → 不要なので消去、使用しない
' Public ListMember2 As String '以前は、Publicだったけど、Privateで良い? → 不要なので消去、使用しない
こういったコメントは、ToDoやHackを使うと後で探しやすくなりますよ。
http://msdn.microsoft.com/ja-jp/library/zce12xx2(v=vs.100).aspx