2009年5月10日日曜日

MessageBoxのアイコン

MessageBoxのアイコンの指定を調べるために、こんなサンプルを作ってみました。
(ヘルプは、今回は使わないから調べませんでした。)
で、WindowsVistaHomePremiumで動かしてみてわかったこと。

・MessageBoxで×ボタンが表示される場合は、以下のボタン表示のときで、×ボタンが押されたら、
一番最後のボタンが押されたものと同じ。
OK
OKCancel
YesNoCancel

・×ボタンが表示されているときに限りEscキーで戻ることができ、その場合、戻り値が×ボタンを押したときと同じ。

・オプション指定が、DefaultDesktopOnlyやServiceNotificationの場合、MessageBox終了後、呼び出し元のウィンドウにはフォーカスが戻らない。

・アイコン指定は、Error,Hand,Stopは同じ値が振られているため同じ動きをしており、ExclamationとWarningも同じ、InformationとAsteriskも同じ。そもそもMessageBoxIcon.Error.ToString()や、MessageBoxIcon.Stop.ToString()をやっても"Hand"が返ってくる。(MessageBoxIcon.Exclamation.ToString()は"Warning",MessageBox.Information.ToString()は"Asterisk")

・(使うことはないが)オプション指定が、RtlReadingの場合、以下のように表示がわけがわからない。
 1.タイトルは右寄せ、表示文字はそのままの順(逆順に表示されることはない。)
 2.ウィンドウ内のアイコンは、(通常の左ではなく)右に表示される。
 3.ウィンドウ内の文字は、左寄せで表示される。表示文字はそのままの順。
 4.ボタンは、順序が逆転し()の変化が微妙でYesNoCancelを選んだ場合は以下のようになる。
   4-1:通常時:はい(Y),いいえ(N),キャンセル
   4.2:RtlReading選択時:キャンセル,(Nいいえ(,(Yはい(
 5.×ボタンが左に来ている。
ServiceNotificationを選んだ場合、


RtlReadingを選んだ場合、


なおサンプルに使ったデザインとプログラムは、

デザインはこんな感じ。



プログラムはこんな感じ。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();


foreach (MessageBoxButtons b in Enum.GetValues(typeof(MessageBoxButtons))) listBox1.Items.Add(b);
foreach (string s in Enum.GetNames(typeof(MessageBoxIcon))) listBox2.Items.Add(s);
foreach (MessageBoxOptions o in Enum.GetValues(typeof(MessageBoxOptions))) listBox3.Items.Add(o);

listBox1.SelectedIndex = 0;
listBox2.SelectedIndex = 0;
listBox3.SelectedIndex = 0;
}

private void button1_Click(object sender, EventArgs e)
{


MessageBoxButtons b = ((MessageBoxButtons)listBox1.SelectedItem);
MessageBoxIcon i = ((MessageBoxIcon)new EnumConverter(typeof(MessageBoxIcon)).ConvertFromString(listBox2.SelectedItem.ToString()));
MessageBoxOptions o = ((MessageBoxOptions)listBox3.SelectedItem);

label1.Text = MessageBox.Show(
b.ToString()+Environment.NewLine+i.ToString()+Environment.NewLine+o.ToString(),
"MessageBoxテスト",b,i,MessageBoxDefaultButton.Button1,o).ToString();
}
}
}

C#のbase.base

C#のbase.base....について調べてみました。
結局はできないみたい。
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
D d = new D();
C c = new C();
B b = c;
A a = c;

string s01 = a.f(); //="C"
string s02 = b.f(); //="C"
string s03 = c.f(); //="C"

string s04 = ((B)b).f(); //="C" キャストを明示してもダメ
string s05 = ((A)a).f(); //="C" 同上
string s06 = (c as B).f(); //="C" このキャストもダメ
string s07 = d.f(); //="C" base.f()はできるけどbase.base.f()は書けない

D1 d1 = new D1();
C1 c1 = new C1();
B1 b1 = c1;
a = c1;
string s11 = a.f(); //=A" この時点で期待外れ
string s12 = b1.f();//="B1" 以下はどうでもいいや。
string s13 = c1.f();//="C1"
string s14 = ((B1)a).f();//="B1"
string s15 = ((C1)a).f();//="C1"
string s16 = ((A)b1).f();//="A"
string s17 = d1.f(); //="C1"

C21 c21 = new C21();
C22 c22 = new C22();

string s21 = c21.f(); //="A2" 期待どおりだがbaseクラス作成時にはをこんな風には作らない...
string s22 = c22.f(); //="B2"

a = c;
b = c;
string s31 = a.g();//="C"拡張メソッド内ではbaseは使えない...
string s32 = b.g();//同上

//型変換エラーが発生するためコメントアウト
//A aa = ((A)Convert.ChangeType(c, Type.GetType("ConsoleApplication3.A")));

string[] strl = { "aa", "bb" };
}

}

//オーバーライド
class A { public virtual string f() { return "A"; } }
class B : A { public override string f() { return "B"; } }
class C : B { public override string f() { return "C"; } }
class D : C { public override string f() { return base.f(); } }

//ある意味オーバーロード?
class B1 : A { public new string f() { return "B1"; } }
class C1 : B1 { public new string f() { return "C1"; } }
class D1 : C1 { public new string f() { return base.f(); } }

//ベースの呼び出しを継承クラスから呼び出せるようにしたもの
//ただA2を作る時点でこんなつくりにすることは考えられないなあ
class A2
{
public virtual string f() { return A2f(); }
protected string A2f() { return "A2"; }
}
class B2 : A2 { public override string f() { return "B2"; } }
class C21 : B2 { public override string f() { return A2f(); } }
class C22 : B2 { public override string f() { return base.f(); } }

//C#3.0からの拡張メソッド g
internal static class ClassUtility { internal static string g(this A a) { return a.f(); } }

}

久しぶりの.netメモ form開発の取り決め

今回windowsformを使った開発がありました。
んで、そのときのメモ。
windowsアプリケーションでは、1画面ごと、Formを使って作るみたい。
一番最初に、
program.csのMain()からApplication.Run(new Form1())が呼ばれて、Form1の処理が始まる。

Mainで、
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler( f);
:

public static void f(object sender, System.Threading.ThreadExceptionEventArgs e)
{
}
で、asp.netのglobal.asaxのerrと同じようなことができる。
ただ、このイベントは、Thread中の例外を捕捉するものなので、Form1のコンストラクタ処理中に発生したの例外は(Run評価前でスレッド開始前なので)これでは取れないので、try/catchしないといけない。

次画面遷移については、親画面(Form1)で、子画面(Form2)をnew→form2.Ownerにthis(つまりform1自身)をセット→form2の引継ぎプロパティをセット→、form2.Show(),form1.Hide()を行って次画面遷移。ダイアログ表示は、form2.ShowDialog()で、戻り値によるダイアログ処理結果取得。

情報共有については、static変数(Application共通の場合のみ)
親画面(Form1)から子画面(Form2)への情報受け渡しは、
1.Owner.親画面のプロパティ
2.親画面でセットされた自画面のプロパティ
3.子画面のコンストラクタを呼ばれたときの引数
このうち1,2は、コンストラクタ生成→子画面のプロパティセット(Ownerプロパティ含む)→Showなので、子画面のコンストラクタやloadでは親画面のプロパティセット前のためプロパティアクセスは不可。
でも、いまのコーディングルール上は、Showをする場合は、2.の子画面側で、自分の画面のプロパティを参照することと、ShowDialogは親画面(Owner)のプロパティを参照することとなっている。

子画面→親画面への引渡し方法は、
1.子画面の処理中に行う親画面へのプロパティセット、
2.ShowDialog()の戻り値(DialogResult)

で、Formの削除、生成のタイミングは、
次画面へ遷移する場合、親画面側で、newすることとなっている。
戻る場合は、子画面側から親画面のShowを行い、自画面をdisposeするとなっている。(親画面側からしかdisposeが利かないような気がするが。。)