Xamarin.Macにおけるシートいろいろ(NSWindow編)

##Problem
[NSAlert編](http://qiita.com/ailen0ada/items/1be70bc50fe8b3b62c55 "NSAlert")に続き，今回はNSWindowをシートにします。
NSWindowの場合はControllerを使って多彩な操作を行うことができます。

##with NSWindow
###Create
ログインウィンドウ的なものを作ってみます。
Xamarin Studioで適当なXamarin.Macプロジェクトを作成します。`MainWindow.xib`を開いてボタンとラベルを一つずつ配置し，`MainWindowController.h`にプロパティを作成しておきます。

![Screen Shot 2014-08-21 at 15.52.03.png](https://qiita-image-store.s3.amazonaws.com/0/34563/7c11401c-5e28-cc9f-5857-874d106c455c.png "Screen Shot 2014-08-21 at 15.52.03.png")


Xamarin Studioに戻り，プロジェクトに`Cocoa Window with Controller`を追加します。

![Screen Shot 2014-08-21 at 15.52.56.png](https://qiita-image-store.s3.amazonaws.com/0/34563/6c43b2f1-8003-3ba7-d5d0-28b50181e3e9.png "Screen Shot 2014-08-21 at 15.52.56.png")

`LoginWindow.xib`を開いて適当にコントロールを配置し，プロパティも作成しておきます。このとき，Windowの`Visible At Launch`チェックボックスをオフにしておきます。
![Screen Shot 2014-08-21 at 16.01.21.png](https://qiita-image-store.s3.amazonaws.com/0/34563/f75eabff-12ba-9c89-737b-c6c68be7c2f4.png "Screen Shot 2014-08-21 at 16.01.21.png")


![Screen Shot 2014-08-21 at 15.56.16.png](https://qiita-image-store.s3.amazonaws.com/0/34563/7eb1772a-1737-ce3d-0a14-d0271fc97a49.png "Screen Shot 2014-08-21 at 15.56.16.png")

`LoginWindowController.cs`に，入力された情報へアクセスするためのプロパティを作成しておきます。さらに`WindowDidLoad`をオーバーライドし，ログインボタンのクリックイベントでこれらのプロパティに値をセットします。

```csharp:LoginWindowController.cs
/// Constructors, accessorなどの下
public string UserName{ get; set; }

public string Password{ get; set; }

public override void WindowDidLoad()
{
    base.WindowDidLoad();

    LoginButton.Activated += (sender, e) =>
    {
        UserName = UserNameBox.StringValue;
        Password = PasswordBox.StringValue;
    };
}
```

まだ閉じる処理を書いていないので，シートを閉じることはできません。それはあとにして，まずは開く処理を書きます。
`MainWindowController.cs`に戻り，先程作ったボタンのクリックイベントを記述します。こんな感じです。

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

    ShowButton.Activated += (sender, e) =>
    {
        var loginWindow = new LoginWindowController();
        NSApplication.SharedApplication.BeginSheet(loginWindow.Window, Window, () =>
        {
            UsernameLabel.StringValue = string.Format("{0} ({1})", loginWindow.UserName, loginWindow.Password);
        });

        Debug.WriteLine("非同期なのでこれが先に処理されます。");
    };
}
```

肝になるのは`NSApplication.SharedApplication.BeginSheet(NSWindow, NSWindow, Action)`です。第1引数に表示したいウィンドウ，第2引数にシートを結びつけるウィンドウ，第3引数にシート表示が終了した時のコールバックを記述します。

このままビルドして，ボタンをクリックするとシートが表示されるのがわかります。またデバッグ出力が先に行われるのも確認できます。

![Screen Shot 2014-08-21 at 17.37.36.png](https://qiita-image-store.s3.amazonaws.com/0/34563/168ae38d-ebb8-f5ae-4c02-1f8833eb32be.png "Screen Shot 2014-08-21 at 17.37.36.png")

では`LoginWindowController.cs`に戻り，閉じる処理を追加します。こうです。

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

    LoginButton.Activated += (sender, e) =>
    {
        UserName = UserNameBox.StringValue;
        Password = PasswordBox.StringValue;

        NSApplication.SharedApplication.EndSheet(Window);
        Window.OrderOut(sender as NSObject);
        Close();
    };
}
```

`NSApplication.SharedApplication`の`BeginSheet`を呼んだら，`EndSheet`も必ず呼んでください。以降のシートが正しく表示できなくなります（強制でダイアログ化します）。シート終了後，ウィンドウを非表示に切り替え(`OrderOut`)，最後に基底クラスの`Close`を呼びます。
ではビルド，実行してログインボタンが動作することを確認してください。
![Screen Shot 2014-08-21 at 17.43.50.png](https://qiita-image-store.s3.amazonaws.com/0/34563/032d50cf-d6ff-c5b1-9149-633a2ccde418.png "Screen Shot 2014-08-21 at 17.43.50.png")

###Sheet on sheet
![Screen Shot 2014-08-21 at 17.57.06.png](https://qiita-image-store.s3.amazonaws.com/0/34563/e4a1d578-2ad0-b407-901c-030fc658daee.png "Screen Shot 2014-08-21 at 17.57.06.png")
こんなことも可能です。外部から閉じる場合は，`ProgressWindowController.cs`に`CloseWindow`を用意して呼べばいいです。こんな感じ。

```csharp:ProgressWindowController.cs
public void CloseWindow(object sender)
{
    NSApplication.SharedApplication.EndSheet(Window);
    Window.OrderOut(sender as NSObject);
    Close();
}
```

さらに`NSAlert`かますとか。
![Screen Shot 2014-08-21 at 18.01.32.png](https://qiita-image-store.s3.amazonaws.com/0/34563/f4b1644b-b3e1-602c-a23f-61fce1897dda.png "Screen Shot 2014-08-21 at 18.01.32.png")
ただあんまり何枚も調子に乗ると大変なことに。シートは2枚までにしましょう。
![Screen Shot 2014-08-21 at 18.05.54.png](https://qiita-image-store.s3.amazonaws.com/0/34563/f36ce429-6a4e-18b5-f933-e7c757c42af5.png "Screen Shot 2014-08-21 at 18.05.54.png")
ただしシートとして出現させた`NSWindow`の配下にシートを結びつけることは特に制限されません。どんどん出せます。
![Screen Shot 2014-08-21 at 18.08.26.png](https://qiita-image-store.s3.amazonaws.com/0/34563/3f1fde61-0982-a49e-1879-786959bf7235.png "Screen Shot 2014-08-21 at 18.08.26.png")

##Conclusion
シートはどのアプリケーションに結びつくウィンドウかがわかりやすく，見た目にもすっきりします。積極的に使いましょう。
次稿ではOpen/SaveDialogあたりにさらっと触れます。