[VS-Mac]VisualStudio for Mac (Cocoa)でファイルダイアログ表示

　VisualStudio for Macをインストールしたものの、Cocoa GUアプリ作成にはXcodeとInterfaceBuilder及びAPIの知識が必要で、自分のようにWindowsから移行してXcodeとSwiftのGUアプリに挫折した者にとっては使いこなせない強敵です。
　幸いに、Xamarinの[Macに関するGuidesのページ](https://developer.xamarin.com/guides/mac/getting_started/)でサンプルアプリのチュートリアルと説明の記事があり、慣れないAPIと英語ではあるものの、なんとか自分でもできそうな気がしてしまいます。
　ところが、自分で新たなツールを作ろうとすると十分に理解できていないことから、何度も同じことを調べ直さなければならず大変でした。  
　そこで、小物ツール作成時に役立つように、これらの記事とサンプルコードを元に、個人的に利用する可能性が高い機能を私的Tutorialとして記事にしておくことにします。

　第１回目は、InterfaceBuilderの使い方とコード実装を、ツールバーとファイルダイアログの表示を例にした備忘録です。

## 利用するAPI:
* [NSToolBar](https://developer.apple.com/reference/appkit/nstoolbar)
* [NSToolBarItem](https://developer.apple.com/reference/appkit/nstoolbaritem)
* [NSTextField](https://developer.apple.com/reference/appkit/nstextfield)
* [NSOpenPanel](https://developer.apple.com/reference/appkit/nsopenpanel)
* [NSAlert](https://developer.apple.com/reference/appkit/nsalert)
* [contentViewController](https://developer.apple.com/reference/appkit/nswindow/1419615-contentviewcontroller)

## 概要
* ツールボタンのクリックでファイルを開くダイアログを表示。
* Openで選択したファイル名をViewに配置したラベルに表示する。
* ツールバーのParentであるWindowsControllerからViewControllerのViewに存在するコントロールを操作する例。
* おまけとして、アプリを直接終了できるツールボタン。

## ソリューションの新規作成
* VisualStudioで「NewProject」
* 「Mac」「Cocoa App」を選択
* AppName 「MyTutorial_OpenFile」
* 作成

## InterfaceBuilderでの操作
Main.storyboardをダブルクリックして、Xcodeを起動
### WindowsControllerのクラス名を設定
1. ツールボタンのアクションを追加するためのWindowsController.hをXcodeで表示させるために必要。
2. Window Controller を選択、Identify Inspectorを表示してClassを「WindowController」と入力。![OpenFile01.png](https://qiita-image-store.s3.amazonaws.com/0/182396/c9118b5d-a23c-49cb-0951-bead6776b885.png)
3. XcodeメニューからSaveして、VisualStudioに戻る。
4. VisualStudioに「WindowController.cs」が追加された。![OpenFile02.png](https://qiita-image-store.s3.amazonaws.com/0/182396/797b7b34-834e-b294-a9eb-67eb21f7dee2.png)
5. Main.storyboardをダブルクリックして、Xcodeを再度表示してコードに「WindowController.h」が追加されていることを確認。![OpenFile03.png](https://qiita-image-store.s3.amazonaws.com/0/182396/2a39fd95-92a5-5260-5f4e-17b8dbb95d8d.png)

* VisualStudioに戻る時に、Xcodeとの同期エラーのダイアログが出現することがまれにあるが、特に支障はないようだ。

### コントロールの追加
1. WindowsControllerにToolbarを追加し、これをクリックしてツールバーの編集モードにする。
2. OpenFile用のImageToolbarItemを追加して、ImageName（アイコン）とLabelを「File」に変更。
3. 同様にQuit用ImageToolbarItemを追加して、Labelを「Quit」に変更。
4. AllowedToolbarItemsに追加したItemを、DefaultToolbarItemsにドラッグ。不要のツールバーをツールバー外にドロップして削除。![OpenFile04.png](https://qiita-image-store.s3.amazonaws.com/0/182396/22d6fcd3-e390-788a-7b24-aebd9fc5b06d.png)

### ツールボタンActionの追加
1. XcodeでAssistantEditorを表示（右上のダブルサークル）
2. AssistantEditorに「WindowController.cs」を表示![OpenFile05.png](https://qiita-image-store.s3.amazonaws.com/0/182396/ed56d041-11ff-ed37-3401-ae2d696318ab.png)

3. ツールバーのAllowedToolbarItemから「File」Itemを選択して、__Ctrlキーを押したまま__「WindowController.h」の `@end` の前にドラッグ
4. Connectionを「Actionに変更」。Nameに「FileButton_Click」と入力して「connect」ボタンをクリック。![OpenFile06.png](https://qiita-image-store.s3.amazonaws.com/0/182396/ca379e0f-26cc-c15f-18e5-add4ec295afb.png)

5. 同様に「Quit」ItemもActionとしてドラッグし、Nameを「QuitButton_Click」と入力。
6. ツールバーの「done」ボタンをクリックして編集モードから抜ける。
7. Save後のWindowController.designer.csにActionの関数宣言が追加されている。![OpenFile08.png](https://qiita-image-store.s3.amazonaws.com/0/182396/2934109f-ce6e-8dca-964c-21e5a4452c47.png)

### ViewControllerにラベルを追加
1. ViewControllerを表示
2. LabelをViewにドラッグしてサイズを適当に変更
3. AssistantEditorにViewController.hを表示
4. 追加したLabelを選択肢、Ctrlキーを押したまま `@end` の前にドラッグ
5. Connectionは「Outlet」のままで変更せず、Nameを「Label1」と入力してConnect。![OpenFile07.png](https://qiita-image-store.s3.amazonaws.com/0/182396/56084df6-f725-f1c9-f74c-a22e92ca81cb.png)

6. XcodeをSaveしてVisualStudioに戻る。
7. ViewController.designer.csにLabel1がNSTextFieldの変数として宣言されている。

## VisualStudioでコードの追加と編集
### ViewController.csの編集
1. Windowタイトルとラベルの表示テキストを変更する関数の追加

```
    //chnage title  UserInterface - Windows - Setting a Window's Title
    public override void ViewWillAppear()
    {
	      base.ViewWillAppear();
	      this.View.Window.Title = "My Tutorial NSOpenPanel";
          ChangeLabeltext("");
    }
    public void ChangeLabeltext(string str)
    {
          this.Label1.StringValue = str;
    }
```

### WindowController.csの編集
1. ViewControllerを参照するController変数を作成


```
    public ViewController Controller
    {
        get { return ContentViewController as ViewController; }
    }
```
　
2. FileButton_Click アクションプロシージャー
  
```
	partial void FileButton_Click(NSObject sender)
    {
			  var dlg = NSOpenPanel.OpenPanel;
			  dlg.CanChooseFiles = true;
			  dlg.CanChooseDirectories = false;
              dlg.AllowsMultipleSelection = false;
              dlg.DirectoryUrl = NSUrl.FromString(Environment.SpecialFolder.MyPictures.ToString());
			  dlg.AllowedFileTypes = new string[] { "png", "jpeg", "jpg" };

		    if ( dlg.RunModal() == 1)
			  {
				    if (dlg.Urls[0] != null)
				    {
					    Controller.ChangeLabeltext(dlg.Urls[0].Path);
				    }
			  }
            else
            {
				  var alert = new NSAlert()
			      {
					    AlertStyle = NSAlertStyle.Informational,
					    InformativeText = "Cancel Button Clicked",
					    MessageText = "Alert",
				  };
				  alert.RunModal();
			}
    }
```
  
3. QuitButton_Click アクションプロシージャー
  
```
        partial void QuitButton_Click(NSObject sender)
        {
			      NSApplication.SharedApplication.Terminate(Self);
        }
```
  

## コード全体像
### ViewController.cs
```
using System;

using AppKit;
using Foundation;

namespace MyTutorial_OpenFile
{
    public partial class ViewController : NSViewController
    {
		public ViewController(IntPtr handle) : base(handle)
        {
        }

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

            // Do any additional setup after loading the view.
        }

		//###### Quote from Xamarin page ##########
		//https://developer.xamarin.com/guides/mac/user-interface/working-with-windows/
		//chnage title  UserInterface - Windows - Setting a Window's Title
		public override void ViewWillAppear()
		{
			base.ViewWillAppear();
			this.View.Window.Title = "My Tutorial NSOpenPanel";
            ChangeLabeltext("");
		}
        //#########################################
		public void ChangeLabeltext(string str)
		{
            this.Label1.StringValue = str;
		}

		public override NSObject RepresentedObject
        {
            get
            {
                return base.RepresentedObject;
            }
            set
            {
                base.RepresentedObject = value;
                // Update the view, if already loaded.
            }
        }
    }
}
```

### WindowController.cs

```
// This file has been autogenerated from a class added in the UI designer.
using System;
using Foundation;
using AppKit;
namespace MyTutorial_OpenFile
{
    public partial class WindowController : NSWindowController
    {
		//###### Quote from Xamarin page ##########
		//https://developer.xamarin.com/guides/mac/application_fundamentals/copy-paste/
		public ViewController Controller
        {
            get { return ContentViewController as ViewController; }
        }
        public WindowController(IntPtr handle) : base(handle)
        {
        }
   		#region ToolButton Action
		partial void FileButton_Click(NSObject sender)
        {
			//###### Quote from Xamarin page ##########
			//https://developer.xamarin.com/guides/mac/user-interface/working-with-dialogs/#The_Open_Dialog
			var dlg = NSOpenPanel.OpenPanel;
			dlg.CanChooseFiles = true;
			dlg.CanChooseDirectories = false;
            dlg.AllowsMultipleSelection = false;
            dlg.DirectoryUrl = NSUrl.FromString(Environment.SpecialFolder.MyPictures.ToString());
			dlg.AllowedFileTypes = new string[] { "png", "jpeg", "jpg" };

			if ( dlg.RunModal() == 1)
			{
				if (dlg.Urls[0] != null)
				{
					Controller.ChangeLabeltext(dlg.Urls[0].Path);
				}
			}
            else
            {
				//###### Quote from Xamarin page ##########
				//https://developer.xamarin.com/guides/mac/user-interface/working-with-alerts/
				var alert = new NSAlert()
				{
					AlertStyle = NSAlertStyle.Informational,
					InformativeText = "Cancel Button Clicked",
					MessageText = "Alert",
				};
				alert.RunModal();
			}
        }
        partial void QuitButton_Click(NSObject sender)
        {
			//######## Refer to StackOverFlow ##########
			//http://stackoverflow.com/questions/1748895/how-to-properly-quit-application-call-exit0
			NSApplication.SharedApplication.Terminate(Self);
        }
        #endregion
    }
}
```

## Reference:
1. Monoのライセンス [Mono Licensing](https://www.xamarin.com/licensing)
2. Xamarine - Developers - Guides - Mac - Getting Started - [Hello,Mac](https://developer.xamarin.com/guides/mac/getting_started/hello%2C_mac/)
3. Xamarine - Developers - Guides - Mac - Application Fundamentals - [Working with .xib Files](https://developer.xamarin.com/guides/mac/application_fundamentals/working-with-xibs/)
　　
4. Xamarine - Developers - Guides - Mac - Application Fundamentals - [Working with Copy and Paste](https://developer.xamarin.com/guides/mac/application_fundamentals/copy-paste/)
  *WindowControllerからViewContorollerの操作（ContentViewController）*
5. Xamarine - Developers - Guides - Mac - User Interface - [Toolbars](https://developer.xamarin.com/guides/mac/user-interface/working-with-toolbars/)
6. Xamarine - Developers - Guides - Mac - User Interface - [Dialogs](https://developer.xamarin.com/guides/mac/user-interface/working-with-dialogs/#The_Open_Dialog)  
　　*file dialogの引用修正*
7. Xamarine - Developers - Guides - Mac - User Interface - [Windows]　　(https://developer.xamarin.com/guides/mac/user-interface/working-with-windows/)     　　*Window Title変更の引用修正*
8. Xamarine - Developers - Guides - Mac - User Interface - [Alerts](https://developer.xamarin.com/guides/mac/user-interface/working-with-alerts/)
9. viva Cocoa [Windowsなどのほかのプラットフォームから転向されてきた方へ](http://vivacocoa.jp/objective-c3e/chapter1c.html)
10. stackoverflow	[How to properly quit application, call exit(0)?](http://stackoverflow.com/questions/1748895/how-to-properly-quit-application-call-exit0)  
  　　*Swiftのコードを参考にした。*

2-8で引用した部分の著作権はXamarinに帰属します。


## License:
Copyright (c) 2017 grayhead0603
Released under the [MIT license](http://opensource.org/licenses/mit-license.php)