WinForms で起動時のカスタマイズする
◆ 全部 Form 閉じたら終了がちょっとめんどうかも
WPF の起動時をカスタマイズする
この記事を書くときのメモに WinForms の情報もあったので書いておきます
Application.Run にフォームのインスタンスを渡す形になってます
WPF のときみたいに自由に Show できるようにします
AppContext というクラスを継承して実行時の処理を書きます
Application.Run には Form のインスタンスの代わりに AppContext のインスタンスを渡します
MainForm プロパティに Form を設定すれば その Form が閉じられるとアプリケーションが終了します
WPF みたいに全部閉じるまで終了したくないなら一手間必要です
1つ目の方法は Idle 状態になったら Form の数を数えて 0 なら終了させる方法
2つ目は Form のベースクラスを作って それぞれの Closed イベントで Form が自分で最後なら終了する方法
Idle と違ってこまめに終了していいかチェックしなくて済みます
0 じゃなく 1 なのはこのときはまだ自分の分が残っているからです
どちらの場合も MainForm を設定しないように気をつけないと MainForm があればそれが閉じると終了してしまいます
なので WPF にあった app.DispatcherUnhandledException というものはありません
Application.Run の前に Application.ThreadException イベントにハンドラをセットしておきます
Application.ThreadException は UI スレッドのハンドルされなかった例外でイベントが起きます
WPF の DispatcherUnhandledException に代わるものです
全部の例外を受け取る AppDomain.CurrentDomain.UnhandledException は WinForms でも使えます
Application.ThreadException の場合は DispatcherUnhandledException で Handled を true にしたのと同じでこっちで受け取ると AppDomain の方にはイベントが行かなくなります
すべて AppDomain で受け取るなど ThreadException で Catch したくないときは
何も設定しないときは Automatic になっていて App.config の設定が反映されます
特に指定ないなら CatchException と同じで ThreadException ハンドラが呼び出されます
WPF と同じボタンで試すと
結果です
ThreadException で受け取ると AppDomain の方には行かない違いだけであとは一緒です
この記事を書くときのメモに WinForms の情報もあったので書いておきます
メインフォーム開くまで
デフォルトではメインフォームを開くまではこんな風になってますstatic void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
Application.Run にフォームのインスタンスを渡す形になってます
WPF のときみたいに自由に Show できるようにします
static class Program
{
/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AppContext());
}
}
public class AppContext : ApplicationContext
{
public AppContext()
{
var startup_form = new Form1();
startup_form.Show();
this.MainForm = startup_form;
}
}
{
/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AppContext());
}
}
public class AppContext : ApplicationContext
{
public AppContext()
{
var startup_form = new Form1();
startup_form.Show();
this.MainForm = startup_form;
}
}
AppContext というクラスを継承して実行時の処理を書きます
Application.Run には Form のインスタンスの代わりに AppContext のインスタンスを渡します
MainForm プロパティに Form を設定すれば その Form が閉じられるとアプリケーションが終了します
WPF みたいに全部閉じるまで終了したくないなら一手間必要です
1つ目の方法は Idle 状態になったら Form の数を数えて 0 なら終了させる方法
Application.Idle += (sender, e) =>
{
if (Application.OpenForms.Count == 0)
{
Application.Exit();
}
};
{
if (Application.OpenForms.Count == 0)
{
Application.Exit();
}
};
2つ目は Form のベースクラスを作って それぞれの Closed イベントで Form が自分で最後なら終了する方法
public class FormBase : Form
{
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
if (Application.OpenForms.Count == 1)
{
Application.Exit();
}
}
}
こんなクラスを作って すべての Form がこれを継承すればおっけいです{
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
if (Application.OpenForms.Count == 1)
{
Application.Exit();
}
}
}
Idle と違ってこまめに終了していいかチェックしなくて済みます
0 じゃなく 1 なのはこのときはまだ自分の分が残っているからです
どちらの場合も MainForm を設定しないように気をつけないと MainForm があればそれが閉じると終了してしまいます
多重起動禁止
これは WPF のと同じ方法で大丈夫ですグローバルエラーのハンドル
WinForms だとエントリポイントのクラスが static なのでインスタンスを作りませんなので WPF にあった app.DispatcherUnhandledException というものはありません
Application.Run の前に Application.ThreadException イベントにハンドラをセットしておきます
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += (o, e) =>
{
MessageBox.Show("Catch app unhandled exception.\n" + e.Exception.Message);
};
AppDomain.CurrentDomain.UnhandledException += (o, e) =>
{
var unhandled = (Exception)e.ExceptionObject;
MessageBox.Show("Catch appdomain unhandled exception.\n" + unhandled.Message);
};
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
Application.ThreadException += (o, e) =>
{
MessageBox.Show("Catch app unhandled exception.\n" + e.Exception.Message);
};
AppDomain.CurrentDomain.UnhandledException += (o, e) =>
{
var unhandled = (Exception)e.ExceptionObject;
MessageBox.Show("Catch appdomain unhandled exception.\n" + unhandled.Message);
};
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
Application.ThreadException は UI スレッドのハンドルされなかった例外でイベントが起きます
WPF の DispatcherUnhandledException に代わるものです
全部の例外を受け取る AppDomain.CurrentDomain.UnhandledException は WinForms でも使えます
Application.ThreadException の場合は DispatcherUnhandledException で Handled を true にしたのと同じでこっちで受け取ると AppDomain の方にはイベントが行かなくなります
SetUnhandledExceptionMode
ThreadException ハンドラに送られるかは Application.SetUnhandledExceptionMode で変更できますApplication.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
を設定すると ThreadException に送られて ThreadException で CatchException されますすべて AppDomain で受け取るなど ThreadException で Catch したくないときは
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
を設定すれば ThreadException ハンドラは呼び出されなくなります何も設定しないときは Automatic になっていて App.config の設定が反映されます
特に指定ないなら CatchException と同じで ThreadException ハンドラが呼び出されます
WPF と同じボタンで試すと
private void button1_Click(object sender, EventArgs e)
{
throw new Exception("普通に例外");
}
private void button2_Click(object sender, EventArgs e)
{
var thread = new Thread(new ThreadStart(() => {
throw new Exception("別スレ例外");
}));
thread.Start();
}
private void button3_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
throw new Exception("await task例外");
});
}
private void button4_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
throw new Exception("task例外");
});
}
{
throw new Exception("普通に例外");
}
private void button2_Click(object sender, EventArgs e)
{
var thread = new Thread(new ThreadStart(() => {
throw new Exception("別スレ例外");
}));
thread.Start();
}
private void button3_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
throw new Exception("await task例外");
});
}
private void button4_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
throw new Exception("task例外");
});
}
結果です
ThreadException | AppDomain | |
button1 | ○ | × |
button2 | × | ○ |
button3 | ○ | × |
button4 | × | × |
ThreadException で受け取ると AppDomain の方には行かない違いだけであとは一緒です