.NETプログラミング研究 第28号 †
.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を閉じることにします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| | Private Shared _form As SplashForm = Nothing
Public Shared ReadOnly Property Form() As SplashForm
Get
Return _form
End Get
End Property
Public Shared Sub ShowSplash()
If _form Is Nothing Then
AddHandler Application.Idle, AddressOf Application_Idle
_form = New SplashForm
_form.Show()
End If
End Sub
Private Shared Sub Application_Idle( _
ByVal sender As Object, ByVal e As EventArgs)
If Not (_form Is Nothing) And _form.IsDisposed = False Then
_form.Close()
End If
_form = Nothing
RemoveHandler Application.Idle, AddressOf Application_Idle
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| | private static SplashForm _form = null;
public static SplashForm Form
{
get { return _form; }
}
public static void ShowSplash()
{
if (_form == null)
{
Application.Idle += new EventHandler(Application_Idle);
_form = new SplashForm();
_form.Show();
}
}
private static void Application_Idle(object sender, EventArgs e)
{
if (_form != null && _form.IsDisposed == false)
{
_form.Close();
}
_form = null;
Application.Idle -= new EventHandler(Application_Idle);
}
|
SplashFormを表示するには、SplashForm.ShowSplashメソッドを呼び出します。次の例では、エントリポイントであるMainメソッドでメインフォームForm1を表示させる前にスプラッシュウィンドウを表示させています。
1
2
3
4
5
6
7
8
| | <STAThread()> _
Shared Sub Main()
SplashForm.ShowSplash()
Application.Run(New Form1)
End Sub
|
1
2
3
4
5
6
7
8
9
| | [STAThread]
static void Main()
{
SplashForm.ShowSplash();
Application.Run(new Form1());
}
|
次にスプラッシュウィンドウをメインスレッドとは別のスレッドで表示させるようにしてみます。
まず、スプラッシュウィンドウのフォームクラスは先の例と同様の方法で作成しておき(ここでもクラスの名前を"SplashForm"とします)、SplashFormクラス内に次のようなコードを記述してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
| | Private Shared _form As SplashForm = Nothing
Private Shared _mainForm As Form = Nothing
Private Shared _thread As System.Threading.Thread = Nothing
Public Shared ReadOnly Property Form() As SplashForm
Get
Return _form
End Get
End Property
Public Shared Sub ShowSplash(ByVal mainForm As Form)
If Not (_form Is Nothing) Or Not (_thread Is Nothing) Then
Return
End If
_mainForm = mainForm
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
Public Shared Sub ShowSplash()
ShowSplash(Nothing)
End Sub
Public Shared Sub CloseSplash()
If Not (_form Is Nothing) And _form.IsDisposed = False Then
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()
_form = New SplashForm
AddHandler _form.Click, AddressOf _form_Click
Application.Run(_form)
End Sub
Private Shared Sub _form_Click( _
ByVal sender As Object, ByVal e As EventArgs)
CloseSplash()
End Sub
Private Shared Sub _mainForm_Activated( _
ByVal sender As Object, ByVal e As EventArgs)
CloseSplash()
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
| | private static SplashForm _form = null;
private static Form _mainForm = null;
private static System.Threading.Thread _thread = null;
public static SplashForm Form
{
get { return _form; }
}
public static void ShowSplash(Form mainForm)
{
if (_form != null || _thread != null)
return;
_mainForm = mainForm;
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();
}
public static void ShowSplash()
{
ShowSplash(null);
}
public static void CloseSplash()
{
if (_form != null && _form.IsDisposed == false)
{
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()
{
_form = new SplashForm();
_form.Click += new EventHandler(_form_Click);
Application.Run(_form);
}
private static void _form_Click(object sender, EventArgs e)
{
CloseSplash();
}
private static void _mainForm_Activated(object sender, EventArgs e)
{
CloseSplash();
}
|
前のようにApplication.Idleイベントでスプラッシュウィンドウを閉じると、スプラッシュウィンドウは表示されたと思うと、あっという間に閉じてしまいますので、別のタイミングで閉じるようにします。ここでは、メインフォームのActivatedイベントで閉じています。(さらにSplashFormのClickイベントでもSplashFormを閉じるようにしています。)
SplashFormを表示するには、前と同様、SplashForm.ShowSplashメソッドを呼び出しますが、このときメインフォームのインスタンスを指定できます。メインフォームを指定すると、そのActivatedイベントハンドラで自動的にSplashFormが閉じられます。メインフォームを指定しなかった時は、適当な位置(メインウィンドウのActivatedイベントハンドラなど)でSplashForm.CloseSplashメソッドを呼び出してSplashFormを閉じてください。
上のコードを使ってスプラッシュウィンドウを表示させるコードを以下に示します。ここでも、エントリポイントであるMainメソッドでメインフォームForm1を表示させる前にスプラッシュウィンドウを表示させています。
1
2
3
4
5
6
7
8
9
10
| | <STAThread()> _
Shared Sub Main()
Dim mainForm As New Form1
SplashForm.ShowSplash(mainForm)
Application.Run(mainForm)
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
| | [STAThread]
static void Main()
{
Form1 mainForm = new Form1();
SplashForm.ShowSplash(mainForm);
Application.Run(mainForm);
}
|
(補足:
スプラッシュウィンドウは、アプリケーションが起動してから準備ができるまでに長い時間がかかる時に、ユーザーを不安にさせたり、待たせたりしないために表示するというのが主要な使用目的でしょう。そのため、起動にそれほど時間のかからないアプリではスプラッシュウィンドウは必要ありません。ユーザーの立場からすると、無駄なスプラッシュウィンドウにはうんざりさせられます。)
.NET質問箱 †
DataGridセル内の文字列を折り返して表示するには? †
質問:
WindowsアプリケーションでDataGridコントロールのセル内の文字列を折り返して表示するにはどのようにすればよいのでしょうか?
回答:
DataGridColumnStyleクラスの派生クラスを作成し、そのPaintメソッドをオーバーライドして、文字列を折り返して描画するようにします。
以下にその例を示します。ここでは、DataGridTextBoxColumnクラスを継承し、新しいクラスDataGridTextBoxColumnExを作ります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| | Public Class DataGridTextBoxColumnEx
Inherits DataGridTextBoxColumn
Private _margin As New Point(0, 2)
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
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| | public class DataGridTextBoxColumnEx : DataGridTextBoxColumn
{
Point _margin = new Point(0, 2);
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オブジェクトを設定します。詳しい方法は、次のページをご覧ください。
この記事の基になった掲示板のスレッド
フォームのコレクションを作成するには? †
質問:
Visual Basic 6.0 にはFormsコレクションがありましたが、.NETにフォームのコレクションはありますか?
回答:
VB6のFormsコレクションに相当するものは.NET Frameworkでは用意されていませんし、フォームオブジェクト専用のコレクションもありません。
「マイクロソフト サポート技術情報 - 308537」の「[HOW TO] Visual Basic .NET で Forms コレクションを作成する方法」では、カスタムのFormsコレクションを作成する方法が紹介されています。
しかしここで紹介されているクラスでは、インデックスを指定してオブジェクトを取得できず、不便です。
そこで、インデックスを指定してオブジェクトを取得できるようにし、さらにInsert、IndexOf、Containsメソッドを追加したコレクションクラスを以下に紹介します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| | 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
Public Function Contains(ByVal frm As Form) As Boolean
Return List.Contains(frm)
End Function
End Class
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
| | 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);
}
public bool Contains(Form frm)
{
return List.Contains(frm);
}
}
|
このコレクションの使い方は、例えば次のような感じです。
1
2
3
4
5
6
7
8
9
10
| |
Dim f As New Form1
_forms.Add(f)
_forms(0).ShowDialog()
|
1
2
3
4
5
6
7
8
9
10
| |
Form1 f = new Form1();
_forms.Add(f);
_forms[0].ShowDialog()
|
なお、このフォームコレクションには、開かれたフォームが自動的に追加されたり、閉じられたフォームが自動的に削除される機能がありません。これらの処理は自分で行う必要があります。
この記事の基になった掲示板のスレッド
コメント †