┏第31号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ──<メニュー>─────────────────────── ■.NET Tips ・コントロールの配列を作成する ■.NET質問箱 ・SmtpMailクラスでメールを送るとQuoted-Printableでエンコードさ れる ・「印刷中...」ダイアログを表示しないようにするには? ─────────────────────────────── ─────────────────────────────── ■.NET Tips ─────────────────────────────── ●コントロールの配列を作成する コントロールの配列を作成する方法は、私の作成しているサイト DOBON.NETの.NET Tipsでは、「コントロールの配列を作成する」で説 明しています。 [URL]DOBON.NET .NET Tips - コントロールの配列を作成する http://dobon.net/vb/dotnet/control/buttonarray.html ここで紹介している方法は、基本的にVS.NETのフォームデザイナを一 切使わない方法です。このページをすでにご覧いただいた方には申し 訳ありませんが、まずはこの方法を簡単に紹介させていただきます。 次のサンプルは、フォーム(Form1)にボタンコントロールの配列を作成 し、ボタンをクリックするとそのボタンのTextを表示するものです。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'ボタンコントロールの配列を作成 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 ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //ボタンコントロールの配列を作成 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); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ また、VS.NETのフォームデザイナで追加してあるコントロールを配列 にする例として、次のようなサンプルも紹介しています。ここではフ ォームデザイナでフォームにボタンコントロールが5つ(Button1〜5)追 加されており、それらを配列に入れています。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'ボタンコントロールの配列を作成 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 ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //ボタンコントロールの配列を作成 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); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 以上が今まで私のサイトで紹介していた方法でした。しかしこれらの 方法はVB6ユーザーの方には扱いにくいようで、掲示板には相変わらず コントロール配列に関する質問が多く寄せられます。 そこで新たな第3の方法を考えてみました。前号で紹介した「フォーム に配置されているコントロールを名前で探すには?」の方法を使い、 「"特定の名前" + "1から連続する数字"」という名前を持つコントロー ルを配列にするメソッドを作ってみます。このメソッドは、例えばフ ォームに"TextBox1"、"TextBox2"、"TextBox3"...という名前の複数の TextBoxコントロールがあったとき、"TextBox"という文字列を指定す ることにより、TextBoxコントロールの配列を返すという機能を持ちま す。(ただし、"TextBox1"のインデックスが0になります。また、数字 が連続しており、途中が抜けたりしていない必要があります。) 以下にコントロール配列を作成するメソッド"GetControlArrayByName "を示します。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ''' ''' コントロールの配列を取得する ''' ''' 後ろの数字を除いたコントロールの名前 ''' コントロールの配列。 ''' 取得できなかった時はnull(VB.NETではNothing)。 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 ''' ''' このフォームに配置されているコントロールを名前で探す ''' (フォームクラスのフィールドをフィールド名で探す) ''' ''' コントロール(フィールド)の名前 ''' 見つかった時は、コントロールのオブジェクト。 ''' 見つからなかった時は、null(VB.NETではNothing)。 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 ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ /// /// コントロールの配列を取得する /// /// 後ろの数字を除いたコントロールの名前 /// コントロールの配列。 /// 取得できなかった時はnull(VB.NETではNothing)。 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()); } /// /// このフォームにあるコントロールを名前で探す /// (フォームクラスのフィールドをフィールド名で探す) /// /// コントロール(フィールド)の名前 /// 見つかった時は、コントロールのオブジェクト。 /// 見つからなかった時は、null(VB.NETではNothing)。 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); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ このGetControlArrayByNameメソッドは、配列にしたいコントロールの あるフォームのクラスのメソッドとして記述します。 以下に具体的な使用法を示します。ここではフォーム(Form1)にテキス トボックスコントロール"TextBox1"、"TextBox2"、"TextBox3"と、さ らに同じ数のボタンコントロール"Button1"、"Button2"、"Button3"が あるものとし、これらのコントロールをGetControlArrayByNameメソッ ドで配列(textBoxArrayとbuttonArrayフィールド)にしています(イ ンデックス番号は"TextBox1"が0、"TextBox2"が1、"TextBox3"が2とな ります)。ボタンコントロールをクリックすると、同じインデックス のテキストボックスのTextを表示します。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'テキストボックスコントロール配列のフィールド 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 ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //テキストボックスコントロール配列のフィールド 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); } } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ─────────────────────────────── ■.NET質問箱 ─────────────────────────────── 「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込 まれた.NETプログラミングに関する投稿をQ&A形式にまとめ、紹介 します。 [URL]どぼん!のプログラミング掲示板 http://dobon.net/vb/bbs.html ●SmtpMailクラスでメールを送るとQuoted-Printableでエンコードされる 【質問】 SmtpMailクラスのSendメソッドを使用してメールを送ると、メールが Quoted-Printableでエンコードされることがあります。なぜでしょう か? 【回答】 SmtpMailクラスはCDOSYS(Collaboration Data Objects for Windows 2000)メッセージコンポーネントを使用してメールを送信しますが、な かさんからのご報告によると、マイクロソフトサポート技術情報 412833の「[IIS]CDONTSで送信するメールがQuoted-Printableでエンコー ドされる」で説明されているのと同じことがCDOSYSでも起こるようで す。 [URL]マイクロソフトサポート技術情報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"に変更してメールを送信 する例を以下に示します。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 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) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 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); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ○この記事の基になった掲示板のスレッド [題名] メールヘッダの「Content-Transfer-Encoding」を設定する方法 [投稿者(敬称略)] なか、どぼん [URL] http://dobon.net/vb/bbs/log2/708.html ●「印刷中...」ダイアログを表示しないようにするには? 【質問】 PrintDocumentクラスのPrintメソッドで印刷をすると、「印刷中...」 というダイアログが表示されますが、このダイアログを表示させない 方法はありませんか? 【回答】 PrintDocumentクラスのPrintControllerプロパティはデフォルトでは PrintControllerWithStatusDialogオブジェクトとなっていますが、こ れをStandardPrintControllerに変更することにより、「印刷中...」 ダイアログを表示することなく印刷できるようになります。 なお、プリントコントローラに関しては、ヘルプをご覧ください。 [URL]PrintController プロパティ http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemdrawingprintingprintdocumentclassprintcontrollertopic.asp またこの方法ではPrintPreviewDialogなど印刷プレビューに表示させ る時に表示されるダイアログは消すことができません。(この場合に ダイアログを表示させない方法は現在不明です。) 「印刷中...」ダイアログを表示せずに印刷をする例を示します。この 例は、フォームにあるボタン(Button1)をクリックすると、ページ余白 の内側の部分いっぱいに画像'test.bmp'を表示するものです。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 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 ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 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(); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ○この記事の基になった掲示板のスレッド [題名] 印刷中メッセージ [投稿者(敬称略)] Tony [URL] http://dobon.net/vb/bbs/log3-1/479.html [題名] 印刷時の表示に関して [投稿者(敬称略)] Yuki、管理人 [URL] http://dobon.net/vb/bbs/log3-3/1390.html [題名] 印刷中ダイアログを消す方法は??? [投稿者(敬称略)] よしね、管理人 [URL] http://dobon.net/vb/bbs/log3-4/2422.html =============================== ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  (Microsoft MVP for Visual Basic, Oct 2003-Oct 2004)  http://dobon.net  dobon_info@yahoo.co.jp ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/vb/bbs.html ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 - 2004 DOBON! All rights reserved. ===============================