┏第50号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ──<メニュー>─────────────────────── ■.NET質問箱 ・DataGridの列の幅をTextの幅に合わせて自動的に変更するには? ・タブコントロールのタブを選択できないようにするには? ・埋め込まれたWAVのリソースファイルを再生するには? ・モードレスウィンドウが閉じられた時に結果を取得するには? ─────────────────────────────── ─────────────────────────────── ■.NET質問箱 ─────────────────────────────── 「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込 まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、 Q&A形式にまとめて紹介します。 [URL]どぼん!のプログラミング掲示板 http://dobon.net/vb/bbs.html ─────────────────────────────── ●DataGridの列の幅を文字列の幅に合わせて自動的に調節するには? 【質問】 System.Windows.Forms.DataGridの列の幅を、その列にあるすべての セルとヘッダの文字列の最大幅となるように自動調節したいのですが、 どのようにすればよいのでしょうか? 【回答】 DataGridの列のすべてのセルの文字列(及びヘッダのHeaderText)の 長さをGraphics.MeasureStringメソッドで調べ、一番長いものに列の Widthを設定するという方法になるでしょう。 この方法を使った簡単なコードを以下に紹介します。ここでは、 DataGridのDataSourceにDataTableが設定されているものとします。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ''' ''' DataGridの列の幅をTextの幅に合わせて変更する ''' ''' 対象とするDataGrid ''' 列の番号 Public Shared Sub AutoSizeColumnWidth( _ ByVal grid As DataGrid, ByVal column As Integer) 'DataGridのGraphicsを取得 Dim g As Graphics = Graphics.FromHwnd(grid.Handle) 'すべてのセルを調べて、一番広い幅を取得 Dim sf As New StringFormat(StringFormat.GenericTypographic) Dim dt As DataTable = CType(grid.DataSource, DataTable) Dim rowsCount As Integer = dt.Rows.Count Dim maxWidth As Single = 0 Dim i As Integer For i = 0 To rowsCount - 1 Dim [text] As String = grid(i, column).ToString() maxWidth = Math.Max(g.MeasureString( _ [text], grid.Font, grid.Width, sf).Width, maxWidth) Next i 'ヘッダの幅も考慮 Dim cs As DataGridColumnStyle = _ grid.TableStyles(dt.TableName).GridColumnStyles(column) maxWidth = Math.Max(g.MeasureString( _ cs.HeaderText, grid.Font, grid.Width, sf).Width, maxWidth) '破棄 g.Dispose() '幅の変更 cs.Width = CInt(maxWidth) + 8 End Sub ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ /// /// DataGridの列の幅をTextの幅に合わせて変更する /// /// 対象とするDataGrid /// 列の番号 public static void AutoSizeColumnWidth(DataGrid grid, int column) { //DataGridのGraphicsを取得 Graphics g = Graphics.FromHwnd(grid.Handle); //すべてのセルを調べて、一番広い幅を取得 StringFormat sf = new StringFormat(StringFormat.GenericTypographic); DataTable dt = ((DataTable) grid.DataSource); int rowsCount = dt.Rows.Count; float maxWidth = 0; for (int i = 0; i < rowsCount; i++) { string text = grid[i, column].ToString(); maxWidth = Math.Max(g.MeasureString( text, grid.Font, grid.Width, sf).Width, maxWidth); } //ヘッダの幅も考慮 DataGridColumnStyle cs = grid.TableStyles[dt.TableName].GridColumnStyles[column]; maxWidth = Math.Max(g.MeasureString( cs.HeaderText, grid.Font, grid.Width, sf).Width, maxWidth); //破棄 g.Dispose(); //幅の変更 cs.Width = (int) maxWidth + 8; } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ この方法は次のページでも紹介されていますので、参考にしてくださ い。 [URL]Windows Forms FAQ 5.52 How can I autosize a column in my datagrid? http://www.syncfusion.com/FAQ/WinForms/FAQ_c44c.asp#q877q [URL]microsoft.public.dotnet.languages.vb Re: how-to autosize the width of a column in a DataGrid http://www.msusenet.com/archive/index.php/t-244101.html しかしこの方法には欠点があります。それは、セル内に表示される文 字列をToStringで取得している点です。実際に表示される文字列は ToStringで取得できる文字列と同じになるとは限りません。しかし残 念ながら、実際に表示される文字列を取得するのはとても難しいです。 例えばDataGridTextBoxColumnが設定されている場合は、 DataGridTextBoxColumnのGetTextメソッドで取得できますが、このメ ソッドがprivateのため、通常はアクセスできません。 よって実際に表示される文字列を取得するには、「隠蔽されている非 パブリックメンバを呼び出す」のようにリフレクションを使って GetTextメソッドにアクセスするか、自分で列スタイルを作成するか などの方法になりそうです。(自分でGetTextと同じことをするコー ドを書く方法もありますが、全く同じになる保障はありません。) [URL]隠蔽されている非パブリックメンバを呼び出す http://dobon.net/vb/dotnet/programing/invokenonpublicmember.html ○この記事の基になった掲示板のスレッド [題名] DataGridの幅設定 [投稿者(敬称略)] えーぴーてー, 深山 [URL] http://dobon.net/vb/bbs/log3-4/2447.html ─────────────────────────────── ●タブコントロールのタブを選択できないようにするには? 【質問】 TabControl内のTabPageのEnabledプロパティをFalseにしてもそのタ ブは選択できてしまいます。指定したタブが選択できないようにする には、どうしたらいいのでしょうか? 【回答】 最も簡単なのは、TabControlのSelectedIndexChangedイベントで選択 したくないタブが選択された時に、別のタブを選択するという方法で す。 例えば次のようにすれば2番目のTabPageは選択できなくなります。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Private _tabIndex As Integer = -1 '選択されたタブが変更された時 Private Sub TabControl1_SelectedIndexChanged( _ ByVal sender As Object, ByVal e As System.EventArgs) _ Handles TabControl1.SelectedIndexChanged If TabControl1.SelectedIndex = 1 Then TabControl1.SelectedIndex = _tabIndex Else _tabIndex = TabControl1.SelectedIndex End If End Sub ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ private int _tabIndex = -1; //選択されたタブが変更された時 private void TabControl1_SelectedIndexChanged( object sender, EventArgs e) { if (TabControl1.SelectedIndex == 1) TabControl1.SelectedIndex = _tabIndex; else _tabIndex = TabControl1.SelectedIndex; } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ この方法では選択させたくないタブが一瞬表示されるかもしれません。 これが嫌だという場合は、さらに面倒な方法を使わなければなりませ ん。この方法については、「プログラミング道掲示板」の No8361 の スレッド、または、次に示すニュースグループ「microsoft.public. dotnet.framework.windowsforms」への投稿を参考にしてください。 [URL]タブコントロールのタブの選択について http://dobon.net/cgi-bin/vbbbs/cbbs.cgi?mode=al2&namber=8361&no=0 (このURLは変更されます。) [URL]Subject:Re: Disabling A Tab Page on A TAb Control Newsgroups:microsoft.public.dotnet.framework.windowsforms http://groups.google.co.jp/groups?hl=ja&lr=&inlang=ja&selm=ALTDd.62632%249N2.2159158%40weber.videotron.net ○この記事の基になった掲示板のスレッド [題名] タブコントロールについて [投稿者(敬称略)] taro, 管理人 [URL] http://dobon.net/vb/bbs/log3-5/2141.html ─────────────────────────────── ●埋め込まれたWAVのリソースファイルを再生するには? 【質問】 アセンブリにリソースとして埋め込んだWAVファイルを再生したいの ですが、どのような方法がありますか? 【回答】 まず、埋め込まれたWAVファイルを一時ファイルに保存し、再生する という方法があります。これは、例えば次のようになります。(この 例では、作成した一時ファイルを削除していません。実際には適当な タイミングで削除すべきです。) ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Imports System.Runtime.InteropServices Imports System.Resources Class MainClass '音を鳴らすWin32 API Private Const SND_ASYNC As Integer = &H1 Private Const SND_MEMORY As Integer = &H4 Private Const SND_FILENAME As Integer = &H20000 _ Private Shared Function PlaySound( _ ByVal pszSound As String, _ ByVal hmod As IntPtr, _ ByVal fdwSound As Integer) As Boolean End Function Public Shared Sub Main() Dim asm As System.Reflection.Assembly = _ System.Reflection.Assembly.GetExecutingAssembly() 'リソースの名前 Dim resourceName As String = asm.GetName().Name + ".test.wav" 'リソースを読み込む Dim strm As System.IO.Stream = _ asm.GetManifestResourceStream(resourceName) Dim buffer() As Byte = New Byte(strm.Length) {} strm.Read(buffer, 0, CInt(buffer.Length)) strm.Close() '一時ファイルに書き込む Dim tempName As String = System.IO.Path.GetTempFileName() Dim fs As New System.IO.FileStream( _ tempName, System.IO.FileMode.Create) fs.Write(buffer, 0, CInt(buffer.Length)) fs.Close() '音を鳴らす PlaySound(tempName, IntPtr.Zero, SND_FILENAME) End Sub End Class ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ using System.Runtime.InteropServices; using System.Resources; class MainClass { //音を鳴らすWin32 API const int SND_ASYNC = 0x1; const int SND_MEMORY = 0x4; const int SND_FILENAME = 0x20000; [DllImport("winmm.dll")] private static extern bool PlaySound( string pszSound, IntPtr hmod, uint fdwSound); public static void Main() { System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); //リソースの名前 string resourceName = asm.GetName().Name + ".test.wav"; //リソースを読み込む System.IO.Stream strm = asm.GetManifestResourceStream(resourceName); byte[] buffer = new Byte[strm.Length]; strm.Read(buffer, 0, (int) buffer.Length); strm.Close(); //一時ファイルに書き込む string tempName = System.IO.Path.GetTempFileName(); System.IO.FileStream fs = new System.IO.FileStream( tempName, System.IO.FileMode.Create); fs.Write(buffer, 0, (int) buffer.Length); fs.Close(); //音を鳴らす PlaySound(tempName, IntPtr.Zero, SND_FILENAME); } } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ PlaySound(もしくはsndPlaySound)関数を使ってWAVを再生する場合 は、一時ファイルを作成する必要はありません。次のようにメモリ内 に読み込まれたデータをそのまま再生することができます。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Imports System.Runtime.InteropServices Imports System.Resources Class MainClass '音を鳴らすWin32 API Private Const SND_ASYNC As Integer = &H1 Private Const SND_MEMORY As Integer = &H4 Private Const SND_FILENAME As Integer = &H20000 _ Private Shared Function PlaySound( _ ByVal pszSound() As Byte, _ ByVal hmod As IntPtr, _ ByVal fdwSound As Integer) As Boolean End Function Public Shared Sub Main() Dim asm As System.Reflection.Assembly = _ System.Reflection.Assembly.GetExecutingAssembly() 'リソースの名前 Dim resourceName As String = asm.GetName().Name + ".test.wav" 'リソースを読み込む Dim strm As System.IO.Stream = _ asm.GetManifestResourceStream(resourceName) Dim buffer() As Byte = New Byte(strm.Length) {} strm.Read(buffer, 0, CInt(buffer.Length)) strm.Close() '音を鳴らす PlaySound(buffer, IntPtr.Zero, SND_MEMORY) End Sub End Class ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ using System.Runtime.InteropServices; using System.Resources; class MainClass { //音を鳴らすWin32 API const int SND_ASYNC = 0x1; const int SND_MEMORY = 0x4; const int SND_FILENAME = 0x20000; [DllImport("winmm.dll")] private static extern bool PlaySound( byte[] pszSound, IntPtr hmod, uint fdwSound); public static void Main() { System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly(); //リソースの名前 string resourceName = asm.GetName().Name + ".test.wav"; //リソースを読み込む System.IO.Stream strm = asm.GetManifestResourceStream(resourceName); byte[] buffer = new Byte[strm.Length]; strm.Read(buffer, 0, (int) buffer.Length); strm.Close(); //音を鳴らす PlaySound(buffer, IntPtr.Zero, SND_MEMORY); } } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 補足:上記の例では音を再生するのにPlaySound関数を使用しました が、代わりにsndPlaySound関数を使用すると、Windows XP( Professional Edition)ではうまく行かないようです。sndPlaySound 関数についてMSDNでは「この関数は、PlaySound関数のサブセットで あり、Windowsの以前のバージョンとの互換性のために残されていま す。」と書かれており、PlaySound関数を使った方がよいでしょう。 ○この記事の基になった掲示板のスレッド [題名] 実行ファイルに埋め込んだ音楽データの読み出し方法 [投稿者(敬称略)] えりんぎ, Codingslave [URL] http://dobon.net/vb/bbs/log3-5/2424.html ─────────────────────────────── ●モードレスウィンドウが閉じられた時に結果を取得するには? 【質問】 モーダルダイアログとしてフォームを表示したときは、DialogResult プロパティで結果を取得することができますが、モードレスで表示し たときはできません。モードレスウィンドウが閉じられた時に結果を 取得するにはどのようにすればよいのでしょうか? 【回答】 ここでは、Form1からForm2をモードレスで表示し、Form1でForm2が閉 じた時の結果を取得するようにします。 まず、Form1にForm2のClosedイベントハンドラを作成することにより Form2が閉じられたことを知り、この時Form2の結果を取得するように します。次の例では、Button1をクリックすることにより、Form2を表 示しています。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'Button1のクリックイベントハンドラ Private Sub Button1_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Button1.Click Dim f2 As New Form2 AddHandler f2.Closed, AddressOf Form2_Closed '表示する f2.Show() End Sub 'Form2が閉じた時 Private Sub Form2_Closed(ByVal sender As Object, _ ByVal e As System.EventArgs) Dim f2 As Form2 = CType(sender, Form2) '結果を表示する Console.WriteLine(f2.DialogResult) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //Button1のクリックイベントハンドラ private void Button1_Click(object sender, System.EventArgs e) { Form2 f2 = new Form2(); f2.Closed += new EventHandler(Form2_Closed); //表示する f2.Show(); } //Form2が閉じた時 private void Form2_Closed(object sender, EventArgs e) { Form2 f = (Form2) sender; //結果を表示する Console.WriteLine(f.DialogResult); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Form2では、結果を設定してからフォームを閉じるようにします。 Form2のButton1をクリックすることによりDialogResultを DialogResult.OKとして閉じるには、次のようにします。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'Button1のクリックイベントハンドラ Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click '結果を設定する Me.DialogResult = DialogResult.OK '閉じる Me.Close() End Sub ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //Button1のクリックイベントハンドラ private void Button1_Click(object sender, System.EventArgs e) { //結果を設定する this.DialogResult = DialogResult.OK; //閉じる this.Close(); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ このような方法以外に掲示板では、Codingslaveさんが、別のスレッ ドを作成してForm.ShowDialogメソッドを呼び出すことにより、モー ドレスウィンドウのようにする方法を提案されています。 ○この記事の基になった掲示板のスレッド [題名] モードレスダイアログの結果を待つ? [投稿者(敬称略)] yuyu, Codingslave [URL] http://dobon.net/vb/bbs/log3-5/2491.html =============================== ■ここで示したコードの多くはまずC#で書き、それを「C# to VB.NET Translator」でVB.NETのコードに変換し、修正を加えたものです。 [URL]C# to VB.NET Translator http://authors.aspalliance.com/aldotnet/examples/translate.aspx ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  (Microsoft MVP for Visual Basic, Oct 2004-Oct 2005)  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. ===============================