Xamarin.Macにおけるシートいろいろ（ファイルダイアログ編）

##Problem
ファイルダイアログもシートで出せます。
シートで出すことにより，どのアプリケーションのダイアログかが明確になるので，できれば使っていきたいところです。
シートを乱発するとどうなるか，そしてこれを防ぐ方法についても本稿でご紹介します。

##Prerequisite
Xamarin.Mac プロジェクトを作成し，MainWindow.xibをXCodeで開いて，このような感じにしてあります。
![XCode上のUIデザイン](https://qiita-image-store.s3.amazonaws.com/0/34563/71dc9108-1c22-a6d6-b6d6-1716bd12e068.png "スクリーンショット 2014-12-26 13.44.24.png")


##NSOpenPanel
いわゆるオープンファイルダイアログです。
オプションを指定することでフォルダ選択ダイアログに仕立てることも可能です。

```csharp:MainWindowController.cs
public override void WindowDidLoad()
{
	base.WindowDidLoad();

	OpenButton.Activated += (sender, _) => ShowOpenPanel((NSObject)sender);
}

[Export("openDocument:")]
void ShowOpenPanel(NSObject sender)
{
	using(var panel = new NSOpenPanel()){
		// 複数選択の可否
		panel.AllowsMultipleSelection = false;
		// ファイル選択の可否
		panel.CanChooseFiles = true;
		// ディレクトリ選択の可否
		panel.CanChooseDirectories = true;
		// 受け入れるファイルタイプ
		// 拡張子のみ，ピリオドなし，複数指定可能
		panel.AllowedFileTypes = new[]{ "txt" };
		// 開くボタンのテキスト，指定しない場合はOS標準
		panel.Prompt = "開く";
		// モーダルウィンドウのタイトル
		panel.Title = "ウィンドウタイトル";
		
		// モーダル表示
		var ret = panel.RunModal();
		
		// キャンセルが押された場合 0 が返る
		if (ret > 0)
			PathField.StringValue = panel.Url.Path;
	}
}
```
![モーダルとして表示](https://qiita-image-store.s3.amazonaws.com/0/34563/6aed05ef-49e1-dd0b-76ac-fb54dab139c6.png "スクリーンショット 2014-12-26 13.50.08.png")

先頭の`[Export]`によりセレクタを宣言することで，メニューバーの「開く」に関連付けています。
`RunModal` しているのでモーダルダイアログとして表示されます。
これをシートにする場合は以下のようになります。

```csharp:MainWindowController.cs
[Export("openDocument:")]
void ShowOpenPanel(NSObject sender)
{
	var panel = new NSOpenPanel{ CanChooseFiles = true, AllowedFileTypes = new[]{ "cs" }, ReleasedWhenClosed = true };

	panel.BeginSheet(Window, ret =>
		{
			panel.EndSheet(Window);
			if (ret > 0)
				PathField.StringValue = panel.Url.Path;
		});
}
```

`BeginSheet`と`EndSheet`は対応させなくてはなりません。また，`using`句でくくると破棄タイミングがよろしくないので，代わりに`ReleasedWhenClosed`を`true`にしておきます。
![ShowAsSheet](https://qiita-image-store.s3.amazonaws.com/0/34563/d5af6d57-bc18-255a-a9c8-505ba21c699b.png "スクリーンショット 2014-12-26 13.58.06.png")

##NSSavePanel
保存先の選択に使います。NSOpenPanelはこれを継承しています。こちらはシートで表示する場合だけ。

```csharp:MainWindowController.cs
[Export("saveDocumentAs:")]
void ShowSavePanel(NSObject sender)
{
	var panel = new NSSavePanel();
	// 許容する拡張子
	panel.AllowedFileTypes = new[]{ "cs" };
	// フォルダの作成ボタンを表示
	panel.CanCreateDirectories = true;
	panel.ReleasedWhenClosed = true;
	panel.Message = "SavePanelを通して伝えたいこと";

	panel.BeginSheet(Window, ret =>
		{
			panel.EndSheet(Window);
			if (ret > 0)
				PathField.StringValue = panel.Url.Path;
		});
}
```
![NSSavePanel](https://qiita-image-store.s3.amazonaws.com/0/34563/6185bf0a-af3b-1dbc-7cf0-120204ed21d2.png "スクリーンショット 2014-12-26 14.14.59.png")

必要であれば`AccessoryView`で何でも追加可能。

```csharp:MainWindowController.cs
var img = new NSImageView(new System.Drawing.RectangleF(0, 0, 64, 64))
	{ Image = NSImage.ImageNamed(NSImageName.QuickLookTemplate), ImageScaling = NSImageScale.ProportionallyUpOrDown };
panel.AccessoryView = img;
```

![accview](https://qiita-image-store.s3.amazonaws.com/0/34563/748fd037-d070-2836-d41b-cd2a2cdb8abb.png "スクリーンショット 2014-12-26 14.23.19.png")

##Conclusion
Xamarin.Macにおけるシートいろいろはこれで完結です。
本当はシート乱発時の危険な動作についてまとめたいですが，10.9以降のAPIを使う場合は起きないようなので，大丈夫だと信じて割愛します。
ここまでのシートいろいろまとめはUnified APIでもほぼ同じコードで動作します。