Xamarin. Mac のダイアログDialogs in Xamarin.Mac

Xamarin. Mac アプリケーションで C# と .NET を使用する場合、 Xcode と**で作業している 開発者が同じダイアログとモーダルウィンドウにアクセスできます。When working with C# and .NET in a Xamarin.Mac application, you have access to the same Dialogs and Modal Windows that a developer working in Objective-C and Xcode does. Xcode は直接統合されているため、Xcode の Interface Builder を使用してモーダルウィンドウを作成および維持できます (または、必要に応じて C# コードで直接作成することもできます)。Because Xamarin.Mac integrates directly with Xcode, you can use Xcode's Interface Builder to create and maintain your Modal Windows (or optionally create them directly in C# code).

ユーザーの操作に応答してダイアログが表示され、通常、ユーザーがアクションを完了する方法が示されます。A dialog appears in response to a user action and typically provides ways users can complete the action. ダイアログを閉じるには、ユーザーからの応答が必要です。A dialog requires a response from the user before it can be closed.

ウィンドウは、モードレス状態 (複数のドキュメントを一度に開くことができるテキストエディターなど) またはモーダル (アプリケーションを続行する前に閉じる必要があるエクスポートダイアログなど) で使用できます。Windows can be used in a Modeless state (such as a text editor that can have multiple documents open at once) or Modal (such as an Export dialog that must be dismissed before the application can continue).

[開く] ダイアログボックスAn open dialog box

この記事では、Xamarin. Mac アプリケーションでのダイアログとモーダルウィンドウの操作の基本について説明します。In this article, we'll cover the basics of working with Dialogs and Modal Windows in a Xamarin.Mac application. この記事で使用する主要な概念と手法について説明しているように、最初に Hello, Mac の記事「 Xcode と Interface Builder の概要 」と「 アウトレットとアクション 」セクションをご覧になることを強くお勧めします。It is highly suggested that you work through the Hello, Mac article first, specifically the Introduction to Xcode and Interface Builder and Outlets and Actions sections, as it covers key concepts and techniques that we'll be using in this article.

C# のクラス/メソッドを Xamarin. Mac の内部 ドキュメントの前に公開する」セクションを参照して Register Export ください。 c# クラスを目的の c オブジェクトと UI 要素に接続するために使用されるコマンドとコマンドについても説明します。You may want to take a look at the Exposing C# classes / methods to Objective-C section of the Xamarin.Mac Internals document as well, it explains the Register and Export commands used to wire-up your C# classes to Objective-C objects and UI Elements.

ダイアログの概要Introduction to Dialogs

ユーザー操作 (ファイルの保存など) への応答としてダイアログが表示され、ユーザーはその操作を完了することができます。A dialog appears in response to a user action (such as saving a file) and provides a way for users to complete that action. ダイアログを閉じるには、ユーザーからの応答が必要です。A dialog requires a response from the user before it can be closed.

Apple によれば、ダイアログを表示するには次の3つの方法があります。According to Apple, there are three ways to present a Dialog:

  • ドキュメントモーダル -ドキュメントモーダルダイアログでは、ユーザーは、ドキュメントが破棄されるまで、特定のドキュメント内で他の操作を実行できません。Document Modal - A Document Modal dialog prevents the user from doing anything else within a given document until it is dismissed.
  • アプリモーダル -アプリのモーダルダイアログでは、ユーザーがアプリケーションと対話できないようにします。App Modal - An App Modal dialog prevents the user from interacting with the application until it is dismissed.
  • モードレス モードレスダイアログボックスを使用すると、ユーザーはドキュメントウィンドウと対話しながらダイアログの設定を変更できます。Modeless A Modeless Dialog enables users to change settings in the dialog while still interacting with the document window.

任意の標準を NSWindow モーダルとして表示することで、カスタマイズされたダイアログとして使用できます。Any standard NSWindow can be used as a customized dialog by displaying it modally:

モーダルウィンドウの例An example modal window

ドキュメントモーダルダイアログシートDocument Modal Dialog Sheets

シート は、特定のドキュメントウィンドウに関連付けられたモーダルダイアログで、ユーザーがダイアログを閉じるまでウィンドウと対話できないようにします。A Sheet is a modal dialog that is attached to a given document window, preventing users from interacting with the window until they dismiss the dialog. ウィンドウが表示されているウィンドウにシートが添付されており、一度に1つのウィンドウで開くことができるシートは1つだけです。A Sheet is attached to the window from which it emerges and only one sheet can be open for a window at any one time.

モーダルシートの例An example modal sheet

[基本設定] ウィンドウPreferences Windows

[基本設定] ウィンドウは、ユーザーが頻繁に変更するアプリケーションの設定を含むモードレスダイアログです。A Preferences Window is a modeless dialog that contains the application's settings that the user changes infrequently. [基本設定] ウィンドウには、ユーザーがさまざまな設定グループを切り替えることができるツールバーが含まれていることがよくあります。Preferences Windows often include a Toolbar that allows the user to switch between different groups of settings:

基本設定ウィンドウの例An example preference window

ダイアログを開くOpen Dialog

[開く] ダイアログボックスでは、アプリケーション内の項目を検索して開くための一貫した方法がユーザーに提供されます。The Open Dialog gives users a consistent way to find and open an item in an application:

[開く] ダイアログボックスA open dialog box

macOS には、標準の印刷とページ設定のダイアログボックスが用意されており、ユーザーは使用するすべてのアプリケーションで一貫した印刷機能を使用できます。macOS provides standard Print and Page Setup Dialogs that your application can display so that users can have a consistent printing experience in every application they use.

[印刷] ダイアログボックスは、フリーフローティングダイアログボックスの両方として表示できます。The Print Dialog can be displayed as both a free floating dialog box:

[印刷] ダイアログボックスA print dialog box

または、このファイルをシートとして表示することもできます。Or it can be displayed as a Sheet:

印刷シートA print sheet

[ページ設定] ダイアログは、無料のフローティングダイアログボックスの両方として表示できます。The Page Setup Dialog can be displayed as both a free floating dialog box:

ページ設定ダイアログA page setup dialog

または、このファイルをシートとして表示することもできます。Or it can be displayed as a Sheet:

ページ設定シートA page setup sheet

ダイアログの保存Save Dialogs

[保存] ダイアログを使用すると、アプリケーションにアイテムを保存するための一貫した方法をユーザーが選択できます。The Save Dialog gives users a consistent way to save an item in an application. [保存] ダイアログには、 最小 (折りたたまれているとも呼ばれます) という2つの状態があります。The Save Dialog has two states: Minimal (also known as Collapsed):

保存ダイアログA save dialog

展開 された状態は次のようになります。And the Expanded state:

拡張保存ダイアログAn expanded save dialog

[ 最小 保存] ダイアログボックスは、次のようにシートとして表示することもできます。The Minimal Save Dialog can also be displayed as a Sheet:

最小保存シートA minimal save sheet

展開 された [保存] ダイアログボックスは次のようになります。As can the Expanded Save Dialog:

展開された保存シートAn expanded save sheet

詳細については、「Apple のOS X ヒューマンインターフェイスガイドライン」の「ダイアログ」セクションを参照してください。For more information, see the Dialogs section of Apple's OS X Human Interface Guidelines

プロジェクトへのモーダルウィンドウの追加Adding a Modal Window to a Project

メインのドキュメントウィンドウとは別に、Xamarin. Mac アプリケーションでは、他の種類のウィンドウをユーザーに表示する必要がある場合があります (ユーザー設定やインスペクターパネルなど)。Aside from the main document window, a Xamarin.Mac application might need to display other types of windows to the user, such as Preferences or Inspector Panels.

新しいウィンドウを追加するには、次の手順を実行します。To add a new window, do the following:

  1. ソリューションエクスプローラー で、 Main.storyboard Xcode の Interface Builder で編集するファイルを開きます。In the Solution Explorer, open the Main.storyboard file for editing in Xcode's Interface Builder.

  2. 新しい ビューコントローラー をデザインサーフェイスにドラッグします。Drag a new View Controller into the Design Surface:

    ライブラリからビューコントローラーを選択するSelecting a View Controller from the Library

  3. Id インスペクター で、 CustomDialogController クラス名 として「」と入力します。In the Identity Inspector, enter CustomDialogController for the Class Name:

    クラス名を Customて Controller に設定しています。Setting the class name to CustomDialogController.

  4. Visual Studio for Mac に戻り、Xcode との同期を許可して、ファイルを作成し CustomDialogController.h ます。Switch back to Visual Studio for Mac, allow it to sync with Xcode and create the CustomDialogController.h file.

  5. Xcode に戻り、インターフェイスを設計します。Return to Xcode and design your interface:

    Xcode で UI を設計するDesigning the UI in Xcode

  6. ダイアログボックスを開く UI 要素からコントロールをドラッグして、アプリのメインウィンドウから新しいビューコントローラーに モーダルセグエ を作成します。Create a Modal Segue from the Main Window of your app to the new View Controller by control-dragging from the UI element that will open the dialog to the dialog's window. 識別子 を割り当て ModalSegue ます。Assign the Identifier ModalSegue:

    モーダルセグエA modal segue

  7. すべての アクションアウトレット を接続します。Wire-up any Actions and Outlets:

    アクションの構成Configuring an Action

  8. 変更を保存し Visual Studio for Mac に戻り、Xcode と同期します。Save your changes and return to Visual Studio for Mac to sync with Xcode.

ファイルの CustomDialogController.cs 外観を次のようにします。Make the CustomDialogController.cs file look like the following:

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
    }
}

このコードでは、いくつかのプロパティを公開して、ダイアログのタイトルと説明を設定し、いくつかのイベントを表示して、ダイアログがキャンセルまたは受け入れられるようにします。This code exposes a few properties to set the title and the description of the dialog and a few events to react to the dialog being canceled or accepted.

次に、ファイルを編集し、 ViewController.cs メソッドをオーバーライドして、 PrepareForSegue 次のようにします。Next, edit the ViewController.cs file, override the PrepareForSegue method and make it look like the following:

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 で定義したセグエをダイアログに初期化し、タイトルと説明を設定します。This code initializes the segue that we defined in Xcode's Interface Builder to our dialog and sets up the title and description. また、ユーザーがダイアログボックスで行った選択を処理します。It also handles the choice the user makes in the dialog box.

アプリケーションを実行し、カスタムダイアログを表示することができます。We can run our application and display the custom dialog:

ダイアログの例An example dialog

Xamarin. Mac アプリケーションで windows を使用する方法の詳細については、 windows のドキュメントを参照してください。For more information about using windows in a Xamarin.Mac application, please see our Working with Windows documentation.

カスタムシートの作成Creating a Custom Sheet

シート は、特定のドキュメントウィンドウに関連付けられたモーダルダイアログで、ユーザーがダイアログを閉じるまでウィンドウと対話できないようにします。A Sheet is a modal dialog that is attached to a given document window, preventing users from interacting with the window until they dismiss the dialog. ウィンドウが表示されているウィンドウにシートが添付されており、一度に1つのウィンドウで開くことができるシートは1つだけです。A Sheet is attached to the window from which it emerges and only one sheet can be open for a window at any one time.

Xamarin. Mac でカスタムシートを作成するには、次の手順を実行します。To create a Custom Sheet in Xamarin.Mac, let's do the following:

  1. ソリューションエクスプローラー で、 Main.storyboard Xcode の Interface Builder で編集するファイルを開きます。In the Solution Explorer, open the Main.storyboard file for editing in Xcode's Interface Builder.

  2. 新しい ビューコントローラー をデザインサーフェイスにドラッグします。Drag a new View Controller into the Design Surface:

    ライブラリからビューコントローラーを選択するSelecting a View Controller from the Library

  3. ユーザーインターフェイスを設計します。Design your user interface:

    UI デザインThe UI design

  4. メインウィンドウから新しいビューコントローラーに シートセグエ を作成します。Create a Sheet Segue from your Main Window to the new View Controller:

    シートのセグエの種類の選択Selecting the Sheet segue type

  5. Id インスペクター で、ビューコントローラーの クラス にという名前を指定し SheetViewController ます。In the Identity Inspector, name the View Controller's Class SheetViewController:

    クラス名を「sheet Viewcontroller」に設定します。Setting the class name to SheetViewController.

  6. 必要な アウトレットアクション を定義します。Define any needed Outlets and Actions:

    必要なコンセントとアクションを定義するDefining the required Outlets and Actions

  7. 変更を保存し、Visual Studio for Mac に戻って同期します。Save your changes and return to Visual Studio for Mac to sync.

次に、ファイルを編集 SheetViewController.cs し、次のように表示します。Next, edit the SheetViewController.cs file and make it look like the following:

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 ます。Next, edit the ViewController.cs file, edit the PrepareForSegue method and make it look like the following:

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;
    }
}

アプリケーションを実行してシートを開くと、ウィンドウに接続されます。If we run our application and open the Sheet, it will be attached to the window:

例のシートAn example sheet

基本設定ダイアログの作成Creating a Preferences Dialog

Interface Builder で基本設定ビューをレイアウトする前に、カスタムのセグエ型を追加して、ユーザー設定の切り替えを処理する必要があります。Before we lay out the Preference View in Interface Builder, we'll need to add a custom segue type to handle switching out the preferences. 新しいクラスをプロジェクトに追加し、それを呼び出し ReplaceViewSeque ます。Add a new class to your project and call it ReplaceViewSeque. クラスを編集し、次のようにします。Edit the class and make it look like the following:

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 に新しいウィンドウを追加して、設定を処理することができます。With the custom segue created, we can add a new window in Xcode's Interface Builder to handle our preferences.

新しいウィンドウを追加するには、次の手順を実行します。To add a new window, do the following:

  1. ソリューションエクスプローラー で、 Main.storyboard Xcode の Interface Builder で編集するファイルを開きます。In the Solution Explorer, open the Main.storyboard file for editing in Xcode's Interface Builder.

  2. 新しい ウィンドウコントローラー をデザインサーフェイスにドラッグします。Drag a new Window Controller into the Design Surface:

    ライブラリからウィンドウコントローラーを選択しますSelect a Window Controller from the Library

  3. ウィンドウを メニューバー デザイナーの近くに配置します。Arrange the Window near the Menu Bar designer:

    新しいウィンドウの追加Adding the new Window

  4. 基本設定ビューにタブが表示されるように、アタッチされたビューコントローラーのコピーを作成します。Create copies of the attached View Controller as there will be tabs in your preference view:

    必要なビューコントローラーの追加Adding the required View Controllers

  5. 新しい ツールバーコントローラーライブラリ からドラッグします。Drag a new Toolbar Controller from the Library:

    ライブラリからツールバーコントローラーを選択しますSelect a Toolbar Controller from the Library

  6. 次のように、デザインサーフェイスのウィンドウにドロップします。And drop it on the Window in the Design Surface:

    新しいツールバーコントローラーの追加Adding a new Toolbar Controller

  7. ツールバーのデザインをレイアウトします。Layout the design of your toolbar:

    ツールバーのレイアウトLayout the toolbar

  8. ツールバー の各ボタンから、前に作成したビューに Control-Click してドラッグします。Control-Click and drag from each Toolbar Button to the Views you created above. カスタム セグエの種類を選択してください:Select a Custom segue type:

    カスタムのセグエ型を設定します。Setting a Custom segue type.

  9. 新しいセグエを選択し、 クラス を次のように設定し ReplaceViewSegue ます。Select the new Segue and set the Class to ReplaceViewSegue:

    セグエクラスの設定Setting the segue class

  10. デザインサーフェイスのメニュー バーデザイナー で、[アプリケーション] メニューの [ 基本設定...] を選択し、コントロールをクリックして [基本設定] ウィンドウまでドラッグし、 Show セグエを作成します。In the Menubar Designer on the Design Surface, from the Application Menu select Preferences..., control-click and drag to the Preferences Window to create a Show segue:

    基本設定を [基本設定] ウィンドウにドラッグして、セグエの種類を設定します。Setting the segue type by dragging Preferences to the Preferences Window.

  11. 変更を保存し、Visual Studio for Mac に戻って同期します。Save your changes and return to Visual Studio for Mac to sync.

コードを実行し、[アプリケーション] メニュー の [基本設定... ] を選択すると、ウィンドウが表示されます。If we run the code and select the Preferences... from the Application Menu, the window will be displayed:

[プロファイルの例] ウィンドウが表示されます。An example preferences window displaying the word Profile.

Windows とツールバーの使用方法の詳細については、 windowsツールバー のドキュメントを参照してください。For more information on working with Windows and Toolbars, please see our Windows and Toolbars documentation.

設定の保存と読み込みSaving and Loading Preferences

一般的な macOS アプリでは、ユーザーがアプリのユーザー設定を変更すると、それらの変更は自動的に保存されます。In a typical macOS App, when the user makes changes to any of the App's User Preferences, those changes are saved automatically. Xamarin. Mac アプリでこれを処理する最も簡単な方法は、1つのクラスを作成して、すべてのユーザーの基本設定を管理し、システム全体を共有することです。The easiest way to handle this in a Xamarin.Mac app, is to create a single class to manage all of the user's preferences and share it system-wide.

まず、新しいクラスを AppPreferences プロジェクトに追加し、を継承し NSObject ます。First, add a new AppPreferences class to the project and inherit from NSObject. 基本設定は、 データバインディングと Key-Value コーディング を使用するように設計されています。これにより、基本設定フォームを作成して維持するプロセスが非常に簡単になります。The preferences will be designed to use Data Binding and Key-Value Coding which will make the process of creating and maintaining the preference forms much simpler. 基本設定は少量の単純なデータ型で構成されるため、組み込みのを使用して NSUserDefaults 値を格納および取得します。Since the Preferences will consist of a small amount of simple datatypes, use the built in NSUserDefaults to store and retrieve values.

ファイルを編集 AppPreferences.cs し、次のように表示します。Edit the AppPreferences.cs file and make it look like the following:

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
    }
}

このクラスには SaveIntLoadInt SaveColor LoadColor 簡単に作業できるように、、、、などのいくつかのヘルパールーチンが NSUserDefaults 含まれています。This class contains a few helper routines such as SaveInt, LoadInt, SaveColor, LoadColor, etc. to make working with NSUserDefaults easier. また、には、を処理するための組み込みの方法がないため、およびメソッドを使用して、 NSUserDefaults NSColors NSColorToHexString NSColorFromHexString 簡単に格納および取得できる web ベースの16進文字列 ( #RRGGBBAA ここ AA ではアルファ透明度) に色を変換します。Also, since NSUserDefaults does not have a built-in way to handle NSColors, the NSColorToHexString and NSColorFromHexString methods are used to convert colors to web-based hex strings (#RRGGBBAA where AA is the alpha transparency) that can be easily stored and retrieved.

ファイルで、 AppDelegate.cs アプリ全体で使用される apppreferences オブジェクトのインスタンスを作成します。In the AppDelegate.cs file, create an instance of the AppPreferences object that will be used app-wide:

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
        
        ...

優先順位ビューへの設定の配線Wiring Preferences to Preference Views

次に、[基本設定] ウィンドウと上で作成したビューの UI 要素に優先クラスを接続します。Next, connect Preference class to UI elements on the Preference Window and Views created above. Interface Builder で、基本設定ビューコントローラーを選択し、 Id インスペクター に切り替えて、コントローラーのカスタムクラスを作成します。In Interface Builder, select a Preference View Controller and switch to the Identity Inspector, create a custom class for the controller:

Id インスペクターThe Identity Inspector

Visual Studio for Mac に戻り、変更を同期し、新しく作成したクラスを編集用に開きます。Switch back to Visual Studio for Mac to sync your changes and open the newly created class for editing. クラスは次のようになります。Make the class look like the following:

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つの処理を行いました。まず、 App appdelegate にアクセスするためのヘルパープロパティがあります。Notice that this class has done two things here: First, there is a helper App property to make accessing the AppDelegate easier. 次に、 Preferences プロパティは、このビューに配置されているすべての UI コントロールを使用して、データバインディングのグローバル apppreferences クラスを公開します。Second, the Preferences property exposes the global AppPreferences class for data binding with any UI controls placed on this View.

次に、ストーリーボードファイルをダブルクリックして Interface Builder で再び開きます (上記の変更を確認してください)。Next, double click the Storyboard file to re-open it in Interface Builder (and see the changes just made above). 好みのインターフェイスを構築するために必要な UI コントロールをビューにドラッグします。Drag any UI controls required to build the preferences interface into the View. 各コントロールに対して、 バインドインスペクター に切り替え、 apppreference クラスの個々のプロパティにバインドします。For each control, switch to the Binding Inspector and bind to the individual properties of the AppPreference class:

バインディングインスペクターThe Binding Inspector

必要なすべてのパネル (ビューコントローラー) と基本設定プロパティについて、上記の手順を繰り返します。Repeat the above steps for all of the panels (View Controllers) and Preference Properties required.

すべての開いているウィンドウに基本設定の変更を適用するApplying Preference Changes to All Open Windows

前述のように、一般的な macOS アプリでは、ユーザーがアプリのユーザー設定を変更すると、それらの変更は自動的に保存され、ユーザーがアプリケーションで開いている可能性のあるすべてのウィンドウに適用されます。As stated above, in a typical macOS App, when the user makes changes to any of the App's User Preferences, those changes are saved automatically and applied to any windows the user might have open in the application.

アプリの基本設定とウィンドウを慎重に計画し、設計することで、このプロセスをエンドユーザーに対してスムーズかつ透過的に実行でき、最小限のコーディング作業が可能になります。Careful planning and design of your app's preferences and windows will allow this process to happen smoothly and transparently to the end user, with a minimal amount of coding work.

アプリの基本設定を使用するウィンドウについては、次のヘルパープロパティをコンテンツビューコントローラーに追加して、 Appdelegate に簡単にアクセスできるようにします。For any Window that will be consuming App Preferences, add the following helper property to its Content View Controller to make accessing our AppDelegate easier:

#region Application Access
public static AppDelegate App {
    get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion

次に、クラスを追加して、ユーザーの設定に基づいてコンテンツまたは動作を構成します。Next, add a class to configure the contents or behavior based on the user's preferences:

public void ConfigureEditor() {

    // General Preferences
    TextEditor.AutomaticLinkDetectionEnabled = App.Preferences.SmartLinks;
    TextEditor.AutomaticQuoteSubstitutionEnabled = App.Preferences.SmartQuotes;
    ...

}

ユーザーの設定に準拠していることを確認するために、最初にウィンドウを開いたときに、構成メソッドを呼び出す必要があります。You need to call the configuration method when the Window is first opened to make sure it conforms to the user's preferences:

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

    // Configure editor from user preferences
    ConfigureEditor ();
    ...
}

次に、ファイルを編集 AppDelegate.cs し、次のメソッドを追加して、すべての開いているウィンドウに基本設定の変更を適用します。Next, edit the AppDelegate.cs file and add the following method to apply any preference changes to all open windows:

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 クラスをプロジェクトに追加し、次のように表示します。Next, add a PreferenceWindowDelegate class to the project and make it look like the following:

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
    }
}

これにより、基本設定ウィンドウが閉じたときに、すべての開いているウィンドウに設定の変更が送信されます。This will cause any preference changes to be sent to all open Windows when the preference Window closes.

最後に、基本設定ウィンドウコントローラーを編集し、上で作成したデリゲートを追加します。Finally, edit the Preference Window Controller and add the delegate created above:

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
    }
}

これらのすべての変更が適用され、ユーザーがアプリの設定を編集し、ユーザー設定ウィンドウを閉じると、すべての開いているウィンドウに変更が適用されます。With all these changes in place, if the user edits the App's Preferences and closes the Preference Window, the changes will be applied to all open Windows:

他のいくつかの開いているウィンドウと共に表示される [設定の例] ウィンドウ。An example Preferences Window, displayed with several other open windows.

[開く] ダイアログThe Open Dialog

[開く] ダイアログボックスでは、アプリケーション内の項目を検索して開くための一貫した方法をユーザーに提供します。The Open Dialog gives users a consistent way to find and open an item in an application. 開いているダイアログを Xamarin. Mac アプリケーションで表示するには、次のコードを使用します。To display an Open Dialog in a Xamarin.Mac application, use the following code:

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;

    }
}

上のコードでは、新しいドキュメントウィンドウを開いて、ファイルの内容を表示しています。In the above code, we are opening a new document window to display the contents of the file. このコードを、アプリケーションで必要な機能に置き換える必要があります。You'll need to replace this code with functionality is required by your application.

を使用する場合は、次のプロパティを使用でき NSOpenPanel ます。The following properties are available when working with a NSOpenPanel:

  • [ファイル true ]-ユーザーがファイルを選択できる場合はです。CanChooseFiles - If true the user can select files.
  • [ディレクトリ true ]-ユーザーがディレクトリを選択できる場合は、CanChooseDirectories - If true the user can select directories.
  • Allows複数選択 - true ユーザーが一度に複数のファイルを選択できる場合。AllowsMultipleSelection - If true the user can select more than one file at a time.
  • Resolvealiases -を true 選択してエイリアスを選択すると、元のファイルのパスに解決されます。ResolveAliases - If true selecting and alias, resolves it to the original file's path.
  • AllowedFileTypes -拡張子または UTI のいずれかとしてユーザーが選択できるファイルの種類の文字列配列です。AllowedFileTypes - Is a string array of file types that the user can select as either an extension or UTI. 既定値は null で、任意のファイルを開くことができます。The default value is null, which allows any file to be opened.

メソッドは、[ RunModal () 開く] ダイアログボックスを表示し、ユーザーが [開く] ボタンをクリックしたときに、ファイルまたはディレクトリを選択できるようにし 1 ます。 The RunModal () method displays the Open Dialog and allow the user to select files or directories (as specified by the properties) and returns 1 if the user clicks the Open button.

[開く] ダイアログボックスでは、ユーザーが選択したファイルまたはディレクトリが、プロパティの Url の配列として返され URL ます。The Open Dialog returns the user's selected files or directories as an array of URLs in the URL property.

プログラムを実行し、[ファイル] メニューから [開く] 項目を選択すると、次のように表示されます。If we run the program and select the Open... item from the File menu, the following is displayed:

[開く] ダイアログボックスAn open dialog box

[印刷とページの設定] ダイアログThe Print and Page Setup Dialogs

macOS には、標準の印刷とページ設定のダイアログボックスが用意されており、ユーザーは使用するすべてのアプリケーションで一貫した印刷機能を使用できます。macOS provides standard Print and Page Setup Dialogs that your application can display so that users can have a consistent printing experience in every application they use.

次のコードは、標準の [印刷] ダイアログボックスを示しています。The following code will show the standard Print Dialog:

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 、アプリケーションを実行して [印刷] ダイアログを表示すると、次のように表示されます。If we set the ShowPrintAsSheet property to false, run the application and display the print dialog, the following will be displayed:

[印刷] ダイアログボックスA print dialog box

ShowPrintAsSheetプロパティをに設定すると、アプリケーションが実行されて [ true 印刷] ダイアログボックスが表示され、次のように表示されます。If set the ShowPrintAsSheet property to true, run the application and display the print dialog, the following will be displayed:

印刷シートA print sheet

次のコードは、[ページレイアウト] ダイアログを表示します。The following code will display the Page Layout Dialog:

[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 、アプリケーションを実行して [印刷レイアウト] ダイアログを表示すると、次のように表示されます。If we set the ShowPrintAsSheet property to false, run the application and display the print layout dialog, the following will be displayed:

ページ設定ダイアログA page setup dialog

ShowPrintAsSheetプロパティをに設定し true てアプリケーションを実行し、[印刷レイアウト] ダイアログを表示すると、次のように表示されます。If set the ShowPrintAsSheet property to true, run the application and display the print layout dialog, the following will be displayed:

ページ設定シートA page setup sheet

[印刷] および [ページ設定] ダイアログの操作の詳細については、Apple の NSPrintPanelNSPageLayout のドキュメントを参照してください。For more information about working with the Print and Page Setup Dialogs, please see Apple's NSPrintPanel and NSPageLayout documentation.

[保存] ダイアログThe Save Dialog

[保存] ダイアログを使用すると、アプリケーションにアイテムを保存するための一貫した方法をユーザーが選択できます。The Save Dialog gives users a consistent way to save an item in an application.

次のコードは、標準の [保存] ダイアログを示しています。The following code will show the standard Save Dialog:

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 ユーザーがファイルを保存するために選択できるファイルの種類の文字列配列です。The AllowedFileTypes property is a string array of file types that the user can select to save the file as. ファイルの種類は、拡張子または UTI として指定できます。The file type can be either specified as an extension or UTI. 既定値は null で、任意のファイルの種類を使用できます。The default value is null, which allows any file type to be used.

プロパティをに設定した場合は、 ShowSaveAsSheet false アプリケーションを実行し、[ファイル] メニューから [名前を付けて保存... ] を選択すると、次の内容が表示されます。If we set the ShowSaveAsSheet property to false, run the application and select Save As... from the File menu, the following will be displayed:

[保存] ダイアログボックスA save dialog box

ユーザーは、ダイアログを展開できます。The user can expand the dialog:

展開された [保存] ダイアログボックスAn expanded save dialog box

プロパティをに設定した場合は、 ShowSaveAsSheet true アプリケーションを実行し、[ファイル] メニューから [名前を付けて保存... ] を選択すると、次の内容が表示されます。If we set the ShowSaveAsSheet property to true, run the application and select Save As... from the File menu, the following will be displayed:

保存シートA save sheet

ユーザーは、ダイアログを展開できます。The user can expand the dialog:

展開された保存シートAn expanded save sheet

[保存] ダイアログの操作の詳細については、Apple の Nssavepanel のドキュメントを参照してください。For more information on working with the Save Dialog, please see Apple's NSSavePanel documentation.

まとめSummary

この記事では、Xamarin. Mac アプリケーションでのモーダルウィンドウ、シート、および標準システムダイアログボックスの使用方法について詳しく説明しました。This article has taken a detailed look at working with Modal Windows, Sheets and the standard system Dialog Boxes in a Xamarin.Mac application. モーダルウィンドウ、シート、およびダイアログのさまざまな型と用途、Xcode の Interface Builder でモーダルウィンドウとシートを作成して管理する方法、および C# コードでモーダルウィンドウ、シート、およびダイアログを操作する方法について説明しました。We saw the different types and uses of Modal Windows, Sheets and Dialogs, how to create and maintain Modal Windows and Sheets in Xcode's Interface Builder and how to work with Modal Windows, Sheets and Dialogs in C# code.