┏第28号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜メニュー ■.NET Tips ・スプラッシュウィンドウを表示する ■.NET質問箱 ・DataGridセル内の文字列を折り返して表示するには? ・フォームのコレクションを作成するには? 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜メニュー ─────────────────────────────── ■.NET Tips ─────────────────────────────── ●スプラッシュウィンドウを表示する 「スプラッシュウィンドウ(Splash Window)」とは、アプリケーショ ン起動時に真っ先に表示され、アプリケーションのロゴやバージョン などの情報を表示し、自動的に消える(消えないものも稀にあります) ウィンドウです。Microsoft Visual Studio .NETや、Officeなど、多 くのアプリケーションでスプラッシュウィンドウが使われています。 まず、スプラッシュウィンドウに必要な機能をあげてみます。 1.メインウィンドウより先に表示される。 2.画面中央に表示される。 3.ウィンドウにタイトルバーや枠が無い。 4.アプリケーションの準備ができたところで、自動的に閉じる。 1.に関しては、エントリポイント(Mainメソッド)または、メインフ ォームのコンストラクタやLoadイベントハンドラなどでメインフォー ムが表示される前にスプラッシュウィンドウを表示するようにすれば よいでしょう。2.に関してはスプラッシュウィンドウのフォームの StartPositionプロパティをCenterScreenに、3.に関しては FormBorderStyleプロパティをNoneにすればよいでしょう。 問題は4.です。どのタイミングでスプラッシュウィンドウを閉じるべ きでしょうか?メインフォームが表示されるタイミングでということ であれば、メインフォームのActivatedイベントで閉じるようにすれば よいでしょう。また、「アプリケーションの準備ができた」というこ とを「アプリケーションがアイドル状態になった時」と解釈すると、 Application.Idleイベントで閉じる方法も考えられます。さらに、メ インフォームのLoadイベントハンドラの最後で閉じる方法もあります。 起動してから指定時間までスプラッシュウィンドウを表示したいとき は、タイマーを使う方法もあるかもしれません(個人的な意見として は、スプラッシュウィンドウは用が無くなったらさっさと閉じるべき だと思いますので、この方法はお勧めしたくありません)。 以上の機能以外に、場合によっては、次のような機能も必要になるで しょう。 5.タスクバーに表示しない。(スプラッシュウィンドウがタスクバー に表示されるアプリも多いです。) 6.メインウィンドウより手前に表示される。 5.については、フォームのShowInTaskbarプロパティをFalseにすれば よいでしょう。6.については、フォームのTopMostプロパティをTrueに する方法があります。しかし、個人的な意見としては、TopMostにはせ ずに、メインウィンドウの後ろに隠れないようにAddOwnedFormメソッ ド等を使用するにとどめた方がよいと思います。 以上のことを踏まえ、実際にスプラッシュウィンドウを作ってみます。 まずプロジェクトに新しいフォームを追加し、名前を「SplashForm」 とします。SplashFormのStartPositionプロパティをCenterScreen、 FormBorderStyleプロパティをNone、さらにShowInTaskbarプロパティ をFalseにします。その他、画像の配置など、SplashFormのデザインを 適当に行ってください。 さらにSplashFormクラスに次のようなコードを記述します。ここでは Application.IdleイベントでSplashFormを閉じることにします。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Splashフォーム Private Shared _form As SplashForm = Nothing '/ '/ Splashフォーム '/ Public Shared ReadOnly Property Form() As SplashForm Get Return _form End Get End Property '/ '/ Splashフォームを表示する '/ Public Shared Sub ShowSplash() If _form Is Nothing Then 'Application.IdleイベントハンドラでSplashフォームを閉じる AddHandler Application.Idle, AddressOf Application_Idle 'Splashフォームを表示する _form = New SplashForm _form.Show() End If End Sub 'アプリケーションがアイドル状態になった時 Private Shared Sub Application_Idle( _ ByVal sender As Object, ByVal e As EventArgs) 'Splashフォームがあるか調べる If Not (_form Is Nothing) And _form.IsDisposed = False Then 'Splashフォームを閉じる _form.Close() End If _form = Nothing 'Application.Idleイベントハンドラの削除 RemoveHandler Application.Idle, AddressOf Application_Idle End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //Splashフォーム private static SplashForm _form = null; /// /// Splashフォーム /// public static SplashForm Form { get { return _form; } } /// /// Splashフォームを表示する /// public static void ShowSplash() { if (_form == null) { //Application.IdleイベントハンドラでSplashフォームを閉じる Application.Idle += new EventHandler(Application_Idle); //Splashフォームを表示する _form = new SplashForm(); _form.Show(); } } //アプリケーションがアイドル状態になった時 private static void Application_Idle(object sender, EventArgs e) { //Splashフォームがあるか調べる if (_form != null && _form.IsDisposed == false) { //Splashフォームを閉じる _form.Close(); } _form = null; //Application.Idleイベントハンドラの削除 Application.Idle -= new EventHandler(Application_Idle); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ SplashFormを表示するには、SplashForm.ShowSplashメソッドを呼び出 します。次の例では、エントリポイントであるMainメソッドでメイン フォームForm1を表示させる前にスプラッシュウィンドウを表示させて います。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ _ Shared Sub Main() 'スプラッシュウィンドウを表示 SplashForm.ShowSplash() 'メインウィンドウを表示 Application.Run(New Form1) End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ [STAThread] static void Main() { //スプラッシュウィンドウを表示 SplashForm.ShowSplash(); //メインウィンドウを表示 Application.Run(new Form1()); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 次にスプラッシュウィンドウをメインスレッドとは別のスレッドで表 示させるようにしてみます。 まず、スプラッシュウィンドウのフォームクラスは先の例と同様の方 法で作成しておき(ここでもクラスの名前を"SplashForm"とします)、 SplashFormクラス内に次のようなコードを記述してください。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Splashフォーム Private Shared _form As SplashForm = Nothing 'メインフォーム Private Shared _mainForm As Form = Nothing 'Splashを表示するスレッド Private Shared _thread As System.Threading.Thread = Nothing '/ '/ Splashフォーム '/ Public Shared ReadOnly Property Form() As SplashForm Get Return _form End Get End Property '/ '/ Splashフォームを表示する '/ '/ メインフォーム Public Shared Sub ShowSplash(ByVal mainForm As Form) If Not (_form Is Nothing) Or Not (_thread Is Nothing) Then Return End If _mainForm = mainForm 'メインフォームのActivatedイベントでSplashフォームを消す If Not (_mainForm Is Nothing) Then AddHandler _mainForm.Activated, _ AddressOf _mainForm_Activated End If 'スレッドの作成 _thread = New System.Threading.Thread( _ New System.Threading.ThreadStart(AddressOf StartThread)) _thread.Name = "SplashForm" _thread.IsBackground = True _thread.ApartmentState = System.Threading.ApartmentState.STA 'スレッドの開始 _thread.Start() End Sub '/ '/ Splashフォームを表示する '/ Public Shared Sub ShowSplash() ShowSplash(Nothing) End Sub '/ '/ Splashフォームを消す '/ Public Shared Sub CloseSplash() If Not (_form Is Nothing) And _form.IsDisposed = False Then 'Splashフォームを閉じる 'Invokeが必要か調べる If _form.InvokeRequired Then _form.Invoke(New MethodInvoker(AddressOf _form.Close)) Else _form.Close() End If End If If Not (_mainForm Is Nothing) Then RemoveHandler _mainForm.Activated, _ AddressOf _mainForm_Activated 'メインフォームをアクティブにする _mainForm.Activate() End If _form = Nothing _thread = Nothing _mainForm = Nothing End Sub 'スレッドで開始するメソッド Private Shared Sub StartThread() 'Splashフォームを作成 _form = New SplashForm 'Splashフォームをクリックして閉じられるようにする AddHandler _form.Click, AddressOf _form_Click 'Splashフォームを表示する Application.Run(_form) End Sub 'Splashフォームがクリックされた時 Private Shared Sub _form_Click( _ ByVal sender As Object, ByVal e As EventArgs) 'Splashフォームを閉じる CloseSplash() End Sub 'メインフォームがアクティブになった時 Private Shared Sub _mainForm_Activated( _ ByVal sender As Object, ByVal e As EventArgs) 'Splashフォームを閉じる CloseSplash() End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //Splashフォーム private static SplashForm _form = null; //メインフォーム private static Form _mainForm = null; //Splashを表示するスレッド private static System.Threading.Thread _thread = null; /// /// Splashフォーム /// public static SplashForm Form { get { return _form; } } /// /// Splashフォームを表示する /// /// メインフォーム public static void ShowSplash(Form mainForm) { if (_form != null || _thread != null) return; _mainForm = mainForm; //メインフォームのActivatedイベントでSplashフォームを消す if (_mainForm != null) { _mainForm.Activated += new EventHandler(_mainForm_Activated); } //スレッドの作成 _thread = new System.Threading.Thread(new System.Threading.ThreadStart(StartThread)); _thread.Name = "SplashForm"; _thread.IsBackground = true; _thread.ApartmentState = System.Threading.ApartmentState.STA; //スレッドの開始 _thread.Start(); } /// /// Splashフォームを表示する /// public static void ShowSplash() { ShowSplash(null); } /// /// Splashフォームを消す /// public static void CloseSplash() { if (_form != null && _form.IsDisposed == false) { //Splashフォームを閉じる //Invokeが必要か調べる if (_form.InvokeRequired) _form.Invoke(new MethodInvoker(_form.Close)); else _form.Close(); } if (_mainForm != null) { _mainForm.Activated -= new EventHandler(_mainForm_Activated); //メインフォームをアクティブにする _mainForm.Activate(); } _form = null; _thread = null; _mainForm = null; } //スレッドで開始するメソッド private static void StartThread() { //Splashフォームを作成 _form = new SplashForm(); //Splashフォームをクリックして閉じられるようにする _form.Click += new EventHandler(_form_Click); //Splashフォームを表示する Application.Run(_form); } //Splashフォームがクリックされた時 private static void _form_Click(object sender, EventArgs e) { //Splashフォームを閉じる CloseSplash(); } //メインフォームがアクティブになった時 private static void _mainForm_Activated(object sender, EventArgs e) { //Splashフォームを閉じる CloseSplash(); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 前のようにApplication.Idleイベントでスプラッシュウィンドウを閉 じると、スプラッシュウィンドウは表示されたと思うと、あっという 間に閉じてしまいますので、別のタイミングで閉じるようにします。 ここでは、メインフォームのActivatedイベントで閉じています。(さ らにSplashFormのClickイベントでもSplashFormを閉じるようにしてい ます。) SplashFormを表示するには、前と同様、SplashForm.ShowSplashメソッ ドを呼び出しますが、このときメインフォームのインスタンスを指定 できます。メインフォームを指定すると、そのActivatedイベントハン ドラで自動的にSplashFormが閉じられます。メインフォームを指定し なかった時は、適当な位置(メインウィンドウのActivatedイベントハ ンドラなど)でSplashForm.CloseSplashメソッドを呼び出して SplashFormを閉じてください。 上のコードを使ってスプラッシュウィンドウを表示させるコードを以 下に示します。ここでも、エントリポイントであるMainメソッドでメ インフォームForm1を表示させる前にスプラッシュウィンドウを表示さ せています。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ _ Shared Sub Main() Dim mainForm As New Form1 'スプラッシュウィンドウを表示 SplashForm.ShowSplash(mainForm) 'メインウィンドウを表示 Application.Run(mainForm) End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ [STAThread] static void Main() { Form1 mainForm = new Form1(); //スプラッシュウィンドウを表示 SplashForm.ShowSplash(mainForm); //メインウィンドウを表示 Application.Run(mainForm); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ (補足: スプラッシュウィンドウは、アプリケーションが起動してから準備が できるまでに長い時間がかかる時に、ユーザーを不安にさせたり、待 たせたりしないために表示するというのが主要な使用目的でしょう。 そのため、起動にそれほど時間のかからないアプリではスプラッシュ ウィンドウは必要ありません。ユーザーの立場からすると、無駄なス プラッシュウィンドウにはうんざりさせられます。) ─────────────────────────────── ■.NET質問箱 ─────────────────────────────── ●DataGridセル内の文字列を折り返して表示するには? 質問: WindowsアプリケーションでDataGridコントロールのセル内の文字列を 折り返して表示するにはどのようにすればよいのでしょうか? 回答: DataGridColumnStyleクラスの派生クラスを作成し、そのPaintメソッ ドをオーバーライドして、文字列を折り返して描画するようにします。 以下にその例を示します。ここでは、DataGridTextBoxColumnクラスを 継承し、新しいクラスDataGridTextBoxColumnExを作ります。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ '/ '/ DataGridに文字列を折り返して表示するためのDataGridColumnStyle '/ Public Class DataGridTextBoxColumnEx Inherits DataGridTextBoxColumn '文字列を描画するマージンを指定する Private _margin As New Point(0, 2) 'Paintメソッドをオーバーライドする Protected Overloads Overrides Sub Paint( _ ByVal g As Graphics, _ ByVal bounds As Rectangle, _ ByVal source As CurrencyManager, _ ByVal rowNum As Integer, _ ByVal backBrush As Brush, _ ByVal foreBrush As Brush, _ ByVal alignToRight As Boolean _ ) '表示する文字列を取得 Dim [text] As String = _ GetColumnValueAtRow([source], rowNum).ToString() Dim sf As New StringFormat '配置を指定する Select Case Me.Alignment Case HorizontalAlignment.Left sf.Alignment = StringAlignment.Near Case HorizontalAlignment.Center sf.Alignment = StringAlignment.Center Case HorizontalAlignment.Right sf.Alignment = StringAlignment.Far End Select 'テキストの方向を指定する If alignToRight Then sf.FormatFlags = sf.FormatFlags Or _ StringFormatFlags.DirectionRightToLeft End If '背景を塗りつぶす g.FillRectangle(backBrush, bounds) '文字列を描画する範囲を取得する Dim rectf As New RectangleF(bounds.X + _margin.X, _ bounds.Y + _margin.Y, _ bounds.Width - _margin.X * 2, _ bounds.Height - _margin.Y * 2) '文字列を描画する g.DrawString([text], Me.DataGridTableStyle.DataGrid.Font, _ foreBrush, rectf, sf) sf.Dispose() End Sub End Class '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ /// /// DataGridに文字列を折り返して表示するためのDataGridColumnStyle /// public class DataGridTextBoxColumnEx : DataGridTextBoxColumn { //文字列を描画するマージンを指定する Point _margin = new Point(0, 2); //Paintメソッドをオーバーライドする protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, Brush backBrush, Brush foreBrush, bool alignToRight) { //表示する文字列を取得 string text = GetColumnValueAtRow(source, rowNum).ToString(); StringFormat sf = new StringFormat(); //配置を指定する switch (this.Alignment) { case HorizontalAlignment.Left: sf.Alignment = StringAlignment.Near; break; case HorizontalAlignment.Center: sf.Alignment = StringAlignment.Center; break; case HorizontalAlignment.Right: sf.Alignment = StringAlignment.Far; break; } //テキストの方向を指定する if (alignToRight) sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft; //背景を塗りつぶす g.FillRectangle(backBrush, bounds); //文字列を描画する g.DrawString(text, this.DataGridTableStyle.DataGrid.Font, foreBrush, bounds.Inflate(-_margin.X, -_margin.Y), sf); sf.Dispose(); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ (上の例ではマージンを"_margin"で指定していますが、これがないと 文字列が描画される位置が上過ぎて、セルが編集になった時も TextBoxの上にちょっとはみ出てしまいます。) DataGridTextBoxColumnExクラスを使用するには、文字列を折り返して 表示したい列の列スタイルにDataGridTextBoxColumnExオブジェクトを 設定します。詳しい方法は、次のページをご覧ください。 ・DataGridの列の幅を変更する http://dobon.net/vb/dotnet/datagrid/columnwidth.html ○この記事の基になった掲示板のスレッド ・DataGridのセル内の文字列を折り返し表示する方法 ・投稿者(敬称略):吉田、管理人 http://dobon.net/vb/bbs/log3-1/208.html ─────────────────────────────── ●フォームのコレクションを作成するには? 質問: Visual Basic 6.0 にはFormsコレクションがありましたが、.NETにフ ォームのコレクションはありますか? 回答: VB6のFormsコレクションに相当するものは.NET Frameworkでは用意さ れていませんし、フォームオブジェクト専用のコレクションもありま せん。 「マイクロソフト サポート技術情報 - 308537」の「[HOW TO] Visual Basic .NET で Forms コレクションを作成する方法」では、カ スタムのFormsコレクションを作成する方法が紹介されています。 ・[HOW TO] Visual Basic .NET で Forms コレクションを作成する方 法 http://support.microsoft.com/default.aspx?scid=kb;ja;JP308537 しかしここで紹介されているクラスでは、インデックスを指定してオ ブジェクトを取得できず、不便です。 そこで、インデックスを指定してオブジェクトを取得できるようにし、 さらにInsert、IndexOf、Containsメソッドを追加したコレクションク ラスを以下に紹介します。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ '/ '/ フォームのコレクション '/ Public Class FormCollection Inherits CollectionBase '/ '/ インデクサ '/ Default Public Property Item(ByVal index As Integer) As Form Get Return CType(List(index), Form) End Get Set(ByVal Value As Form) List(index) = value End Set End Property '/ '/ コレクションにフォームを追加する '/ '/ 追加するフォーム '/ 追加された位置 Public Function Add(ByVal frm As Form) As Integer Return List.Add(frm) End Function '/ '/ コレクションからフォームを削除する '/ '/ 削除するフォーム Public Sub Remove(ByVal frm As Form) List.Remove(frm) End Sub '/ '/ コレクションにフォームを挿入する '/ '/ 挿入する位置 '/ 挿入するフォーム Public Sub Insert(ByVal index As Integer, ByVal frm As Form) List.Insert(index, frm) End Sub '/ '/ フォームのインデックスを調べる '/ '/ 検索するフォーム '/ フォームのインデックス Public Function IndexOf(ByVal frm As Form) As Integer Return List.IndexOf(frm) End Function '/ '/ コレクションにフォームが格納されているか調べる '/ '/ 検索するフォーム '/ コレクションに格納されている時はtrue Public Function Contains(ByVal frm As Form) As Boolean Return List.Contains(frm) End Function End Class '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ /// /// フォームのコレクション /// public class FormCollection : CollectionBase { /// /// インデクサ /// public Form this[int index] { get { return (Form) List[index]; } set { List[index] = value; } } /// /// コレクションにフォームを追加する /// /// 追加するフォーム /// 追加された位置 public int Add(Form frm) { return List.Add(frm); } /// /// コレクションからフォームを削除する /// /// 削除するフォーム public void Remove(Form frm) { List.Remove(frm); } /// /// コレクションにフォームを挿入する /// /// 挿入する位置 /// 挿入するフォーム public void Insert(int index, Form frm) { List.Insert(index, frm); } /// /// フォームのインデックスを調べる /// /// 検索するフォーム /// フォームのインデックス public int IndexOf(Form frm) { return List.IndexOf(frm); } /// /// コレクションにフォームが格納されているか調べる /// /// 検索するフォーム /// コレクションに格納されている時はtrue public bool Contains(Form frm) { return List.Contains(frm); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ このコレクションの使い方は、例えば次のような感じです。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Dim _forms As New FormCollection 'というフィールドが宣言されているものとする 'Form1オブジェクトを作成 Dim f As New Form1 'コレクションに追加する _forms.Add(f) 'インデックス0のフォームを表示する _forms(0).ShowDialog() '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //FormCollection _forms = new FormCollection(); //というフィールドが宣言されているものとする //Form1オブジェクトを作成 Form1 f = new Form1(); //コレクションに追加する _forms.Add(f); //インデックス0のフォームを表示する _forms[0].ShowDialog() //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ なお、このフォームコレクションには、開かれたフォームが自動的に 追加されたり、閉じられたフォームが自動的に削除される機能があり ません。これらの処理は自分で行う必要があります。 ○この記事の基になった掲示板のスレッド ・Formsコレクションについて ・投稿者(敬称略):はるか、よねKEN、tina、管理人 http://dobon.net/vb/bbs/log3-1/208.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. ===============================