#title(.NETプログラミング研究 第28号)

#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!)
[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]