.NETプログラミング研究 第31号

.NET Tips

コントロールの配列を作成する

注意

この記事の最新版は「コントロールの配列を作成する」で公開しています。

コントロールの配列を作成する方法は、私の作成しているサイトDOBON.NETの.NET Tipsでは、「コントロールの配列を作成する」で説明しています。

ここで紹介している方法は、基本的にVS.NETのフォームデザイナを一切使わない方法です。このページをすでにご覧いただいた方には申し訳ありませんが、まずはこの方法を簡単に紹介させていただきます。

次のサンプルは、フォーム(Form1)にボタンコントロールの配列を作成し、ボタンをクリックするとそのボタンのTextを表示するものです。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
'ボタンコントロールの配列を作成
Friend testButtons(4) As System.Windows.Forms.Button
 
Private Sub Form1_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
    Dim i As Integer
 
    'ボタンコントロールのインスタンス作成し、プロパティを設定する
    For i = 0 To 4
        'インスタンス作成
        testButtons(i) = New System.Windows.Forms.Button()
        'プロパティ設定
        testButtons(i).Text = i.ToString()
        testButtons(i).Size = New Size(30, 30)
        testButtons(i).Location = New Point(i * 30, 10)
        'イベントハンドラに関連付け
        AddHandler testButtons(i).Click, AddressOf Me.testButtons_Click
    Next
 
    'フォームにコントロールを追加
    Me.Controls.AddRange(testButtons)
End Sub
 
Private Sub testButtons_Click(ByVal sender As System.Object, ByVal e As EventArgs)
    'クリックされたボタンを表示する
    MsgBox(sender.Text)
End Sub
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
//ボタンコントロールの配列を作成
private System.Windows.Forms.Button[] testButtons =
    new System.Windows.Forms.Button[5];
 
private void Form1_Load(object sender, System.EventArgs e)
{
    //ボタンコントロールのインスタンス作成し、プロパティを設定する
    for (int i = 0; i < 5; i++)
    {
        //インスタンス作成
        testButtons[i] = new System.Windows.Forms.Button();
        //プロパティ設定
        testButtons[i].Text = i.ToString();
        testButtons[i].Size = new Size(30, 30);
        testButtons[i].Location = new Point(i * 30, 10);
        //イベントハンドラに関連付け
        testButtons[i].Click += new EventHandler(testButtons_Click);
    }
 
    //フォームにコントロールを追加
    this.Controls.AddRange(testButtons);
}
 
private void testButtons_Click(object sender, EventArgs e)
{
    //クリックされたボタンを表示する
    MessageBox.Show(((System.Windows.Forms.Button) sender).Text);
}

また、VS.NETのフォームデザイナで追加してあるコントロールを配列にする例として、次のようなサンプルも紹介しています。ここではフォームデザイナでフォームにボタンコントロールが5つ(Button1〜5)追加されており、それらを配列に入れています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
'ボタンコントロールの配列を作成
Private testButtons(4) As System.Windows.Forms.Button
 
Private Sub Form1_Load(sender As Object, e As System.EventArgs)
        Handles MyBase.Load
   'ボタンコントロールの配列にすでに作成されているインスタンスを代入
   testButtons(0) = Button1
   testButtons(1) = Button2
   testButtons(2) = Button3
   testButtons(3) = Button4
   testButtons(4) = Button5
   
   'イベントハンドラに関連付け
   Dim i As Integer
   For i = 0 To 4
      AddHandler testButtons(i).Click, AddressOf testButtons_Click
   Next i
End Sub
 
Private Sub testButtons_Click(sender As Object, e As EventArgs)
   'クリックされたボタンを表示する
   MessageBox.Show(CType(sender, System.Windows.Forms.Button).Text)
End Sub
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
//ボタンコントロールの配列を作成
private System.Windows.Forms.Button[] testButtons =
    new System.Windows.Forms.Button[5];
 
private void Form1_Load(object sender, System.EventArgs e)
{
    //ボタンコントロールの配列にすでに作成されているインスタンスを代入
    testButtons[0] = Button1;
    testButtons[1] = Button2;
    testButtons[2] = Button3;
    testButtons[3] = Button4;
    testButtons[4] = Button5;
 
    //イベントハンドラに関連付け
    for (int i = 0; i < 5; i++)
        testButtons[i].Click += new EventHandler(testButtons_Click);
}
 
private void testButtons_Click(object sender, EventArgs e)
{
    //クリックされたボタンを表示する
    MessageBox.Show(((System.Windows.Forms.Button) sender).Text);
}

以上が今まで私のサイトで紹介していた方法でした。しかしこれらの方法はVB6ユーザーの方には扱いにくいようで、掲示板には相変わらずコントロール配列に関する質問が多く寄せられます。

そこで新たな第3の方法を考えてみました。前号で紹介した「フォームに配置されているコントロールを名前で探すには?」の方法を使い、「"特定の名前" + "1から連続する数字"」という名前を持つコントロールを配列にするメソッドを作ってみます。このメソッドは、例えばフォームに"TextBox1"、"TextBox2"、"TextBox3"...という名前の複数のTextBoxコントロールがあったとき、"TextBox"という文字列を指定することにより、TextBoxコントロールの配列を返すという機能を持ちます。(ただし、"TextBox1"のインデックスが0になります。また、数字が連続しており、途中が抜けたりしていない必要があります。)

以下にコントロール配列を作成するメソッド"GetControlArrayByName"を示します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
''' <summary>
''' コントロールの配列を取得する
''' </summary>
''' <param name="name">後ろの数字を除いたコントロールの名前</param>
''' <returns>コントロールの配列。
''' 取得できなかった時はnull(VB.NETではNothing)。</returns>
Public Function GetControlArrayByName(ByVal name As String) As Object
    Dim ctrs As New System.Collections.ArrayList
    Dim obj As Object
    Dim i As Integer = 1
    While True
        obj = FindControlByFieldName(name + i.ToString())
        i += 1
        If obj Is Nothing Then
            Exit While
        Else
            ctrs.Add(obj)
        End If
    End While
 
    If ctrs.Count = 0 Then
        Return Nothing
    Else
        Return ctrs.ToArray(ctrs(0).GetType())
    End If
End Function
 
''' <summary>
''' このフォームに配置されているコントロールを名前で探す
''' (フォームクラスのフィールドをフィールド名で探す)
''' </summary>
''' <param name="name">コントロール(フィールド)の名前</param>
''' <returns>見つかった時は、コントロールのオブジェクト。
''' 見つからなかった時は、null(VB.NETではNothing)。</returns>
Public Function FindControlByFieldName(ByVal name As String) As Object
    'まずプロパティ名を探し、見つからなければフィールド名を探す
 
    Dim t As System.Type = Me.GetType()
 
    Dim pi As System.Reflection.PropertyInfo = _
        t.GetProperty(name, _
            System.Reflection.BindingFlags.Public Or _
            System.Reflection.BindingFlags.NonPublic Or _
            System.Reflection.BindingFlags.Instance Or _
            System.Reflection.BindingFlags.DeclaredOnly)
 
    If Not pi Is Nothing Then
        Return pi.GetValue(Me, Nothing)
    End If
 
    Dim fi As System.Reflection.FieldInfo = _
        t.GetField(name, _
            System.Reflection.BindingFlags.Public Or _
            System.Reflection.BindingFlags.NonPublic Or _
            System.Reflection.BindingFlags.Instance Or _
            System.Reflection.BindingFlags.DeclaredOnly)
 
    If fi Is Nothing Then
        Return Nothing
    End If
 
    Return fi.GetValue(Me)
End Function
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
/// <summary>
/// コントロールの配列を取得する
/// </summary>
/// <param name="name">後ろの数字を除いたコントロールの名前</param>
/// <returns>コントロールの配列。
/// 取得できなかった時はnull(VB.NETではNothing)。</returns>
public object GetControlArrayByName(string name)
{
    System.Collections.ArrayList ctrs =
        new System.Collections.ArrayList();
    object obj;
    for (int i = 1;
        (obj = FindControlByFieldName(name + i.ToString())) != null;
        i++)
        ctrs.Add(obj);
    if (ctrs.Count == 0)
        return null;
    else
        return ctrs.ToArray(ctrs[0].GetType());
}
 
/// <summary>
/// このフォームにあるコントロールを名前で探す
/// (フォームクラスのフィールドをフィールド名で探す)
/// </summary>
/// <param name="name">コントロール(フィールド)の名前</param>
/// <returns>見つかった時は、コントロールのオブジェクト。
/// 見つからなかった時は、null(VB.NETではNothing)。</returns>
public object FindControlByFieldName(string name)
{
    System.Type t = this.GetType();
 
    System.Reflection.FieldInfo fi = t.GetField(
        name,
        System.Reflection.BindingFlags.Public |
        System.Reflection.BindingFlags.NonPublic |
        System.Reflection.BindingFlags.Instance |
        System.Reflection.BindingFlags.DeclaredOnly);
 
    if (fi == null)
        return null;
 
    return fi.GetValue(this);
}

このGetControlArrayByNameメソッドは、配列にしたいコントロールのあるフォームのクラスのメソッドとして記述します。

以下に具体的な使用法を示します。ここではフォーム(Form1)にテキストボックスコントロール"TextBox1"、"TextBox2"、"TextBox3"と、さらに同じ数のボタンコントロール"Button1"、"Button2"、"Button3"があるものとし、これらのコントロールをGetControlArrayByNameメソッドで配列(textBoxArrayとbuttonArrayフィールド)にしています(インデックス番号は"TextBox1"が0、"TextBox2"が1、"TextBox3"が2となります)。ボタンコントロールをクリックすると、同じインデックスのテキストボックスのTextを表示します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
'テキストボックスコントロール配列のフィールド
Dim textBoxArray() As TextBox
'ボタンコントロール配列のフィールド
Dim buttonArray() As Button
 
'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As EventArgs) Handles MyBase.Load
    'テキストボックスコントロールの配列を作成する
    textBoxArray = _
        CType(GetControlArrayByName("TextBox"), TextBox())
    'ボタンコントロールの配列を作成する
    buttonArray = _
        CType(GetControlArrayByName("Button"), Button())
 
    'ボタンをクリックした時にButton_Clickが呼び出されるようにする
    Dim btn As Button
    For Each btn In buttonArray
        AddHandler btn.Click, AddressOf Button_Click
    Next btn
End Sub
 
Private Sub Button_Click( _
    ByVal sender As Object, ByVal e As EventArgs)
    'クリックされたボタンのインデックス番号を取得する
    Dim index As Integer = -1
    Dim i As Integer
    For i = 0 To buttonArray.Length - 1
        If buttonArray(i).Equals(sender) Then
            index = i
            Exit For
        End If
    Next i
 
    '押されたボタンと同じインデックスのテキストボックスのTextを表示する
    If index > -1 Then
        MessageBox.Show(textBoxArray(index).Text)
    End If
End Sub
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
//テキストボックスコントロール配列のフィールド
TextBox[] textBoxArray;
//ボタンコントロール配列のフィールド
Button[] buttonArray;
 
//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    //テキストボックスコントロールの配列を作成する
    textBoxArray = (TextBox[]) GetControlArrayByName("TextBox");
    //ボタンコントロールの配列を作成する
    buttonArray = (Button[]) GetControlArrayByName("Button");
 
    //ボタンをクリックした時にButton_Clickが呼び出されるようにする
    foreach (Button btn in buttonArray)
        btn.Click += new EventHandler(Button_Click);
}
 
private void Button_Click(object sender, EventArgs e)
{
    //クリックされたボタンのインデックス番号を取得する
    int index = -1;
    for (int i = 0; i < buttonArray.Length; i++)
    {
        if (buttonArray[i].Equals(sender))
        {
            index = i;
            break;
        }
    }
 
    //押されたボタンと同じインデックスのテキストボックスのTextを表示する
    if (index > -1)
    {
        MessageBox.Show(textBoxArray[index].Text);
    }
}

.NET質問箱

「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿をQ&A形式にまとめ、紹介します。

SmtpMailクラスでメールを送るとQuoted-Printableでエンコードされる

注意

この記事の最新版は「SmtpMailクラスでメールを送るとQuoted-Printableでエンコードされる問題の解決法」で公開しています。

【質問】

SmtpMailクラスのSendメソッドを使用してメールを送ると、メールがQuoted-Printableでエンコードされることがあります。なぜでしょうか?

【回答】

SmtpMailクラスはCDOSYS(Collaboration Data Objects for Windows 2000)メッセージコンポーネントを使用してメールを送信しますが、なかさんからのご報告によると、マイクロソフトサポート技術情報412833の「[IIS]CDONTSで送信するメールがQuoted-Printableでエンコードされる」で説明されているのと同じことがCDOSYSでも起こるようです。

サポート技術情報412833での説明によると、メール本文の1行の文字数が76バイト以上になることが予想される場合、Quoted-Printableを適用し、転送時のみ75バイト以内に折り返す仕様になっているということです。さらに、なかさんからのご報告によると、半角文字と全角文字を何回か繰り返した場合もエンコードされるそうです(また同じ内容のメールであっても環境によって、エンコードされたりされなかったりすることがあるそうです)。

サポート技術情報412833で提示されている解決法は、あらかじめメールの本文をチェックし、一行が75バイト以下になるように改行を入れておくというものです。

この解決法が適切なのだろうと思いますが、なかさんからのご報告では、メールメッセージの"Content-Transfer-Encoding"ヘッダを"7bit"に変更すると、Quoted-Printableエンコードされなくなったとのことです。

"Content-Transfer-Encoding"ヘッダを"7bit"に変更してメールを送信する例を以下に示します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
Dim mm As New System.Web.Mail.MailMessage()
'送信者
mm.From = "sender@xxx.xx.com"
'送信先
mm.To = "recipient1@xxx.xx.com"
'題名
mm.Subject = "テスト"
'本文
mm.Body = "こんにちは。これはテストです。"
'JISコードに変換する
mm.BodyEncoding = System.Text.Encoding.GetEncoding(50220)
'無理やりContent-Transfer-Encoding=7bitとする
mm.Headers("Content-Transfer-Encoding") = "7bit"
'SMTPサーバーを指定する
System.Web.Mail.SmtpMail.SmtpServer = "(SMTPサーバーを指定する)"
'送信する
System.Web.Mail.SmtpMail.Send(mm)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
System.Web.Mail.MailMessage mm = new System.Web.Mail.MailMessage();
//送信者
mm.From = "sender@xxx.xx.com";
//送信先
mm.To = "recipient1@xxx.xx.com";
//題名
mm.Subject = "テスト";
//本文
mm.Body = "こんにちは。これはテストです。";
//JISコードに変換する
mm.BodyEncoding = System.Text.Encoding.GetEncoding(50220);
//無理やりContent-Transfer-Encoding=7bitとする
mm.Headers("Content-Transfer-Encoding") = "7bit";
//SMTPサーバーを指定する
System.Web.Mail.SmtpMail.SmtpServer = "(SMTPサーバーを指定する)";
//送信する
System.Web.Mail.SmtpMail.Send(mm);

○この記事の基になった掲示板のスレッド

「印刷中...」ダイアログを表示しないようにするには?

注意

この記事の最新版は「「印刷中...」ダイアログを表示しないようにする」で公開しています。

【質問】

PrintDocumentクラスのPrintメソッドで印刷をすると、「印刷中...」というダイアログが表示されますが、このダイアログを表示させない方法はありませんか?

【回答】

PrintDocumentクラスのPrintControllerプロパティはデフォルトではPrintControllerWithStatusDialogオブジェクトとなっていますが、これをStandardPrintControllerに変更することにより、「印刷中...」ダイアログを表示することなく印刷できるようになります。

なお、プリントコントローラに関しては、ヘルプをご覧ください。

またこの方法ではPrintPreviewDialogなど印刷プレビューに表示させる時に表示されるダイアログは消すことができません。(この場合にダイアログを表示させない方法は現在不明です。)

「印刷中...」ダイアログを表示せずに印刷をする例を示します。この例は、フォームにあるボタン(Button1)をクリックすると、ページ余白の内側の部分いっぱいに画像'test.bmp'を表示するものです。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
Private Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button1.Click
    'PrintDocumentオブジェクトの作成
    Dim pd As New System.Drawing.Printing.PrintDocument
    'PrintControllerプロパティをStandardPrintControllerに
    pd.PrintController = _
        New System.Drawing.Printing.StandardPrintController
    'PrintPageイベントハンドラの追加
    AddHandler pd.PrintPage, AddressOf pd_PrintPage
    '印刷を開始する
    pd.Print()
End Sub
 
Private Sub pd_PrintPage(ByVal sender As Object, _
        ByVal e As System.Drawing.Printing.PrintPageEventArgs)
    '画像を読み込む
    Dim img As Image = Image.FromFile("test.bmp")
    '画像を描画する
    e.Graphics.DrawImage(img, e.MarginBounds)
    '次のページがないことを通知する
    e.HasMorePages = False
    '後始末をする
    img.Dispose()
End Sub
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
private void button1_Click(object sender, System.EventArgs e)
{
    //PrintDocumentオブジェクトの作成
    System.Drawing.Printing.PrintDocument pd =
        new System.Drawing.Printing.PrintDocument();
    //PrintControllerプロパティをStandardPrintControllerに
    pd.PrintController =
        new System.Drawing.Printing.StandardPrintController();
    //PrintPageイベントハンドラの追加
    pd.PrintPage +=
        new System.Drawing.Printing.PrintPageEventHandler(pd_PrintPage);
    //印刷を開始する
    pd.Print();
}
 
private void pd_PrintPage(object sender,
    System.Drawing.Printing.PrintPageEventArgs e)
{
    //画像を読み込む
    Image img = Image.FromFile("test.bmp");
    //画像を描画する
    e.Graphics.DrawImage(img, e.MarginBounds);
    //次のページがないことを通知する
    e.HasMorePages = false;
    //後始末をする
    img.Dispose();
}

○この記事の基になった掲示板のスレッド

コメント



ページ情報
  • カテゴリ : .NET
  • 作成日 : 2004-04-19 (月) 06:00:00
  • 作成者 : DOBON!
  • 最終編集日 : 2010-03-21 (日) 02:05:53
  • 最終編集者 : DOBON!
[ トップ ]   [ 編集 | 凍結 | 差分 | バックアップ | 添付 | 複製 | 名前変更 | リロード ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]