Xamarin.Mac のダイアログ
Xamarin.Mac アプリケーションで C# と .NET を使用している場合、Objective-C と Xcode で開発者が使用しているのと同じダイアログおよびモーダル ウィンドウにアクセスできます。 Xamarin.Mac は直接 Xcode と統合できるため、Xcode の Interface Builder を使用して、モーダル ウィンドウを作成および管理できます (または、必要に応じて、C# コードで直接作成することもできます)。
ユーザー操作に応答してダイアログが表示され、通常はユーザーがアクションを完了する方法が提供されます。 ダイアログを閉じる前に、ユーザーからの応答が必要です。
ウィンドウは、モードレス状態 (複数のドキュメントを一度に開くことができるテキスト エディターなど) またはモーダル (アプリケーションを続行する前に閉じる必要がある [エクスポート] ダイアログなど) で使用できます。
この記事では、Xamarin.Mac アプリケーションでのダイアログおよびモーダル ウィンドウの操作の基本について説明します。 この記事で使用する主要な概念と手法については、まず Hello Mac の記事、特に「Xcode と Interface Builder の概要」および「アウトレットとアクション」のセクションを参照することを強くお勧めします。
Xamarin.Mac Internals ドキュメントの「C# クラス/メソッドの Objective-C への公開」セクションも参照することをお勧めします。C# クラスを Objective-C オブジェクトと UI 要素に結び付けるために使われる Register
および Export
コマンドについて説明されています。
ダイアログの概要
ユーザー アクション (ファイルの保存など) に応答してダイアログが表示され、ユーザーがそのアクションを完了する方法が提供されます。 ダイアログを閉じる前に、ユーザーからの応答が必要です。
Apple によると、ダイアログを表示するには、次の 3 つの方法があります。
- Document Modal - [Document Modal] ダイアログを使用すると、ダイアログが閉じられるまで、ユーザーは特定のドキュメント内で他の操作を行うことができなくなります。
- App Modal - [App Modal] ダイアログを使用すると、ダイアログが閉じられるまで、ユーザーはアプリケーションを操作できなくなります。
- Modeless - [Modeless] ダイアログを使用すると、ユーザーはドキュメント ウィンドウを操作しながらダイアログの設定を変更できます。
モーダル ウィンドウ
標準 NSWindow
は、モーダルで表示することで、カスタマイズされたダイアログとして使用できます。
ドキュメント モーダル ダイアログ シート
"シート" は、特定のドキュメント ウィンドウにアタッチされたモーダル ダイアログであり、ユーザーはダイアログを閉じるまでウィンドウを操作できなくなります。 シートは表示元のウィンドウにアタッチされ、1 つのウィンドウに対して一度に開くことができるシートは 1 つだけです。
基本設定ウィンドウ
基本設定ウィンドウは、ユーザーが頻繁に変更しないアプリケーションの設定を含むモードレス ダイアログです。 基本設定ウィンドウには、多くの場合、ユーザーがさまざまな設定グループを切り替えることができるツール バーが含まれています。
オープン ダイアログ
オープン ダイアログでは、ユーザーがアプリケーション内でアイテムを検索して開くための一貫性のある方法を提供します。
印刷およびページ設定ダイアログ
macOS には、アプリケーションで表示できる標準の印刷およびページ設定ダイアログが用意されているため、ユーザーが使用するすべてのアプリケーションで一貫した印刷エクスペリエンスを実現できます。
印刷ダイアログは、次のようにフリー フローティング ダイアログ ボックスとして表示することも、
次のようにシートとして表示することもできます。
ページ設定ダイアログは、次のようにフリー フローティング ダイアログ ボックスとして表示することも、
次のようにシートとして表示することもできます。
保存ダイアログ
保存ダイアログでは、ユーザーはアプリケーションにアイテムを保存するための一貫性のある方法を提供します。 保存ダイアログには、最小 (折りたたみとも呼ばれます) 状態と
展開状態の 2 つがあります。
最小保存ダイアログは、シートとして表示することもできます。
展開保存ダイアログも同様です。
詳細については、Apple の OS X Human Interface Guidelines のダイアログに関するセクションを参照してください。
プロジェクトへのモーダル ウィンドウの追加
メイン ドキュメント ウィンドウとは別に、Xamarin.Mac アプリケーションでは、環境設定やインスペクター パネルなど、他の種類のウィンドウをユーザーに表示することが必要になる場合があります。
新しいウィンドウを追加するには、次の操作を行います:
ソリューション エクスプローラーで、Xcode の Interface Builder で編集するために
Main.storyboard
ファイルを開きます。新しいビュー コントローラーをデザイン サーフェイスにドラッグします。
ID インスペクターで、クラス名として
CustomDialogController
を入力します。Visual Studio for Mac に戻り、Xcode と同期して
CustomDialogController.h
ファイルを作成できるようにします。Xcode に戻り、インターフェイスを設計します。
アプリのメインウィンドウから新しいビュー コントローラーへのモーダル セグエを作成するには、ダイアログを開く UI 要素からダイアログのウィンドウに Control キーを押しながらドラッグします。 Identifier を割り当てます
ModalSegue
:アクションとアウトレットを接続します。
変更内容を保存し、Visual Studio for Mac に戻って Xcode と同期します。
CustomDialogController.cs
ファイルを次のようにします。
using System;
using Foundation;
using AppKit;
namespace MacDialog
{
public partial class CustomDialogController : NSViewController
{
#region Private Variables
private string _dialogTitle = "Title";
private string _dialogDescription = "Description";
private NSViewController _presentor;
#endregion
#region Computed Properties
public string DialogTitle {
get { return _dialogTitle; }
set { _dialogTitle = value; }
}
public string DialogDescription {
get { return _dialogDescription; }
set { _dialogDescription = value; }
}
public NSViewController Presentor {
get { return _presentor; }
set { _presentor = value; }
}
#endregion
#region Constructors
public CustomDialogController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void ViewWillAppear ()
{
base.ViewWillAppear ();
// Set initial title and description
Title.StringValue = DialogTitle;
Description.StringValue = DialogDescription;
}
#endregion
#region Private Methods
private void CloseDialog() {
Presentor.DismissViewController (this);
}
#endregion
#region Custom Actions
partial void AcceptDialog (Foundation.NSObject sender) {
RaiseDialogAccepted();
CloseDialog();
}
partial void CancelDialog (Foundation.NSObject sender) {
RaiseDialogCanceled();
CloseDialog();
}
#endregion
#region Events
public EventHandler DialogAccepted;
internal void RaiseDialogAccepted() {
if (this.DialogAccepted != null)
this.DialogAccepted (this, EventArgs.Empty);
}
public EventHandler DialogCanceled;
internal void RaiseDialogCanceled() {
if (this.DialogCanceled != null)
this.DialogCanceled (this, EventArgs.Empty);
}
#endregion
}
}
このコードでは、ダイアログのタイトルと説明を設定するいくつかのプロパティと、取り消された、または受け入れられたダイアログに対応するいくつかのイベントを公開します。
次に、ViewController.cs
ファイルを編集し、PrepareForSegue
メソッドをオーバーライドして、次のようにします:
public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue (segue, sender);
// Take action based on the segue name
switch (segue.Identifier) {
case "ModalSegue":
var dialog = segue.DestinationController as CustomDialogController;
dialog.DialogTitle = "MacDialog";
dialog.DialogDescription = "This is a sample dialog.";
dialog.DialogAccepted += (s, e) => {
Console.WriteLine ("Dialog accepted");
DismissViewController (dialog);
};
dialog.Presentor = this;
break;
}
}
このコードは、Xcode の Interface Builder で定義したセグエをダイアログに初期化し、タイトルと説明を設定します。 また、ユーザーがダイアログ ボックスで行う選択も処理します。
アプリケーションを実行し、カスタム ダイアログを表示できます。
Xamarin.Mac アプリケーションでのウィンドウの使用の詳細については、Microsoft の「Windows の操作」ドキュメントを参照してください。
カスタム シートの作成
"シート" は、特定のドキュメント ウィンドウにアタッチされたモーダル ダイアログであり、ユーザーはダイアログを閉じるまでウィンドウを操作できなくなります。 シートは表示元のウィンドウにアタッチされ、1 つのウィンドウに対して一度に開くことができるシートは 1 つだけです。
Xamarin.Mac でカスタム シートを作成するには、次の手順を実行します。
ソリューション エクスプローラーで、Xcode の Interface Builder で編集するために
Main.storyboard
ファイルを開きます。新しいビュー コントローラーをデザイン サーフェイスにドラッグします。
ユーザー インターフェイスを設計します。
メイン ウィンドウから新しいビュー コントローラーにシート セグエを作成します。
Identity Inspectorで、ビュー コントローラーの Class という名前を付けます
SheetViewController
。必要なアウトレットとアクションを定義します。
変更内容を保存し、Visual Studio for Mac に戻って同期します。
次に、SheetViewController.cs
ファイルを編集して、次のようにします。
using System;
using Foundation;
using AppKit;
namespace MacDialog
{
public partial class SheetViewController : NSViewController
{
#region Private Variables
private string _userName = "";
private string _password = "";
private NSViewController _presentor;
#endregion
#region Computed Properties
public string UserName {
get { return _userName; }
set { _userName = value; }
}
public string Password {
get { return _password;}
set { _password = value;}
}
public NSViewController Presentor {
get { return _presentor; }
set { _presentor = value; }
}
#endregion
#region Constructors
public SheetViewController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void ViewWillAppear ()
{
base.ViewWillAppear ();
// Set initial values
NameField.StringValue = UserName;
PasswordField.StringValue = Password;
// Wireup events
NameField.Changed += (sender, e) => {
UserName = NameField.StringValue;
};
PasswordField.Changed += (sender, e) => {
Password = PasswordField.StringValue;
};
}
#endregion
#region Private Methods
private void CloseSheet() {
Presentor.DismissViewController (this);
}
#endregion
#region Custom Actions
partial void AcceptSheet (Foundation.NSObject sender) {
RaiseSheetAccepted();
CloseSheet();
}
partial void CancelSheet (Foundation.NSObject sender) {
RaiseSheetCanceled();
CloseSheet();
}
#endregion
#region Events
public EventHandler SheetAccepted;
internal void RaiseSheetAccepted() {
if (this.SheetAccepted != null)
this.SheetAccepted (this, EventArgs.Empty);
}
public EventHandler SheetCanceled;
internal void RaiseSheetCanceled() {
if (this.SheetCanceled != null)
this.SheetCanceled (this, EventArgs.Empty);
}
#endregion
}
}
次に、ViewController.cs
ファイルを編集し、PrepareForSegue
メソッドを編集して、次のようにします:
public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue (segue, sender);
// Take action based on the segue name
switch (segue.Identifier) {
case "ModalSegue":
var dialog = segue.DestinationController as CustomDialogController;
dialog.DialogTitle = "MacDialog";
dialog.DialogDescription = "This is a sample dialog.";
dialog.DialogAccepted += (s, e) => {
Console.WriteLine ("Dialog accepted");
DismissViewController (dialog);
};
dialog.Presentor = this;
break;
case "SheetSegue":
var sheet = segue.DestinationController as SheetViewController;
sheet.SheetAccepted += (s, e) => {
Console.WriteLine ("User Name: {0} Password: {1}", sheet.UserName, sheet.Password);
};
sheet.Presentor = this;
break;
}
}
アプリケーションを実行してシートを開くと、ウィンドウにアタッチされます。
基本設定ダイアログの作成
Interface Builder で基本設定ビューをレイアウトする前に、基本設定の切り替えを処理するカスタム セグエの種類を追加する必要があります。 プロジェクトに新しいクラスを追加して、ReplaceViewSeque
という名前を付けます。 クラスを編集し、次のようにします。
using System;
using AppKit;
using Foundation;
namespace MacWindows
{
[Register("ReplaceViewSeque")]
public class ReplaceViewSeque : NSStoryboardSegue
{
#region Constructors
public ReplaceViewSeque() {
}
public ReplaceViewSeque (string identifier, NSObject sourceController, NSObject destinationController) : base(identifier,sourceController,destinationController) {
}
public ReplaceViewSeque (IntPtr handle) : base(handle) {
}
public ReplaceViewSeque (NSObjectFlag x) : base(x) {
}
#endregion
#region Override Methods
public override void Perform ()
{
// Cast the source and destination controllers
var source = SourceController as NSViewController;
var destination = DestinationController as NSViewController;
// Is there a source?
if (source == null) {
// No, get the current key window
var window = NSApplication.SharedApplication.KeyWindow;
// Swap the controllers
window.ContentViewController = destination;
// Release memory
window.ContentViewController?.RemoveFromParentViewController ();
} else {
// Swap the controllers
source.View.Window.ContentViewController = destination;
// Release memory
source.RemoveFromParentViewController ();
}
}
#endregion
}
}
カスタム セグエを作成したら、基本設定を処理するための新しいウィンドウを Xcode の Interface Builder に追加できます。
新しいウィンドウを追加するには、次の操作を行います:
ソリューション エクスプローラーで、Xcode の Interface Builder で編集するために
Main.storyboard
ファイルを開きます。新しいウィンドウ コントローラーをデザイン サーフェイスにドラッグします。
メニュー バー デザイナーの近くにウィンドウを配置します。
基本設定ビューにはタブが存在するため、アタッチされたビュー コントローラのコピーを作成します。
ライブラリから新しいツール バー コントローラーをドラッグします。
それを、デザイン サーフェイスのウィンドウにドロップします。
ツール バーのデザインをレイアウトします。
Control キーを押しながらクリックして、各ツール バー ボタンから上で作成したビューにドラッグします。 カスタム セグエの種類を選択します。
新しいセグエを選択し、[Class] を
ReplaceViewSegue
に設定します。デザイン サーフェイスのメニュー バー デザイナーで、[Application] メニューから [Preferences...] を選択し、Control キーを押しながらクリックし、[Preferences] ウィンドウにドラッグして、[Show] セグエを作成します。
変更内容を保存し、Visual Studio for Mac に戻って同期します。
コードを実行し、[Application] メニューから [Preferences...] を選択すると、ウィンドウが表示されます。
ウィンドウとツール バーの操作の詳細については、Microsoft のウィンドウとツール バーに関するドキュメントを参照してください。
基本設定の保存と読み込み
一般的な macOS アプリでは、ユーザーがアプリのユーザー設定に変更を加えると、それらの変更は自動的に保存されます。 Xamarin.Mac アプリでこれを処理する最も簡単な方法は、すべてのユーザー設定を管理し、システム全体で共有するための 1 つのクラスを作成することです。
まず、新しい AppPreferences
クラスをプロジェクトに追加し、NSObject
から継承します。 基本設定は、データ バインディングとキー値コーディングを使用するように設計されています。これにより、基本設定フォームを作成および保守するプロセスが大幅に簡単になります。 基本設定は少量の単純なデータ型で構成されるため、組み込みの NSUserDefaults
を使用して値を格納および取得します。
AppPreferences.cs
ファイルを編集し、次のようにします。
using System;
using Foundation;
using AppKit;
namespace SourceWriter
{
[Register("AppPreferences")]
public class AppPreferences : NSObject
{
#region Computed Properties
[Export("DefaultLanguage")]
public int DefaultLanguage {
get {
var value = LoadInt ("DefaultLanguage", 0);
return value;
}
set {
WillChangeValue ("DefaultLanguage");
SaveInt ("DefaultLanguage", value, true);
DidChangeValue ("DefaultLanguage");
}
}
[Export("SmartLinks")]
public bool SmartLinks {
get { return LoadBool ("SmartLinks", true); }
set {
WillChangeValue ("SmartLinks");
SaveBool ("SmartLinks", value, true);
DidChangeValue ("SmartLinks");
}
}
// Define any other required user preferences in the same fashion
...
[Export("EditorBackgroundColor")]
public NSColor EditorBackgroundColor {
get { return LoadColor("EditorBackgroundColor", NSColor.White); }
set {
WillChangeValue ("EditorBackgroundColor");
SaveColor ("EditorBackgroundColor", value, true);
DidChangeValue ("EditorBackgroundColor");
}
}
#endregion
#region Constructors
public AppPreferences ()
{
}
#endregion
#region Public Methods
public int LoadInt(string key, int defaultValue) {
// Attempt to read int
var number = NSUserDefaults.StandardUserDefaults.IntForKey(key);
// Take action based on value
if (number == null) {
return defaultValue;
} else {
return (int)number;
}
}
public void SaveInt(string key, int value, bool sync) {
NSUserDefaults.StandardUserDefaults.SetInt(value, key);
if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
}
public bool LoadBool(string key, bool defaultValue) {
// Attempt to read int
var value = NSUserDefaults.StandardUserDefaults.BoolForKey(key);
// Take action based on value
if (value == null) {
return defaultValue;
} else {
return value;
}
}
public void SaveBool(string key, bool value, bool sync) {
NSUserDefaults.StandardUserDefaults.SetBool(value, key);
if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
}
public string NSColorToHexString(NSColor color, bool withAlpha) {
//Break color into pieces
nfloat red=0, green=0, blue=0, alpha=0;
color.GetRgba (out red, out green, out blue, out alpha);
// Adjust to byte
alpha *= 255;
red *= 255;
green *= 255;
blue *= 255;
//With the alpha value?
if (withAlpha) {
return String.Format ("#{0:X2}{1:X2}{2:X2}{3:X2}", (int)alpha, (int)red, (int)green, (int)blue);
} else {
return String.Format ("#{0:X2}{1:X2}{2:X2}", (int)red, (int)green, (int)blue);
}
}
public NSColor NSColorFromHexString (string hexValue)
{
var colorString = hexValue.Replace ("#", "");
float red, green, blue, alpha;
// Convert color based on length
switch (colorString.Length) {
case 3 : // #RGB
red = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(0, 1)), 16) / 255f;
green = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(1, 1)), 16) / 255f;
blue = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(2, 1)), 16) / 255f;
return NSColor.FromRgba(red, green, blue, 1.0f);
case 6 : // #RRGGBB
red = Convert.ToInt32(colorString.Substring(0, 2), 16) / 255f;
green = Convert.ToInt32(colorString.Substring(2, 2), 16) / 255f;
blue = Convert.ToInt32(colorString.Substring(4, 2), 16) / 255f;
return NSColor.FromRgba(red, green, blue, 1.0f);
case 8 : // #AARRGGBB
alpha = Convert.ToInt32(colorString.Substring(0, 2), 16) / 255f;
red = Convert.ToInt32(colorString.Substring(2, 2), 16) / 255f;
green = Convert.ToInt32(colorString.Substring(4, 2), 16) / 255f;
blue = Convert.ToInt32(colorString.Substring(6, 2), 16) / 255f;
return NSColor.FromRgba(red, green, blue, alpha);
default :
throw new ArgumentOutOfRangeException(string.Format("Invalid color value '{0}'. It should be a hex value of the form #RBG, #RRGGBB or #AARRGGBB", hexValue));
}
}
public NSColor LoadColor(string key, NSColor defaultValue) {
// Attempt to read color
var hex = NSUserDefaults.StandardUserDefaults.StringForKey(key);
// Take action based on value
if (hex == null) {
return defaultValue;
} else {
return NSColorFromHexString (hex);
}
}
public void SaveColor(string key, NSColor color, bool sync) {
// Save to default
NSUserDefaults.StandardUserDefaults.SetString(NSColorToHexString(color,true), key);
if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
}
#endregion
}
}
このクラスには、NSUserDefaults
の操作を作業を容易にするために、SaveInt
、LoadInt
、SaveColor
、LoadColor
などのいくつかのヘルパー ルーチンが含まれています。 また、NSUserDefaults
には NSColors
を処理するための組み込みの方法がないため、NSColorToHexString
および NSColorFromHexString
メソッドを使用して、簡単に格納および取得できる Web ベースの 16 進文字列 (#RRGGBBAA
、ここで AA
はアルファ透明度) に色を変換します。
AppDelegate.cs
ファイルで、アプリ全体で使用される AppPreferences オブジェクトのインスタンスを作成します。
using AppKit;
using Foundation;
using System.IO;
using System;
namespace SourceWriter
{
[Register ("AppDelegate")]
public class AppDelegate : NSApplicationDelegate
{
#region Computed Properties
public int NewWindowNumber { get; set;} = -1;
public AppPreferences Preferences { get; set; } = new AppPreferences();
#endregion
#region Constructors
public AppDelegate ()
{
}
#endregion
...
基本設定を基本設定ビューに関連付ける
次に、Preference クラスを、上で作成した基本設定ウィンドウとビューの UI 要素に関連付けます。 Interface Builder で、基本設定ビュー コントローラーを選択し、ID インスペクターに切り替えて、コントローラーのカスタム クラスを作成します。
Visual Studio for Mac に戻って変更内容を同期し、新しく作成したクラスを編集のために開きます。 クラスを次のようにします。
using System;
using Foundation;
using AppKit;
namespace SourceWriter
{
public partial class EditorPrefsController : NSViewController
{
#region Application Access
public static AppDelegate App {
get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion
#region Computed Properties
[Export("Preferences")]
public AppPreferences Preferences {
get { return App.Preferences; }
}
#endregion
#region Constructors
public EditorPrefsController (IntPtr handle) : base (handle)
{
}
#endregion
}
}
このクラスでは、ここで次の 2 つのことを行っていることに注意してください。1 つ目は、AppDelegate へのアクセスを容易にするヘルパー App
プロパティがあります。 2 つ目は、Preferences
プロパティは、このビューに配置された UI コントロールとのデータ バインディングのためにグローバル AppPreferences クラスを公開しています。
次に、ストーリーボード ファイルをダブルクリックして Interface Builder で再度開きます (上で行った変更を確認します)。 基本設定インターフェイスをビルドするために必要な UI コントロールをビューにドラッグします。 コントロールごとに、Binding Inspector に切り替え、AppPreference クラスの個々のプロパティにバインドします。
必要なすべてのパネル (ビュー コントローラー) と基本設定プロパティについて、上記の手順を繰り返します。
開いているすべてのウィンドウに基本設定の変更を適用する
前述のように、一般的な macOS アプリでは、ユーザーがアプリのユーザー設定のいずれかに変更を加えると、それらの変更が自動的に保存され、ユーザーがアプリケーションで開いている可能性があるすべてのウィンドウに適用されます。
アプリの基本設定とウィンドウを慎重に計画および設計することで、エンド ユーザーは最小限のコーディング作業でこのプロセスをにスムーズかつ透過的に行えるようになります。
App Preferences を使用するウィンドウの場合は、次のヘルパー プロパティをコンテンツ ビュー コントローラーに追加して、AppDelegate へのアクセスを容易にします。
#region Application Access
public static AppDelegate App {
get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion
次に、ユーザーの設定に基づいてコンテンツまたは動作を構成するクラスを追加します。
public void ConfigureEditor() {
// General Preferences
TextEditor.AutomaticLinkDetectionEnabled = App.Preferences.SmartLinks;
TextEditor.AutomaticQuoteSubstitutionEnabled = App.Preferences.SmartQuotes;
...
}
ユーザーの設定に準拠していることを確認するために、ウィンドウが最初に開かれたときに構成メソッドを呼び出す必要があります。
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Configure editor from user preferences
ConfigureEditor ();
...
}
次に、AppDelegate.cs
ファイルを編集し、次のメソッドを追加して、開いているすべてのウィンドウに基本設定の変更を適用します。
public void UpdateWindowPreferences() {
// Process all open windows
for(int n=0; n<NSApplication.SharedApplication.Windows.Length; ++n) {
var content = NSApplication.SharedApplication.Windows[n].ContentViewController as ViewController;
if (content != null ) {
// Reformat all text
content.ConfigureEditor ();
}
}
}
次に、PreferenceWindowDelegate
クラスをプロジェクトに追加し、次のようにします。
using System;
using AppKit;
using System.IO;
using Foundation;
namespace SourceWriter
{
public class PreferenceWindowDelegate : NSWindowDelegate
{
#region Application Access
public static AppDelegate App {
get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion
#region Computed Properties
public NSWindow Window { get; set;}
#endregion
#region constructors
public PreferenceWindowDelegate (NSWindow window)
{
// Initialize
this.Window = window;
}
#endregion
#region Override Methods
public override bool WindowShouldClose (Foundation.NSObject sender)
{
// Apply any changes to open windows
App.UpdateWindowPreferences();
return true;
}
#endregion
}
}
これにより、基本設定ウィンドウを閉じたときに、開いているすべてのウィンドウに基本設定の変更が送信されます。
最後に、基本設定ウィンドウ コントローラーを編集し、上記で作成したデリゲートを追加します。
using System;
using Foundation;
using AppKit;
namespace SourceWriter
{
public partial class PreferenceWindowController : NSWindowController
{
#region Constructors
public PreferenceWindowController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void WindowDidLoad ()
{
base.WindowDidLoad ();
// Initialize
Window.Delegate = new PreferenceWindowDelegate(Window);
Toolbar.SelectedItemIdentifier = "General";
}
#endregion
}
}
これらの変更がすべて適切に行われると、ユーザーがアプリの基本設定を編集し、基本設定ウィンドウを閉じると、すべての開いているウィンドウに変更が適用されます。
オープン ダイアログ
オープン ダイアログでは、ユーザーがアプリケーション内でアイテムを検索して開くための一貫性のある方法を提供します。 Xamarin.Mac アプリケーションでオープン ダイアログを表示するには、次のコードを使用します。
var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = true;
dlg.CanChooseDirectories = false;
dlg.AllowedFileTypes = new string[] { "txt", "html", "md", "css" };
if (dlg.RunModal () == 1) {
// Nab the first file
var url = dlg.Urls [0];
if (url != null) {
var path = url.Path;
// Create a new window to hold the text
var newWindowController = new MainWindowController ();
newWindowController.Window.MakeKeyAndOrderFront (this);
// Load the text into the window
var window = newWindowController.Window as MainWindow;
window.Text = File.ReadAllText(path);
window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
window.RepresentedUrl = url;
}
}
上記のコードでは、新しいドキュメント ウィンドウを開き、ファイルのコンテンツを表示します。 このコードは、アプリケーションで必要な関数に置き換える必要があります。
次のプロパティは、NSOpenPanel
の使用時に使用できます。
- CanChooseFiles -
true
の場合、ユーザーはファイルを選択できます。 - CanChooseDirectories -
true
の場合、ユーザーはディレクトリを選択できます。 - AllowsMultipleSelection -
true
の場合、ユーザーは一度に複数のファイルを選択できます。 - ResolveAliases -
true
の場合、別名を選択すると、元のファイルのパスに解決されます。 - AllowedFileTypes - ユーザーが拡張子または UTI として選択できるファイルの種類の文字列配列です。 既定値は
null
で、任意のファイルを開くことができます。
RunModal ()
メソッドは、オープン ダイアログを表示し、ユーザーが (プロパティで指定されている) ファイルまたはディレクトリを選択できるようにし、ユーザーが [Open] ボタンをクリックした場合に 1
を返します。
オープン ダイアログは、ユーザーが選択したファイルまたはディレクトリを、URL
プロパティで URL の配列として返します。
プログラムを実行し、[File] メニューから [Open...] i項目を選択すると、次の内容が表示されます。
印刷およびページ設定ダイアログ
macOS には、アプリケーションで表示できる標準の印刷およびページ設定ダイアログが用意されているため、ユーザーが使用するすべてのアプリケーションで一貫した印刷エクスペリエンスを実現できます。
次のコードは、標準の印刷ダイアログを表示します。
public bool ShowPrintAsSheet { get; set;} = true;
...
[Export ("showPrinter:")]
void ShowDocument (NSObject sender) {
var dlg = new NSPrintPanel();
// Display the print dialog as dialog box
if (ShowPrintAsSheet) {
dlg.BeginSheet(new NSPrintInfo(),this,this,null,new IntPtr());
} else {
if (dlg.RunModalWithPrintInfo(new NSPrintInfo()) == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Critical,
InformativeText = "We need to print the document here...",
MessageText = "Print Document",
};
alert.RunModal ();
}
}
}
ShowPrintAsSheet
プロパティを false
に設定し、アプリケーションを実行して印刷ダイアログを表示すると、次の内容が表示されます。
ShowPrintAsSheet
プロパティを true
に設定し、アプリケーションを実行して印刷ダイアログを表示すると、次の内容が表示されます。
次のコードは、ページ レイアウト ダイアログを表示します。
[Export ("showLayout:")]
void ShowLayout (NSObject sender) {
var dlg = new NSPageLayout();
// Display the print dialog as dialog box
if (ShowPrintAsSheet) {
dlg.BeginSheet (new NSPrintInfo (), this);
} else {
if (dlg.RunModal () == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Critical,
InformativeText = "We need to print the document here...",
MessageText = "Print Document",
};
alert.RunModal ();
}
}
}
ShowPrintAsSheet
プロパティを false
に設定し、アプリケーションを実行して印刷レイアウト ダイアログを表示すると、次の内容が表示されます。
ShowPrintAsSheet
プロパティを true
に設定し、アプリケーションを実行して印刷レイアウト ダイアログを表示すると、次の内容が表示されます。
印刷およびページ設定ダイアログの操作の詳細については、Apple の NSPrintPanel および NSPageLayout に関するドキュメントを参照してください。
保存ダイアログ
保存ダイアログでは、ユーザーはアプリケーションにアイテムを保存するための一貫性のある方法を提供します。
次のコードは、標準の保存ダイアログを表示します。
public bool ShowSaveAsSheet { get; set;} = true;
...
[Export("saveDocumentAs:")]
void ShowSaveAs (NSObject sender)
{
var dlg = new NSSavePanel ();
dlg.Title = "Save Text File";
dlg.AllowedFileTypes = new string[] { "txt", "html", "md", "css" };
if (ShowSaveAsSheet) {
dlg.BeginSheet(mainWindowController.Window,(result) => {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Critical,
InformativeText = "We need to save the document here...",
MessageText = "Save Document",
};
alert.RunModal ();
});
} else {
if (dlg.RunModal () == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Critical,
InformativeText = "We need to save the document here...",
MessageText = "Save Document",
};
alert.RunModal ();
}
}
}
AllowedFileTypes
プロパティは、ユーザーがファイルを保存するために選択できるファイルの種類の文字列配列です。 ファイルの種類は、拡張子または UTI として指定できます。 既定値は null
で、任意のファイルの種類を使用できます。
ShowSaveAsSheet
プロパティを false
に設定し、アプリケーションを実行して [File] メニューから [Save As...] を選択すると、次の内容が表示されます。
ユーザーはダイアログを展開できます。
ShowSaveAsSheet
プロパティを true
に設定し、アプリケーションを実行して [File] メニューから [Save As...] を選択すると、次の内容が表示されます。
ユーザーはダイアログを展開できます。
保存ダイアログの操作の詳細については、Apple の NSSavePanel に関するドキュメントを参照してください。
まとめ
この記事では、Xamarin.Mac アプリケーションでのモーダル ウィンドウ、シート、標準システム ダイアログ ボックスの操作について詳しく説明しました。 モーダル ウィンドウ、シート、ダイアログのさまざまな種類と使用方法、Xcode の Interface Builder でモーダル ウィンドウとシートを作成および保守する方法、および C# コードでモーダル ウィンドウ、シート、ダイアログを操作する方法について説明しました。