#title(.NETプログラミング研究 第28号) #navi(.NET プログラミング研究) #contents *.NETプログラミング研究 第28号 [#re2e1ff6] **.NET Tips [#l4cd1397] ***スプラッシュウィンドウを表示する [#bfd6c36c] #column(注意){{ この記事の最新版は「[[スプラッシュウィンドウを表示する>http://dobon.net/vb/dotnet/form/splashwindow.html]]」で公開しています。 }} 「スプラッシュウィンドウ(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を閉じることにします。 #code(vbnet){{ 'Splashフォーム Private Shared _form As SplashForm = Nothing '/ <summary> '/ Splashフォーム '/ </summary> Public Shared ReadOnly Property Form() As SplashForm Get Return _form End Get End Property '/ <summary> '/ Splashフォームを表示する '/ </summary> 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 }} #code(csharp){{ //Splashフォーム private static SplashForm _form = null; /// <summary> /// Splashフォーム /// </summary> public static SplashForm Form { get { return _form; } } /// <summary> /// Splashフォームを表示する /// </summary> 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を表示させる前にスプラッシュウィンドウを表示させています。 #code(vbnet){{ <STAThread()> _ Shared Sub Main() 'スプラッシュウィンドウを表示 SplashForm.ShowSplash() 'メインウィンドウを表示 Application.Run(New Form1) End Sub }} #code(csharp){{ [STAThread] static void Main() { //スプラッシュウィンドウを表示 SplashForm.ShowSplash(); //メインウィンドウを表示 Application.Run(new Form1()); } }} 次にスプラッシュウィンドウをメインスレッドとは別のスレッドで表示させるようにしてみます。 まず、スプラッシュウィンドウのフォームクラスは先の例と同様の方法で作成しておき(ここでもクラスの名前を"SplashForm"とします)、SplashFormクラス内に次のようなコードを記述してください。 #code(vbnet){{ 'Splashフォーム Private Shared _form As SplashForm = Nothing 'メインフォーム Private Shared _mainForm As Form = Nothing 'Splashを表示するスレッド Private Shared _thread As System.Threading.Thread = Nothing '/ <summary> '/ Splashフォーム '/ </summary> Public Shared ReadOnly Property Form() As SplashForm Get Return _form End Get End Property '/ <summary> '/ Splashフォームを表示する '/ </summary> '/ <param name="mainForm">メインフォーム</param> 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 '/ <summary> '/ Splashフォームを表示する '/ </summary> Public Shared Sub ShowSplash() ShowSplash(Nothing) End Sub '/ <summary> '/ Splashフォームを消す '/ </summary> 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 }} #code(csharp){{ //Splashフォーム private static SplashForm _form = null; //メインフォーム private static Form _mainForm = null; //Splashを表示するスレッド private static System.Threading.Thread _thread = null; /// <summary> /// Splashフォーム /// </summary> public static SplashForm Form { get { return _form; } } /// <summary> /// Splashフォームを表示する /// </summary> /// <param name="mainForm">メインフォーム</param> 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(); } /// <summary> /// Splashフォームを表示する /// </summary> public static void ShowSplash() { ShowSplash(null); } /// <summary> /// Splashフォームを消す /// </summary> 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を表示させる前にスプラッシュウィンドウを表示させています。 #code(vbnet){{ <STAThread()> _ Shared Sub Main() Dim mainForm As New Form1 'スプラッシュウィンドウを表示 SplashForm.ShowSplash(mainForm) 'メインウィンドウを表示 Application.Run(mainForm) End Sub }} #code(csharp){{ [STAThread] static void Main() { Form1 mainForm = new Form1(); //スプラッシュウィンドウを表示 SplashForm.ShowSplash(mainForm); //メインウィンドウを表示 Application.Run(mainForm); } }} (補足: スプラッシュウィンドウは、アプリケーションが起動してから準備ができるまでに長い時間がかかる時に、ユーザーを不安にさせたり、待たせたりしないために表示するというのが主要な使用目的でしょう。そのため、起動にそれほど時間のかからないアプリではスプラッシュウィンドウは必要ありません。ユーザーの立場からすると、無駄なスプラッシュウィンドウにはうんざりさせられます。) **.NET質問箱 [#fbecf8a5] ***DataGridセル内の文字列を折り返して表示するには? [#w73d3049] #column(注意){{ この記事の最新版は「[[DataGridセル内の文字列を折り返して表示する>http://dobon.net/vb/dotnet/datagrid/wrapwordtextcolumn.html]]」で公開しています。 }} ''質問:'' WindowsアプリケーションでDataGridコントロールのセル内の文字列を折り返して表示するにはどのようにすればよいのでしょうか? ''回答:'' DataGridColumnStyleクラスの派生クラスを作成し、そのPaintメソッドをオーバーライドして、文字列を折り返して描画するようにします。 以下にその例を示します。ここでは、DataGridTextBoxColumnクラスを継承し、新しいクラスDataGridTextBoxColumnExを作ります。 #code(vbnet){{ '/ <summary> '/ DataGridに文字列を折り返して表示するためのDataGridColumnStyle '/ </summary> 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 }} #code(csharp){{ /// <summary> /// DataGridに文字列を折り返して表示するためのDataGridColumnStyle /// </summary> 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]] ***フォームのコレクションを作成するには? [#gc368011] #column(注意){{ この記事の最新版は「[[VB6のFormsコレクションに代わるものは?>http://dobon.net/vb/dotnet/vb6/formscollection.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メソッドを追加したコレクションクラスを以下に紹介します。 #code(vbnet){{ '/ <summary> '/ フォームのコレクション '/ </summary> Public Class FormCollection Inherits CollectionBase '/ <summary> '/ インデクサ '/ </summary> 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 '/ <summary> '/ コレクションにフォームを追加する '/ </summary> '/ <param name="frm">追加するフォーム</param> '/ <returns>追加された位置</returns> Public Function Add(ByVal frm As Form) As Integer Return List.Add(frm) End Function '/ <summary> '/ コレクションからフォームを削除する '/ </summary> '/ <param name="frm">削除するフォーム</param> Public Sub Remove(ByVal frm As Form) List.Remove(frm) End Sub '/ <summary> '/ コレクションにフォームを挿入する '/ </summary> '/ <param name="index">挿入する位置</param> '/ <param name="frm">挿入するフォーム</param> Public Sub Insert(ByVal index As Integer, ByVal frm As Form) List.Insert(index, frm) End Sub '/ <summary> '/ フォームのインデックスを調べる '/ </summary> '/ <param name="frm">検索するフォーム</param> '/ <returns>フォームのインデックス</returns> Public Function IndexOf(ByVal frm As Form) As Integer Return List.IndexOf(frm) End Function '/ <summary> '/ コレクションにフォームが格納されているか調べる '/ </summary> '/ <param name="frm">検索するフォーム</param> '/ <returns>コレクションに格納されている時はtrue</returns> Public Function Contains(ByVal frm As Form) As Boolean Return List.Contains(frm) End Function End Class }} #code(csharp){{ /// <summary> /// フォームのコレクション /// </summary> public class FormCollection : CollectionBase { /// <summary> /// インデクサ /// </summary> public Form this[int index] { get { return (Form) List[index]; } set { List[index] = value; } } /// <summary> /// コレクションにフォームを追加する /// </summary> /// <param name="frm">追加するフォーム</param> /// <returns>追加された位置</returns> public int Add(Form frm) { return List.Add(frm); } /// <summary> /// コレクションからフォームを削除する /// </summary> /// <param name="frm">削除するフォーム</param> public void Remove(Form frm) { List.Remove(frm); } /// <summary> /// コレクションにフォームを挿入する /// </summary> /// <param name="index">挿入する位置</param> /// <param name="frm">挿入するフォーム</param> public void Insert(int index, Form frm) { List.Insert(index, frm); } /// <summary> /// フォームのインデックスを調べる /// </summary> /// <param name="frm">検索するフォーム</param> /// <returns>フォームのインデックス</returns> public int IndexOf(Form frm) { return List.IndexOf(frm); } /// <summary> /// コレクションにフォームが格納されているか調べる /// </summary> /// <param name="frm">検索するフォーム</param> /// <returns>コレクションに格納されている時はtrue</returns> public bool Contains(Form frm) { return List.Contains(frm); } } }} このコレクションの使い方は、例えば次のような感じです。 #code(vbnet){{ 'Dim _forms As New FormCollection 'というフィールドが宣言されているものとする 'Form1オブジェクトを作成 Dim f As New Form1 'コレクションに追加する _forms.Add(f) 'インデックス0のフォームを表示する _forms(0).ShowDialog() }} #code(csharp){{ //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]] **コメント [#mfda5dc1] #comment //これより下は編集しないでください #pageinfo([[:Category/.NET]],2004-03-08 (月) 06:00:00,DOBON!,2010-03-21 (日) 01:49:21,DOBON!) |