• 追加された行はこの色です。
  • 削除された行はこの色です。
#title(31)
#title(.NETプログラミング研究 第31号)

#navi(.NETプログラミング研究)

#contents

*31 [#s2216f6c]
*.NETプログラミング研究 第31号 [#wa28f118]

**.NET Tips [#kf68387c]

***コントロールの配列を作成する [#paa71abd]

**コメント [#wd5093a6]
#column(注意){{
この記事の最新版は「[[コントロールの配列を作成する>https://dobon.net/vb/dotnet/control/buttonarray.html]]」で公開しています。
}}

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

-[[DOBON.NET .NET Tips - コントロールの配列を作成する>https://dobon.net/vb/dotnet/control/buttonarray.html]]

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

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

#code(vbnet){{
'ボタンコントロールの配列を作成
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
}}

#code(csharp){{
//ボタンコントロールの配列を作成
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)追加されており、それらを配列に入れています。

#code(vbnet){{
'ボタンコントロールの配列を作成
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
}}

#code(csharp){{
//ボタンコントロールの配列を作成
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"を示します。

#code(vbnet){{
''' <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
}}

#code(csharp){{
/// <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を表示します。

#code(vbnet){{
'テキストボックスコントロール配列のフィールド
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
}}

#code(csharp){{
//テキストボックスコントロール配列のフィールド
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質問箱 [#d723c771]

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

-[[どぼん!のプログラミング掲示板>https://dobon.net/vb/bbs.html]]

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

#column(注意){{
この記事の最新版は「[[SmtpMailクラスでメールを送るとQuoted-Printableでエンコードされる問題の解決法>https://dobon.net/vb/dotnet/internet/smtpmailquotedprintable.html]]」で公開しています。
}}

''【質問】''

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

''【回答】''

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

-[[マイクロソフトサポート技術情報412833 - [IIS]CDONTSで送信するメールがQuoted-Printableでエンコードされる>http://support.microsoft.com/default.aspx?scid=kb;ja;JP412833]]

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

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

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

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

#code(vbnet){{
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)
}}

#code(csharp){{
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);
}}

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

-[[メールヘッダの「Content-Transfer-Encoding」を設定する方法: 投稿者(敬称略) なか、どぼん>https://dobon.net/vb/bbs/log2/708.html]]

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

#column(注意){{
この記事の最新版は「[[「印刷中...」ダイアログを表示しないようにする>https://dobon.net/vb/dotnet/graphics/hideprintingdialog.html]]」で公開しています。
}}

''【質問】''

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

''【回答】''

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

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

-[[PrintController プロパティ>http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemdrawingprintingprintdocumentclassprintcontrollertopic.asp]]

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

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

#code(vbnet){{
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
}}

#code(csharp){{
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();
}
}}

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

-[[印刷中メッセージ: 投稿者(敬称略) Tony>https://dobon.net/vb/bbs/log3-1/479.html]]
-[[印刷時の表示に関して: 投稿者(敬称略) Yuki、管理人>https://dobon.net/vb/bbs/log3-3/1390.html]]
-[[印刷中ダイアログを消す方法は???: 投稿者(敬称略) よしね、管理人>https://dobon.net/vb/bbs/log3-4/2422.html]]

**コメント [#obb6ab53]
#comment

//これより下は編集しないでください
#pageinfo([[:Category/.NET]] [[:Category/ASP.NET]],2010-03-21 (日) 02:05:43,DOBON!,2010-03-21 (日) 02:05:43,DOBON!)
#pageinfo([[:Category/.NET]],2004-04-19 (月) 06:00:00,DOBON!,2010-03-21 (日) 02:05:53,DOBON!)

[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]