#title(.NETプログラミング研究 第31号) #navi(.NETプログラミング研究) #contents *.NETプログラミング研究 第31号 [#wa28f118] **.NET Tips [#kf68387c] ***コントロールの配列を作成する [#paa71abd] #column(注意){{ この記事の最新版は「[[コントロールの配列を作成する>http://dobon.net/vb/dotnet/control/buttonarray.html]]」で公開しています。 この記事の最新版は「[[コントロールの配列を作成する>https://dobon.net/vb/dotnet/control/buttonarray.html]]」で公開しています。 }} コントロールの配列を作成する方法は、私の作成しているサイトDOBON.NETの.NET Tipsでは、「コントロールの配列を作成する」で説明しています。 -[[DOBON.NET .NET Tips - コントロールの配列を作成する>http://dobon.net/vb/dotnet/control/buttonarray.html]] -[[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形式にまとめ、紹介します。 -[[どぼん!のプログラミング掲示板>http://dobon.net/vb/bbs.html]] -[[どぼん!のプログラミング掲示板>https://dobon.net/vb/bbs.html]] ***SmtpMailクラスでメールを送るとQuoted-Printableでエンコードされる [#ad5cb8b4] #column(注意){{ この記事の最新版は「[[SmtpMailクラスでメールを送るとQuoted-Printableでエンコードされる問題の解決法>http://dobon.net/vb/dotnet/internet/smtpmailquotedprintable.html]]」で公開しています。 この記事の最新版は「[[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」を設定する方法: 投稿者(敬称略) なか、どぼん>http://dobon.net/vb/bbs/log2/708.html]] -[[メールヘッダの「Content-Transfer-Encoding」を設定する方法: 投稿者(敬称略) なか、どぼん>https://dobon.net/vb/bbs/log2/708.html]] ***「印刷中...」ダイアログを表示しないようにするには? [#ife69f56] #column(注意){{ この記事の最新版は「[[「印刷中...」ダイアログを表示しないようにする>http://dobon.net/vb/dotnet/graphics/hideprintingdialog.html]]」で公開しています。 この記事の最新版は「[[「印刷中...」ダイアログを表示しないようにする>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>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]] -[[印刷中メッセージ: 投稿者(敬称略) 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]],2004-04-19 (月) 06:00:00,DOBON!,2010-03-21 (日) 02:05:53,DOBON!) |