- 追加された行はこの色です。
- 削除された行はこの色です。
#title(.NETプログラミング研究 第31号)
#navi(.NET プログラミング研究)
#navi(.NETプログラミング研究)
#contents
*.NETプログラミング研究 第31号 [#wa28f118]
**.NET Tips [#kf68387c]
***コントロールの配列を作成する [#paa71abd]
#column(注意){{
この記事の最新版は「[[コントロールの配列を作成する>http://dobon.net/vb/dotnet/control/buttonarray.html]]」で公開しています。
}}
コントロールの配列を作成する方法は、私の作成しているサイトDOBON.NETの.NET Tipsでは、「コントロールの配列を作成する」で説明しています。
-[[DOBON.NET .NET Tips - コントロールの配列を作成する>http://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形式にまとめ、紹介します。
-[[どぼん!のプログラミング掲示板>http://dobon.net/vb/bbs.html]]
***SmtpMailクラスでメールを送るとQuoted-Printableでエンコードされる [#ad5cb8b4]
#column(注意){{
この記事の最新版は「[[SmtpMailクラスでメールを送るとQuoted-Printableでエンコードされる問題の解決法>http://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」を設定する方法: 投稿者(敬称略) なか、どぼん>http://dobon.net/vb/bbs/log2/708.html]]
***「印刷中...」ダイアログを表示しないようにするには? [#ife69f56]
#column(注意){{
この記事の最新版は「[[「印刷中...」ダイアログを表示しないようにする>http://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>http://dobon.net/vb/bbs/log3-1/479.html]]
-[[印刷時の表示に関して: 投稿者(敬称略) Yuki、管理人>http://dobon.net/vb/bbs/log3-3/1390.html]]
-[[印刷中ダイアログを消す方法は???: 投稿者(敬称略) よしね、管理人>http://dobon.net/vb/bbs/log3-4/2422.html]]
**コメント [#obb6ab53]
#comment
//これより下は編集しないでください
#pageinfo([[:Category/.NET]],2004-04-19 (月) 06:00:00,DOBON!,2010-03-21 (日) 02:05:53,DOBON!)