So-net無料ブログ作成
検索選択

継承したコントロールのTextの初期値を変更する [VB.NET]

Buttonを継承してカスタムコントロール(以下SearchButton)を作成した。
SearchButtonには虫眼鏡のアイコンを設定したけど、ツールボックスから配置すると
TextにSearchButton1と入ってしまう。
別にTextを削除すれば良いのだけど、最初から空白が入れば良いなと思ってコンストラクタで
MyBase.Textに空白を設定したけど空白にならなかった。

調べてみて次のコードで実現できた。
参照:System.Design.dll
Imports System.ComponentModel

Public Class ControlDesignerEx
    Inherits System.Windows.Forms.Design.ControlDesigner

    Public Overrides Sub InitializeNewComponent(ByVal defaultValues As System.Collections.IDictionary)
        MyBase.InitializeNewComponent(defaultValues)
        Me.Control.Text = ""
    End Sub
End Class

<Designer(GetType(ControlDesignerEx))>
Public Class SearchButton
    Inherits Button

    Public Sub New()
        MyBase.New()
        MyBase.Image = UserCtrl.My.Resources.Resources.SearchIcon
    End Sub
End Class


Jsonファイルの読込み、書込み [VB.NET]

データを保存したり、マスターデータを読み込んだりしたいときは
CSVでデータファイルを作っていたのを、Jsonファイルにしてみた。

Jsonファイルの読み書きは、DataContractJsonSerializerを使用すれば簡単に使える。
Jsonファイルの内容をクラスとして扱える(シリアル化、逆シリアル化)ようになる。
使用するには、参照で System.Runtime.Serialization を追加する。

Dim trend As New TrendGroup

'TrendGroup.jsonを読み込んで、TrendGroupクラスにデータを格納する。
Util.ReadJson(Of TrendGroup)("C:\TrendGroup.json", trend)

'何か処理

'TrendGroupクラスのデータをTrendGroup.jsonに保存する。
Util.WriteJson(Of TrendGroup)("C:\TrendGroup.json", trend)

ファイル名:Util.vb
Imports System.Runtime.Serialization.Json

Public Class Util

    ''' <summary>
    ''' JSONファイルの書込
    ''' </summary>
    ''' <typeparam name="T"></typeparam>
    ''' <param name="path"></param>
    ''' <param name="target"></param>
    ''' <returns></returns>
    Public Shared Function WriteJson(Of T)(path As String, ByRef target As T) As Boolean

        Dim encUTF8 As System.Text.Encoding = New System.Text.UTF8Encoding(False)
        Dim resValue As Boolean = True
        Dim memory As New IO.MemoryStream
        Try
            'クラスのデータをJSON形式に変換(シリアル化)
            Dim serializer As New DataContractJsonSerializer(GetType(T))
            serializer.WriteObject(memory, target)
            Dim json As String = encUTF8.GetString(memory.ToArray())
            'データの書込
            Dim writer As New IO.StreamWriter(path, False, encUTF8)
            Try
                writer.Write(json)
            Catch ex As Exception
                resValue = False
            Finally
                writer.Close()
            End Try
        Catch ex As Exception
            resValue = False
        Finally
            memory.Close()
        End Try

        Return resValue

    End Function

    ''' <summary>
    ''' JSONファイルの読込
    ''' </summary>
    ''' <typeparam name="T"></typeparam>
    ''' <param name="path"></param>
    ''' <param name="target"></param>
    ''' <returns></returns>
    Public Shared Function ReadJson(Of T)(ByVal path As String, ByRef target As T) As Boolean

        Dim encUTF8 As System.Text.Encoding = New System.Text.UTF8Encoding(False)
        Dim resValue As Boolean = True
        Dim reader As New IO.StreamReader(path, encUTF8)
        Try
            'データの読込
            Dim json As String = reader.ReadToEnd()
            Dim memory As New IO.MemoryStream(encUTF8.GetBytes(json))
            Try
                'JSON形式のデータをクラスに変換(逆シリアル化)
                Dim serializer As New DataContractJsonSerializer(GetType(T))
                target = DirectCast(serializer.ReadObject(memory), T)
            Catch ex As Exception
                resValue = False
            Finally
                memory.Close()
            End Try
        Catch ex As Exception
            resValue = False
        Finally
            reader.Close()
        End Try

        Return resValue

    End Function

End Class


Order:=0とか付けておくと、保存したときにOrderの順番で保存される。
付けないとアルファベット順に保存される。(順番は読み書きするには問題ないけど)
ファイル名:TrendGroup.vb
Imports System.Runtime.Serialization.Json

<DataContract()>
Public Class TrendGroup

    <DataContract()>
    Public Class Axis
        <DataMember(Order:=0)> Public Property Minimum As Double
        <DataMember(Order:=1)> Public Property Maximum As Double
        <DataMember(Order:=2)> Public Property Interval As Double
    End Class

    <DataContract()>
    Public Class Trend
        <DataMember(Order:=0)> Public Property TrendNo As Integer
        <DataMember(Order:=1)> Public Property AxisType As Integer
        <DataMember(Order:=2)> Public Property LineColor As Integer
    End Class

    <DataContract()>
    Public Class Member
        <DataMember(Order:=0)> Public Property GroupNo As Integer
        <DataMember(Order:=1)> Public Property GroupName As String
        <DataMember(Order:=2)> Public Property Axis1 As Axis
        <DataMember(Order:=3)> Public Property Axis2 As Axis
        <DataMember(Order:=4)> Public Property Trend As Trend()
    End Class

    <DataMember(Order:=0)> Public Property TrendGroup As Member()

End Class


Jsonファイルを書き込むと、整形しないと見づらくなるので整形用ツールを用意しておくと便利。
Jsonファイルは、UTF-8(BOMなし)で保存する。
ファイル名:TrendGroup.json
{
    "TrendGroup": [
        {
            "GroupNo": 1,
            "GroupName": "グループNo1",
            "Axis1": {
                "Minimum": -30,
                "Maximum": 30,
                "Interval": 5
            },
            "Axis2": {
                "Minimum": 0,
                "Maximum": 100,
                "Interval": 5
            },
            "Trend": [
                {
                    "TrendNo": 1,
                    "AxisType": 0,
                    "LineColor": -65536
                },
                {
                    "TrendNo": 2,
                    "AxisType": 0,
                    "LineColor": -23296
                },
                {
                    "TrendNo": 3,
                    "AxisType": 1,
                    "LineColor": -256
                }
            ]
        },
        {
            "GroupNo": 2,
            "GroupName": "グループNo2",
            "Axis1": {
                "Minimum": 0,
                "Maximum": 60,
                "Interval": 5
            },
            "Axis2": {
                "Minimum": 0,
                "Maximum": 100,
                "Interval": 5
            },
            "Trend": [
                {
                    "TrendNo": 4,
                    "AxisType": 0,
                    "LineColor": -65536
                },
                {
                    "TrendNo": 5,
                    "AxisType": 0,
                    "LineColor": -23296
                },
                {
                    "TrendNo": 6,
                    "AxisType": 1,
                    "LineColor": -256
                }
            ]
        }
    ]
}

DataGridViewの選択行を元の行に戻す [VB.NET]

DataGridViewでDataSourceを設定すると選択行が先頭行になる。
選択行を覚えておき、元の位置に戻すプログラムのメモ。
'現在選択している行を取得
Dim rowFirst As Integer = 0
Dim rowCurrent As Integer = 0
If DataGridView1.CurrentCell IsNot Nothing Then
    rowFirst = DataGridView1.FirstDisplayedScrollingRowIndex
    rowCurrent = DataGridView1.CurrentCell.RowIndex
End If

DataGridView1.DataSource = dt

'選択していた行へ復旧
If DataGridView1.CurrentCell IsNot Nothing Then
    DataGridView1.FirstDisplayedScrollingRowIndex = rowFirst
    DataGridView1.CurrentCell = DataGridView1(0, rowCurrent)
End If

DataGridViewのセル結合 [VB.NET]

DataGridViewでセルを結合するためには、自分でセルを結合したかのように描画する必要があるみたい。

そこで次の表を作成したいときのプログラムメモ。
表.png

・表を見せるのみです。
・行列の追加、編集、削除や、セル幅の変更は考慮していません。
・行列のヘッダーは無効にしています。
・DataGridViewコントロールの名前は、「DataGridView1」です。
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    Init()

End Sub

Private Sub Init()

    'ちらつき防止
    Dim type As System.Type = GetType(DataGridView)
    Dim propertyInfo As System.Reflection.PropertyInfo = type.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.NonPublic)
    propertyInfo.SetValue(DataGridView1, True, Nothing)

    DataGridView1.AllowUserToAddRows = False
    DataGridView1.ReadOnly = True
    DataGridView1.AllowUserToDeleteRows = False
    DataGridView1.ColumnHeadersVisible = False
    DataGridView1.RowHeadersVisible = False

    Dim i As Integer
    Dim j As Integer
    Dim colAdd As DataGridViewColumn
    For i = 1 To 3
        colAdd = New DataGridViewColumn
        colAdd.Name = String.Format("Column{0}", i)
        colAdd.Width = 60
        colAdd.CellTemplate = New DataGridViewTextBoxCell
        DataGridView1.Columns.Add(colAdd)
    Next

    '結合したいセルには同じテキストを入れておく
    Dim cellText(,) As String = {
        {"店舗", "店舗", "売上"},
        {"店舗名", "担当エリア", "売上"},
        {"北支店", "駅北", "10000"},
        {"北支店", "駅南", "9000"},
        {"西支店", "商店街", "12000"},
        {"東支店", "住宅区", "8000"},
        {"南支店", "商業区", "11000"}
    }
    Dim rowAdd As DataGridViewRow
    For i = 0 To 6
        rowAdd = New DataGridViewRow
        rowAdd.CreateCells(DataGridView1)
        For j = 0 To rowAdd.Cells.Count - 1
            rowAdd.Cells(j).Value = cellText(i, j)
        Next
        DataGridView1.Rows.Add(rowAdd)
    Next

End Sub

Private Sub DataGridView1_CellPainting(sender As Object, e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting

    '結合したいセルをここに書く
    MergeCell(e, New Point(0, 0), New Point(1, 0))
    MergeCell(e, New Point(2, 0), New Point(2, 1))
    MergeCell(e, New Point(0, 1), New Point(0, 1))
    MergeCell(e, New Point(1, 1), New Point(1, 1))

End Sub

'Cell1には、セルの開始位置(X, Y)
'Cell2には、セルの終了位置(X, Y)
Private Sub MergeCell(ByRef e As System.Windows.Forms.DataGridViewCellPaintingEventArgs, Cell1 As Point, Cell2 As Point)

    If (e.RowIndex >= Cell1.Y AndAlso e.RowIndex <= Cell2.Y) AndAlso (e.ColumnIndex >= Cell1.X AndAlso e.ColumnIndex <= Cell2.X) Then

        Dim rect As New Rectangle With {.X = 0, .Y = 0, .Width = 0, .Height = 0}
        Dim i As Integer

        '開始セルの位置
        '結合セルが画面外にあるときの位置を考慮
        For i = Cell1.Y + 1 To DataGridView1.FirstDisplayedScrollingRowIndex
            rect.Y -= DataGridView1(Cell1.X, i - 1).Size.Height
        Next
        For i = DataGridView1.FirstDisplayedScrollingRowIndex + 1 To Cell1.Y
            rect.Y += DataGridView1(Cell1.X, i - 1).Size.Height
        Next
        '結合セルが画面外にあるときの位置を考慮
        For i = Cell1.X + 1 To DataGridView1.FirstDisplayedScrollingColumnIndex
            rect.X -= DataGridView1(i - 1, Cell1.Y).Size.Width
        Next
        For i = DataGridView1.FirstDisplayedScrollingColumnIndex + 1 To Cell1.X
            rect.X += DataGridView1(i - 1, Cell1.Y).Size.Width
        Next

        '終了セルの幅
        For i = Cell1.Y To Cell2.Y
            rect.Height += DataGridView1(Cell2.X, i).Size.Height
        Next
        For i = Cell1.X To Cell2.X
            rect.Width += DataGridView1(i, Cell2.Y).Size.Width
        Next

        'セル位置の補正
        rect.X += 1
        rect.Y += 1

        'グラデーションをかけてヘッダーぽく見せる
        Dim gb As New System.Drawing.Drawing2D.LinearGradientBrush(rect, SystemColors.ControlLightLight, SystemColors.Control, System.Drawing.Drawing2D.LinearGradientMode.Vertical)
        gb.GammaCorrection = True

        '通常の塗りつぶし
        'e.Graphics.FillRectangle(New SolidBrush(SystemColors.Control), rect)
        e.Graphics.FillRectangle(gb, rect)
        e.Graphics.DrawRectangle(New Pen(DataGridView1.GridColor), rect)

        gb.Dispose()

        '描画するセル位置の文字をヘッダーテキストとして表示
        Dim headerText As String = DataGridView1(e.ColumnIndex, e.RowIndex).Value
        TextRenderer.DrawText(e.Graphics, headerText, e.CellStyle.Font, rect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

        e.Handled = True

    End If

End Sub

2016/01/27 結合セルが半分だけ画面外の場合を考慮
実行した表.png

FillRectangleで塗りつぶしが出来ない [VB.NET]

FillRectangleとDrawRectangleで塗りつぶされる領域が違ってた。

DrawRectangleより、Width, Height が、それぞれ1ずつ少なく塗りつぶされていた。

Dim g As Graphics = e.Graphics
Dim rect As New Rectangle(20, 20, 120, 120)

g.DrawRectangle(Pens.Blue, rect)
g.FillRectangle(Brushes.Red, rect)

g.Dispose()


FillRectangle.png

AssemblyAttributes.vbが見つからないエラーの対処 [VB.NET]

Windows 7 Professional 32ビット、VB2010を使用している環境で
急に次のエラーが表示されるようになった。
エラー 1 モジュール ファイル 'C:\Users\Administrator\AppData\Local\Temp\.NETFramework,Version=v4.0.AssemblyAttributes.vb' を開けません: 指定されたファイルが見つかりません。
対処方法は次のどれかで対処する。
1.プロジェクトの拡張子がsuoのファイルを削除すると出なくなる。
2.リビルドすると出なくなる。
3.ソリューション構成を「Debug」や「Release」に切り替えると出なくなる。

なんとなく3がおすすめ。
1.ファイルを消すのが何かいや。
2.色々更新されるからいや。とくにexeが。
3.何もしないから好き。

TableLayoutPanelにプログラムで列(Column)を追加 [VB.NET]

横にLabelを並べてチェック用の数値を表示したい。
チェック項目は設定で変更される。

Formのサイズを取得して、Labelのサイズを調整しても良かったけど
TableLayoutPanelを使ってみた。

参考にしたHP
http://dobon.net/vb/dotnet/control/tlrowstyles.html
http://dobon.net/vb/dotnet/control/tlrowinsert.html

FormにTableLayoutPanelを配置して横幅いっぱいまで広げる。
Labelは7個配置したい。
Dim intControllerStatusCount As Integer = 7

'動的にLabelを作成する
Dim lblControllerStatus() As Label
Array.Resize(Of Label)(lblControllerStatus, intControllerStatusCount)

'TableLayoutPanelの行列を全削除してから、1行を追加
'デザイナで行列の追加削除を繰り返していると残ったりしたので。
tlpControllerStatus.ColumnStyles.Clear()
tlpControllerStatus.RowStyles.Clear()
tlpControllerStatus.ColumnCount = intControllerStatusCount
tlpControllerStatus.RowCount = 1
tlpControllerStatus.RowStyles.Add(New RowStyle(SizeType.Percent, 100))

Dim i As Integer
For i = 0 To intControllerStatusCount - 1
    'Labelの初期設定、MarginをAll=0にしないと綺麗に並ばない
    lblControllerStatus(i) = New Label
    lblControllerStatus(i).Dock = DockStyle.Fill
    lblControllerStatus(i).BorderStyle = BorderStyle.Fixed3D
    lblControllerStatus(i).Margin = New Padding(0)
    '同じサイズで配置する
    tlpControllerStatus.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100))
    tlpControllerStatus.Controls.Add(lblControllerStatus(i), i, 0)
Next

'動的に作成したLabelにテキストを書くと画面に反映されたのでOK
For i = 0 To lblControllerStatus.Length - 1
    lblControllerStatus(i).Text = i.ToString()
Next

DataTableのデータをLINQで抽出する [VB.NET]

DataTableのSelectを使うと遅いって、よく見るのでLINQを使ってみた。
LINQがさっぱり分からなくて、とりあえずSelectの変わりになる程度のを作成した。

ItemTable (Index作成済み)には
ITEM_NO, ITEM_NAME

SeihinTable (Index作成済み)には
SEIHIN_NO, SEIHIN_NAME

製品Noの製品名称をItemTableから取得して格納する。
どちらのテーブルも数1000件ほどのデータで実行すると
Selectの方が早かった!

Select:200ms
LINQ:500ms

たぶんもっとデータが多くなるとLINQの方が早くなるんだと思います。

For Each row As DataRow In SeihinTable.Rows
    Dim itemNo As String

    'Select使用
    itemNo = row.Field(Of String)("SEIHIN_NO")
    If itemNo IsNot Nothing Then
        Dim rowsItem() As DataRow = ItemTable.Select("ITEM_NO = " & itemNo)
        row.SetField(Of String)("SEIHIN_NAME", rowsItem(0).Field(Of String)("ITEM_NAME"))
    End If

    'LINQを使用
    itemNo = row.Field(Of String)("SEIHIN_NO")
    If itemNo IsNot Nothing Then
        Dim rowsItem = From rowItem In ItemTable.AsEnumerable()
                   Where rowItem.Field(Of String)("ITEM_NO") = itemNo
                   Select rowItem
        row.SetField(Of String)("SEIHIN_NAME", rowsItem(0).Field(Of String)("ITEM_NAME"))
    End If
Next


SQL Serverに億単位のテストデータを作成する [VB.NET]

SQL Server 2014を導入しているシステムで21億件(int型の最大値まで)のテストデータで
検証する必要があったのでテストデータの作成方法を調べた。

最初にストアドでWHILEを使ってINSERTしていたら10日間ほどかかる計算になった。
ネットを調べると高速にテストデータを作成する方法が見つかったので試してみた。

SqlBulkCopyというメソッドを使用する。
で、下記のプログラムを作成した。
実行した結果、7700万件で27分でした。
*あとで21億件のかかった時間を追記予定

実行してたら11億件あたりで外付けHDDが壊れた。
別の外付けHDDを用意して実行したら、HDDからの応答がなくなった。
HDDは生きてるっぽい。

Imports System.Data.SqlClient

Private Sub CreateTestData()

    Dim connectionString As String = "Data Source=WINSERVER\MSSQLSERVER;Initial Catalog=TEST_DB;User ID=test_user;Password=test_user"
    Dim rowCount As Integer
    Dim stopCount As Integer

    Dim sbc As SqlBulkCopy = New SqlBulkCopy(connectionString, SqlBulkCopyOptions.TableLock)
    sbc.DestinationTableName = "TEST_TABLE"

    rowCount = 0
    stopCount = 2147483647

    Dim sw As Stopwatch = Stopwatch.StartNew()
    Dim i As Integer
    For i = 0 To 220000
        sbc.WriteToServer(TestDataTable(rowCount, stopCount))
        If stopCount = rowCount Then
            Exit For
        End If
        Debug.Print(sw.Elapsed.ToString())
    Next
    sw.Stop()

End Sub

Private Function TestDataTable(ByRef rowCount As Integer, ByVal stopCount As Integer) As DataTable

    Dim dt As New DataTable

    With dt
        .Columns.Add("ID", Type.GetType("System.Int32"))
        .Columns.Add("CHECK_FLG", Type.GetType("System.Int32"))
        .Columns.Add("EMG_DATE", Type.GetType("System.DateTime"))
        .Columns.Add("RES_DATE", Type.GetType("System.DateTime"))
        .Columns.Add("ITEM_NAME", Type.GetType("System.String"))
        .Columns.Add("SUB_NAME", Type.GetType("System.String"))
        .Columns.Add("TAG_NAME", Type.GetType("System.String"))
        .Columns.Add("UNIT", Type.GetType("System.String"))
        .Columns.Add("ITEM_TYPE1", Type.GetType("System.String"))
        .Columns.Add("ITEM_TYPE2", Type.GetType("System.String"))
        .Columns.Add("ITEM_TYPE3", Type.GetType("System.String"))
        .Columns.Add("VALUE", Type.GetType("System.Double"))
        .Columns.Add("COMMENT", Type.GetType("System.String"))
    End With

    Dim row As DataRow
    Dim i As Integer
    For i = 1 To 10000
        row = dt.NewRow

        row("ID") = rowCount
        row("CHECK_FLG") = 0
        row("EMG_DATE") = DateTime.Now
        row("RES_DATE") = DateTime.Now
        row("ITEM_NAME") = ""
        row("SUB_NAME") = ""
        row("TAG_NAME") = ""
        row("UNIT") = ""
        row("ITEM_TYPE1") = ""
        row("ITEM_TYPE2") = ""
        row("ITEM_TYPE3") = ""
        row("VALUE") = 0.0
        row("COMMENT") = ""

        dt.Rows.Add(row)

        If stopCount = rowCount Then
            Exit For
        End If

        rowCount += 1
    Next

    Return dt

End Function

Try Catchの例外を無効にする [VB.NET]

Try Catchの例外を無効にするというか、例外発生時にコード上で停止させる方法です。
Try Catchを書くのは良いけど、エラーが出ても「動いてるように見える」ので
デバッグ時には「ここでエラーでたよ!」と教えて欲しいです。

Visual Studio 2010を使用していますが、たぶん他も一緒だと思います。
メニューの[デバッグ] → [例外]を選択します。
例外の設定画面が表示されるので
とりあえず"Common Language Runtime Exceptions"の「スローされるとき」にチェックを
入れておけば、Try Catchを書いていても、例外が発生したコードで止まります。
例外.png
チェックを付けたままDebugやReleseでコンパイルしたexeを実行しても、
例外で止まることはなくちゃんとTry Catchしてくれます。


この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。