次の方法で共有


Xamarin.Mac の Windows

この記事では、Xamarin.Mac アプリケーションでのウィンドウとパネルの使用について説明しています。 Xcode と Interface Builder でのウィンドウとパネルの作成、ストーリーボードと .xib ファイルからの読み込み、プログラムによる操作について説明します。

Xamarin.Mac アプリケーションで C# と .NET を使用している場合、Objective-CXcode で開発者が使用しているのと同じ Windows とパネルにアクセスできます。 Xamarin.Mac は直接 Xcode と統合できるため、Xcode の Interface Builder を使用して、Windows とパネルを作成および保守できます (または、必要に応じて C# コードで直接作成することも可能です)。

Xamarin.Mac アプリケーションは、その目的に基づいて、画面に 1 つ以上の Windows を表示して、表示および使用する情報を管理および調整できます。 ウィンドウの主な機能は次のとおりです:

  1. ビューとコントロールを配置および管理できる領域を提供します。
  2. キーボードとマウスの両方を使用したユーザー操作に応答してイベントを受け入れて応答する。

Windows は、モードレス状態 (複数のドキュメントを一度に開くことができるテキスト エディターなど) またはモーダル (アプリケーションを続行する前に閉じる必要がある [エクスポート] ダイアログなど) で使用できます。

パネルは特殊な種類の Window (基本 NSWindow クラスのサブクラス) であり、通常は、テキスト形式インスペクターやシステム Color Picker などのユーティリティ ウィンドウなど、アプリケーションで補助関数を提供します。

Xcode でのウィンドウの編集

この記事では、Xamarin.Mac アプリケーションでの Windows とパネルの操作の基本について説明します。 この記事で使用する主要な概念と手法については、まず Hello Mac の記事、特に「Xcode と Interface Builder の概要」および「アウトレットとアクション」のセクションを参照することを強くお勧めします。

Xamarin.Mac Internals ドキュメントの 「C# クラス/メソッドの Objective-C への公開」のセクションも参照することをお勧めします。C# クラスを Objective-C オブジェクトと UI 要素に結び付けるために使われる Register および Export コマンドについて説明されています。

Windows の概要

前述のように、ウィンドウには、ビューとコントロールを配置して管理し、ユーザーの操作に基づいて (キーボードまたはマウスを使用して) イベントに応答できる領域が用意されています。

Apple によると、macOS アプリには主に 5 種類の Windows があります:

  • ドキュメント ウィンドウ - ドキュメント ウィンドウには、スプレッドシートやテキスト ドキュメントなどのファイル ベースのユーザー データが含まれます。
  • アプリ ウィンドウ - アプリ ウィンドウは、ドキュメント ベースではないアプリケーションのメイン ウィンドウです (Mac のカレンダー アプリなど)。
  • パネル - パネルは他のウィンドウの上に浮動し、ドキュメントを開いている間にユーザーが操作できるツールまたはコントロールを提供します。 パネルが半透明になる場合があります (大きなグラフィックスを操作する場合など)。
  • ダイアログ - ユーザー操作に応答してダイアログが表示され、通常はユーザーがアクションを完了する方法が提供されます。 ダイアログを閉じる前に、ユーザーからの応答が必要です。 (「ダイアログの操作」を参照してください)
  • アラート - アラートは、重大な問題 (エラーなど) が発生したとき、または警告 (ファイルの削除の準備など) として表示される特殊な種類のダイアログです。 アラートはダイアログであるため、閉じる前にユーザーの応答も必要です。 (「アラートの操作」を参照してください)

詳細については、Apple の macOS デザイン テーマの「Windows について」セクションを参照してください。

メイン ウィンドウ、キー ウィンドウ、非アクティブウィンドウ

Xamarin.Mac アプリケーションの Windows の外観と動作は、ユーザーが現在どのように操作しているかに基づいて異なります。 ユーザーの注意を引く最も重要なドキュメント ウィンドウまたはアプリ ウィンドウは、メイン ウィンドウと呼ばれます。 ほとんどの場合、このウィンドウはキー ウィンドウ (現在ユーザー入力を受け入れているウィンドウ) にもなります。 ただし、これは常にそうであるとは限りません。たとえば、Color Picker を開き、ユーザーが操作しているキー ウィンドウを使用して、ドキュメント ウィンドウ (メイン ウィンドウ) 内のアイテムの状態を変更できます。

メイン ウィンドウとキー ウィンドウ (独立している場合) は常にアクティブ、非アクティブな Windows はフォアグラウンドにない開いているウィンドウです。 たとえば、テキスト エディター アプリケーションでは、一度に複数のドキュメントを開くことができ、メイン ウィンドウのみがアクティブになり、それ以外はすべて非アクティブになります。

詳細については、Apple の macOS デザイン テーマの「Windows について」セクションを参照してください。

名前付けウィンドウ

ウィンドウにはタイトル バーを表示できます。タイトルが表示されると、通常、アプリケーション名、作業中のドキュメント名、またはウィンドウの関数 (インスペクターなど) になります。 一部のアプリケーションでは、タイトル バーが表示されないのは、見た目で認識でき、ドキュメントでは動作しないためです。

Apple は、次のガイドラインを推奨しています:

  • メインの、ドキュメント以外のウィンドウのタイトルには、アプリケーション名を使用します。
  • 新しいドキュメント ウィンドウに untitled という名前を付けます。 最初の新しいドキュメントでは、タイトル (untitled 1 など) に数値を追加しないでください。 ユーザーが最初のウィンドウを保存して書き込む前に別の新しいドキュメントを作成した場合は、そのウィンドウ untitled 2untitled 3、などと呼びます。

詳細については、Apple の macOS デザイン テーマの「macOS のデザイン テーマについて」セクションを参照してください。

全画面表示ウィンドウ

macOS では、アプリケーションのウィンドウは、アプリケーション メニュー バー (カーソルを画面の上部に移動することで表示できます) を含むすべてを非表示にして、そのコンテンツを自由に操作できるように全画面表示にすることができます。

Apple は、次のガイドラインを推奨しています:

  • ウィンドウが全画面表示になるのが理にかなっているかどうかを判断します。 短い操作を提供するアプリケーション (電卓など) では、全画面表示モードを提供しないでください。
  • 全画面表示タスクで必要な場合は、ツール バーを表示します。 通常、全画面表示モードではツール バーは非表示になります。
  • 全画面表示ウィンドウには、ユーザーがタスクを完了するために必要なすべての機能が表示されます。
  • 可能であれば、ユーザーが全画面表示ウィンドウにいる間は Finder の操作を避けてください。
  • メイン タスクからフォーカスを移動することなく、大きくなった画面領域を活用します。

詳細については、Apple の macOS デザイン テーマの「全画面表示ウィンドウ」セクションを参照してください。

パネル

パネルは、作業中の文書または選択に影響を与えるコントロールとオプション (システム Color Picker など) を含む補助ウィンドウです:

カラー パネル

パネルは、アプリ固有またはシステム全体のいずれかにすることができます。 アプリ固有のパネルは、アプリケーションのドキュメント ウィンドウの上部に浮動し、アプリケーションがバックグラウンドで表示されると消えます。 システム全体のパネル (フォント パネルなど) は、アプリケーションに関係なく、開いているすべてのウィンドウの上に浮動します。

Apple は、次のガイドラインを推奨しています:

  • 一般に、標準のパネルを使用します。透明パネルは慎重に使用し、グラフィカルに集中的なタスクにのみ使用する必要があります。
  • パネルを使用して、タスクに直接影響を与える重要なコントロールや情報に簡単にアクセスできるようにすることを検討してください。
  • 必要に応じてパネルを非表示にして表示します。
  • パネルには常にタイトル バーを含める必要があります。
  • パネルにはアクティブな最小化ボタンを含めてはなりません。

Inspectors

ほとんどの最新の macOS アプリケーションでは、パネル ウィンドウを使用する代わりに、メイン ウィンドウの一部である Inspectors (次に示す ページ アプリなど) として、作業中のドキュメントまたは選択に影響を与える補助コントロールとオプションが表示されます:

Inspector の例

詳細については、Apple の macOS デザイン テーマの「パネル」セクションを参照してください。

Xcode でのウィンドウの作成と保守

新しい Xamarin.Mac Cocoa アプリケーションを作成すると、既定で標準の空白のウィンドウが表示されます。 このウィンドウは、プロジェクトに自動的に含まれる .storyboard ファイルで定義されます。 Windows デザインを編集するには、ソリューション エクスプローラーで、Main.storyboard ファイルをダブルクリックします:

メイン ストーリーボードの選択

これにより、Xcode の Interface Builder でウィンドウ デザインが開きます:

Xcode での UI の編集

属性インスペクターには、ウィンドウの定義と制御に使用できるプロパティがいくつかあります:

  • Title - ウィンドウのタイトル バーに表示されるテキストです。
  • Autosave - これは、ウィンドウの位置と設定が自動的に保存されるときにウィンドウの ID を設定するために使用されるキーです。
  • Title Bar - ウィンドウにタイトル バーを表示するか。
  • Unified Title and Toolbar - ウィンドウにツール バーが含まれている場合は、そのツール バーがタイトル バーの一部である必要があります。
  • Full Sized Content View - ウィンドウのコンテンツ領域をタイトル バーの下に配置できます。
  • Shadow - ウィンドウに影を付けるか。
  • Textured - テクスチャ ウィンドウは効果 (バイブランシーなど) を使用でき、体の任意の場所をドラッグして移動できます。
  • Close - ウィンドウに閉じるボタンがあるか。
  • Minimize - ウィンドウに最小化ボタンがあるか。
  • Resize - ウィンドウにサイズ変更コントロールがあるか。
  • Toolbar Button - ウィンドウにツール バー ボタンの表示/非表示を切り替えるか。
  • Restorable - ウィンドウの位置と設定が自動的に保存および復元されるか。
  • Visible at Launch - .xib ファイルが読み込まれるときにウィンドウが自動的に表示されるか。
  • Hide On Deactivate - アプリケーションがバックグラウンドに入ったときにウィンドウが非表示になるか。
  • Release When Closed - ウィンドウが閉じられたときにメモリから消去されるか。
  • Always Display Tooltips - ツール ヒントは常に表示されるか。
  • Recalculates View Loop - ウィンドウが描画される前にビューの順序が再計算されるか。
  • SpacesExposéCycling - これらすべてが macOS 環境でのウィンドウの動作を定義します。
  • Full Screen - このウィンドウが全画面表示モードに入ることができるかどうかを決定します。
  • Animation - ウィンドウで使用できるアニメーションの種類を制御します。
  • Appearance - ウィンドウの外観を制御します。 現時点では、Aqua の外観は 1 つだけです。

詳細については、Apple の Windows の概要NSWindow に関するドキュメントを参照してください。

既定のサイズと場所の設定

ウィンドウの初期位置を設定し、ウィンドウのサイズを制御するには、Size Inspector に切り替えます:

既定のサイズと場所

ここから、ウィンドウの初期サイズを設定し、最小および最大サイズを指定し、画面上の初期位置を設定し、ウィンドウの周囲の境界線を制御できます。

カスタム メイン ウィンドウ コントローラーの設定

アウトレットとアクションを作成して UI 要素を C# コードに公開できるようにするには、Xamarin.Mac アプリでカスタム ウィンドウ コントローラーを使用する必要があります。

次の操作を行います。

  1. Xcode の Interface Builder でアプリのストーリーボードを開きます。

  2. デザイン サーフェイスで [NSWindowController] を選択します。

  3. Identity Inspector ビューに切り替え、クラス名として「WindowController」と入力します:

    クラス名の設定

  4. 変更内容を保存し、Visual Studio for Mac に戻って同期します。

  5. WindowController.cs ファイルは、Visual Studio for Mac の ソリューション エクスプローラー のプロジェクトに追加されます:

    ウィンドウ コントローラーの選択

  6. Xcode の Interface Builder でアプリのストーリーボードを再度開きます。

  7. WindowController.h ファイルを使用できるようになります:

    WindowController.h ファイルの編集

UI 要素の追加

ウィンドウの内容を定義するには、ライブラリ インスペクター からインターフェイス エディターにコントロールをドラッグします。 Interface Builder を使用してコントロールを作成および有効にする方法の詳細については、Xcode と Interface Builder の概要に関するドキュメントを参照してください。

例として、ツールバーをライブラリ インスペクタからインターフェイス エディターのウィンドウにドラッグします:

ライブラリからのツールバーの選択

次に、テキスト ビューでドラッグし、ツールバーの下の領域に合わせてサイズを変更します:

テキスト ビューの追加

ウィンドウのサイズの変化に合わせてテキスト ビューを縮小および拡大する必要があるため、制約エディターに切り替えて、次の制約を追加しましょう:

制約の編集

エディターの上部にある 4 つの 赤い I-Beams をクリックし、[4 つの制約の追加] をクリックすると、指定した X、Y 座標に従い、ウィンドウのサイズを変更すると、水平方向と垂直方向に拡大または縮小するようにテキスト ビューに指示します。

最後に、アウトレットを使用して、テキスト ビューをコードに公開します (必ず ViewController.h ファイルを選択してください):

アウトレットの構成

変更を保存し、Visual Studio for Mac に戻って Xcode と同期します。

アウトレットアクションの操作の詳細情報については、アウトレットとアクションに関するドキュメントを参照してください。

標準ウィンドウ ワークフロー

Xamarin.Mac アプリケーションで作成して操作するウィンドウの場合、プロセスは基本的に上記と同じです:

  1. プロジェクトに自動的に追加される既定ではない新しいウィンドウの場合は、新しいウィンドウ定義をプロジェクトに追加します。 これについては、以下の詳細で説明します。
  2. Main.storyboard ファイルをダブルクリックして、Xcode の Interface Builder で編集用のウィンドウ デザインを開きます。
  3. 新しいウィンドウをユーザー インターフェイスのデザインにドラッグし、セグエを使用してウィンドウをメイン ウィンドウにフックします (詳細については、「ストーリーボードの操作」に関するドキュメントの「セグエ」セクションを参照してください)。
  4. 属性インスペクターサイズ インスペクターで、必要なウィンドウ プロパティを設定します。
  5. インターフェイスを構築するために必要なコントロールをドラッグし、それらを属性インスペクターで構成します。
  6. [サイズ インスペクター] を使用して、UI 要素のサイズ変更を処理します。
  7. アウトレットアクションを使用して、ウィンドウの UI 要素を C# コードに公開します。
  8. 変更を保存し、Visual Studio for Mac に戻って Xcode と同期します。

基本的なウィンドウが作成されたので、Xamarin.Mac アプリケーションがウィンドウを操作するときに行う一般的なプロセスを見ていきます。

既定のウィンドウの表示

既定では、新しい Xamarin.Mac アプリケーションの起動時に、MainWindow.xib ファイルで定義されているウィンドウが自動的に表示されます:

実行中のウィンドウの例

上のウィンドウのデザインを変更したので、既定のツール バーとテキスト ビュー コントロールが含まれるようになりました。 Info.plist ファイルの次のセクションでは、このウィンドウを表示します:

Info.plist の編集

[メイン インターフェイス] ドロップダウンは、メイン アプリ UI として使用されるストーリーボード (この場合は Main.storyboard) を選択するために使用されます。

ビュー コントローラーがプロジェクトに自動的に追加され、メイン ウィンドウが表示される (プライマリ ビューと共に) 制御されます。 これは、ViewController.cs ファイルで定義され、Interface Builder の Identity Inspector の下にある ファイルの所有者にアタッチされます:

ファイルの所有者の設定

このウィンドウでは、最初に開いたときに untitled のタイトルを付けたいので、ViewController.csViewWillAppear メソッドをオーバーライドして、次のようにします:

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

    // Set Window Title
    this.View.Window.Title = "untitled";
}

Note

ウィンドウの Title プロパティは、ViewDidLoad メソッドではなく ViewWillAppear メソッドで設定されます。これは、ビューがメモリに読み込まれる可能性がある一方で、まだ完全にはインスタンス化されていないためです。 ViewDidLoad メソッドの Title プロパティにアクセスすると、ウィンドウがまだ構築されておらず、プロパティに関連付けられていないため、null 例外が発生します。

プログラムによってウィンドウを閉じる

Xamarin.Mac アプリケーションでは、ユーザーがウィンドウの [閉じる] ボタンをクリックしたり、メニュー項目を使用したりする以外に、プログラムでウィンドウを閉じる場合があります。 macOS には、NSWindow をプログラムで閉じる方法として、PerformCloseClose の 2 種類があります。

PerformClose

NSWindowPerformClose メソッドを呼び出すと、ボタンを一時的に強調表示してからウィンドウを閉じることで、ユーザーがウィンドウの [閉じる] ボタンをクリックすることをシミュレートします。

アプリケーションが NSWindowWillClose イベントを実装している場合、ウィンドウが閉じられる前に発生します。 イベントが false を返した場合、ウィンドウは閉じません。 ウィンドウに [閉じる] ボタンがない場合、または何らかの理由で閉じることができない場合、OS はアラートを出力します。

次に例を示します。

MyWindow.PerformClose(this);

MyWindowNSWindow インスタンスを閉じようとします。 それが成功した場合、ウィンドウは閉じられ、それ以外の場合はアラート音が出力され、開いたままになります。

NSWindowClose メソッドを呼び出しても、ユーザーがボタンを一時的に強調表示してウィンドウの [閉じる] ボタンをクリックしてもシミュレートされず、ウィンドウが閉じられます。

ウィンドウを閉じるにはウィンドウを表示する必要はありません。NSWindowWillCloseNotification 通知は、閉じているウィンドウの既定の通知センターに投稿されます。

Close メソッドは、PerformClose メソッドとは 2 つの重要な方法で異なります:

  1. WillClose イベントの発生は試行されません。
  2. ボタンを一時的に強調表示することで、 [閉じる] ボタンをクリックしたユーザーはシミュレートされません。

次に例を示します。

MyWindow.Close();

MyWindowNSWindow インスタンスを閉じます。

変更されたウィンドウのコンテンツ

macOS では、Apple は、ウィンドウ (NSWindow) の内容がユーザーによって変更され、保存する必要があることをユーザーに通知する方法を提供しました。 ウィンドウに変更されたコンテンツが含まれている場合、小さな黒いドットが "閉じる" ウィジェットに表示されます:

変更されたマーカーが表示されたウィンドウ

ユーザーがウィンドウのコンテンツに未保存の変更がある間にウィンドウを閉じたり、Mac アプリを終了しようとした場合は、ダイアログ ボックスまたはモーダル シートを表示し、ユーザーが最初に変更を保存できるようにする必要があります:

ウィンドウが閉じられたときに表示される保存シート

ウィンドウを変更済みとしてマークする

ウィンドウに変更されたコンテンツを含むものとしてマークするには、次のコードを使用します:

// Mark Window content as modified
Window.DocumentEdited = true;

変更が保存されたら、以下を使用して変更されたフラグをクリアします:

// Mark Window content as not modified
Window.DocumentEdited = false;

ウィンドウを閉じる前に変更を保存する

ユーザーがウィンドウを閉じるのを監視し、変更されたコンテンツを事前に保存できるようにするには、NSWindowDelegate のサブクラスを作成し、その WindowShouldClose メソッドをオーバーライドする必要があります。 次に例を示します。

using System;
using AppKit;
using System.IO;
using Foundation;

namespace SourceWriter
{
    public class EditorWindowDelegate : NSWindowDelegate
    {
        #region Computed Properties
        public NSWindow Window { get; set;}
        #endregion

        #region constructors
        public EditorWindowDelegate (NSWindow window)
        {
            // Initialize
            this.Window = window;

        }
        #endregion

        #region Override Methods
        public override bool WindowShouldClose (Foundation.NSObject sender)
        {
            // is the window dirty?
            if (Window.DocumentEdited) {
                var alert = new NSAlert () {
                    AlertStyle = NSAlertStyle.Critical,
                    InformativeText = "Save changes to document before closing window?",
                    MessageText = "Save Document",
                };
                alert.AddButton ("Save");
                alert.AddButton ("Lose Changes");
                alert.AddButton ("Cancel");
                var result = alert.RunSheetModal (Window);

                // Take action based on result
                switch (result) {
                case 1000:
                    // Grab controller
                    var viewController = Window.ContentViewController as ViewController;

                    // Already saved?
                    if (Window.RepresentedUrl != null) {
                        var path = Window.RepresentedUrl.Path;

                        // Save changes to file
                        File.WriteAllText (path, viewController.Text);
                        return true;
                    } else {
                        var dlg = new NSSavePanel ();
                        dlg.Title = "Save Document";
                        dlg.BeginSheet (Window, (rslt) => {
                            // File selected?
                            if (rslt == 1) {
                                var path = dlg.Url.Path;
                                File.WriteAllText (path, viewController.Text);
                                Window.DocumentEdited = false;
                                viewController.View.Window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
                                viewController.View.Window.RepresentedUrl = dlg.Url;
                                Window.Close();
                            }
                        });
                        return true;
                    }
                    return false;
                case 1001:
                    // Lose Changes
                    return true;
                case 1002:
                    // Cancel
                    return false;
                }
            }

            return true;
        }
        #endregion
    }
}

このデリゲートのインスタンスをウィンドウにアタッチするには、次のコードを使用します:

// Set delegate
Window.Delegate = new EditorWindowDelegate(Window);

ウィンドウを閉じる前に変更を保存する

最後に、Xamarin.Mac アプリで、Windows に変更されたコンテンツが含まれているかどうかを確認し、終了する前にユーザーが変更を保存できるようにします。 これを行うには、AppDelegate.cs ファイルを編集し、ApplicationShouldTerminate メソッドをオーバーライドして、次のようにします:

public override NSApplicationTerminateReply ApplicationShouldTerminate (NSApplication sender)
{
    // See if any window needs to be saved first
    foreach (NSWindow window in NSApplication.SharedApplication.Windows) {
        if (window.Delegate != null && !window.Delegate.WindowShouldClose (this)) {
            // Did the window terminate the close?
            return NSApplicationTerminateReply.Cancel;
        }
    }

    // Allow normal termination
    return NSApplicationTerminateReply.Now;
}

複数のウィンドウの操作

ほとんどのドキュメント ベースの Mac アプリケーションでは、複数のドキュメントを同時に編集できます。 たとえば、テキスト エディターでは、複数のテキスト ファイルを同時に開いて編集できます。 既定では、新しい Xamarin.Mac アプリケーションには [ファイル] メニューがあり、[新規] 項目は自動的にnewDocument:アクションに関連付けられます。

下記のコードでは、この新しい項目をアクティブ化し、ユーザーがメイン ウィンドウの複数のコピーを開いて複数のドキュメントを一度に編集できるようにします。

AppDelegate.cs ファイルを編集し、次の計算プロパティを追加します:

public int UntitledWindowCount { get; set;} =1;

これを使用して、保存されていないファイルの数を追跡して、ユーザーにフィードバックを送信できるようにします (前述の Apple のガイドラインに従います)。

次に、以下のメソッドを追加します:

[Export ("newDocument:")]
void NewDocument (NSObject sender) {
    // Get new window
    var storyboard = NSStoryboard.FromName ("Main", null);
    var controller = storyboard.InstantiateControllerWithIdentifier ("MainWindow") as NSWindowController;

    // Display
    controller.ShowWindow(this);

    // Set the title
    controller.Window.Title = (++UntitledWindowCount == 1) ? "untitled" : string.Format ("untitled {0}", UntitledWindowCount);
}

このコードは、新しいバージョンのウィンドウ コントローラーを作成し、新しいウィンドウを読み込み、メイン ウィンドウとキー ウィンドウにして、タイトルを設定します。 アプリケーションを実行し、[ファイル] メニューから [新規] を選択すると、新しいエディター ウィンドウが開き、表示されます:

追加された新しい無題のウィンドウ

Windows メニューを開くと、アプリケーションが自動的に追跡され、開いているウィンドウを処理していることがわかります:

[ウィンドウ] メニュー

Xamarin.Mac アプリケーションでのメニューの操作の詳細については、メニューの操作に関するドキュメントを参照してください。

現在アクティブなウィンドウの取得

複数のウィンドウ (ドキュメント) を開くことができる Xamarin.Mac アプリケーションでは、現在の最上位ウィンドウ (キー ウィンドウ) を取得する必要がある場合があります。 次のコードは、キー ウィンドウを返します:

var window = NSApplication.SharedApplication.KeyWindow;

現在のキー ウィンドウにアクセスする必要がある任意のクラスまたはメソッドで呼び出すことができます。 ウィンドウが現在開かっていない場合は、null が返されます。

すべてのアプリ ウィンドウへのアクセス

Xamarin.Mac アプリが現在開いているすべてのウィンドウにアクセスする必要がある場合があります。 たとえば、ユーザーが開きたいファイルが既に終了ウィンドウで開いているかどうかを確認します。

NSApplication.SharedApplication は、アプリで開いているすべてのウィンドウの配列を含む Windows プロパティを保持します。 この配列を反復処理して、アプリのすべての現在のウィンドウにアクセスできます。 次に例を示します。

// Is the file already open?
for(int n=0; n<NSApplication.SharedApplication.Windows.Length; ++n) {
    var content = NSApplication.SharedApplication.Windows[n].ContentViewController as ViewController;
    if (content != null && path == content.FilePath) {
        // Bring window to front
        NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);
        return true;
    }
}

コード例では、返された各ウィンドウをアプリのカスタム ViewController クラスにキャストし、ユーザーが開くファイルのパスに対してカスタム Path プロパティの値をテストしています。 ファイルが既に開いている場合は、そのウィンドウを前面に移動します。

コードでのウィンドウ サイズの調整

アプリケーションでコード内のウィンドウのサイズを変更する必要がある場合があります。 ウィンドウのサイズと位置を変更するには、 Frame プロパティを調整します。 通常、ウィンドウのサイズを調整するときは、macOS の座標系のためにウィンドウを同じ場所に維持するために、元の位置も調整する必要があります。

左上隅が (0,0) を表す iOS とは異なり、macOS では、画面の左下隅が (0,0) を表す数学座標系が使用されます。 iOS では、右に向かって下に移動すると座標が増加します。 macOS では、座標の値が右に上向きに増加します。

次のコード例では、ウィンドウのサイズを変更します:

nfloat y = 0;

// Calculate new origin
y = Frame.Y - (768 - Frame.Height);

// Resize and position window
CGRect frame = new CGRect (Frame.X, y, 1024, 768);
SetFrame (frame, true);

重要

コードでウィンドウのサイズと場所を調整する場合は、Interface Builder で設定した最小サイズと最大サイズを考慮する必要があります。 これは自動的には受け入れられないので、ウィンドウをこれらの制限より大きくまたは小さくすることができます。

ウィンドウ サイズの変更の監視

Xamarin.Mac アプリ内でウィンドウのサイズの変更を監視する必要がある場合があります。 たとえば、新しいサイズに合わせてコンテンツを再描画する場合です。

サイズの変更を監視するには、まず、Xcode の Interface Builder でウィンドウ コントローラーのカスタム クラスが割り当てられていることを確認します。 たとえば、次の MasterWindowController:

Identity Inspector

次に、カスタム ウィンドウ コントローラー クラスを編集し、コントローラーのウィンドウで DidResize イベントを監視して、ライブ サイズの変更を通知します。 次に例を示します。

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

    Window.DidResize += (sender, e) => {
        // Do something as the window is being live resized
    };
}

必要に応じて、DidEndLiveResize イベントを使用して、ユーザーがウィンドウのサイズの変更を完了後にのみ通知を受け取ることができます。 例:

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

        Window.DidEndLiveResize += (sender, e) => {
        // Do something after the user's finished resizing
        // the window
    };
}

ウィンドウのタイトルと表されるファイルの設定

ドキュメントを表すウィンドウを操作する場合、NSWindow には DocumentEdited プロパティがあります。true に設定すると、[閉じる] ボタンに小さなドットが表示され、ユーザーはファイルが変更され、閉じる前に保存する必要があることを示します。

ViewController.cs ファイルを編集し、次の変更を行います:

public bool DocumentEdited {
    get { return View.Window.DocumentEdited; }
    set { View.Window.DocumentEdited = value; }
}
...

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

    // Set Window Title
    this.View.Window.Title = "untitled";

    View.Window.WillClose += (sender, e) => {
        // is the window dirty?
        if (DocumentEdited) {
            var alert = new NSAlert () {
                AlertStyle = NSAlertStyle.Critical,
                InformativeText = "We need to give the user the ability to save the document here...",
                MessageText = "Save Document",
            };
            alert.RunModal ();
        }
    };
}

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

    // Show when the document is edited
    DocumentEditor.TextDidChange += (sender, e) => {
        // Mark the document as dirty
        DocumentEdited = true;
    };

    // Overriding this delegate is required to monitor the TextDidChange event
    DocumentEditor.ShouldChangeTextInRanges += (NSTextView view, NSValue[] values, string[] replacements) => {
        return true;
    };

}

また、ウィンドウ上の WillClose イベントを監視し、 DocumentEdited プロパティの状態を確認しています。 true の場合は、変更をファイルに保存する機能をユーザーに付与する必要があります。 アプリを実行してテキストを入力すると、ドットが表示されます:

変更されたウィンドウ

ウィンドウを閉じようとすると、アラートが表示されます:

保存ダイアログの表示

ファイルからドキュメントを読み込む場合は、window.SetTitleWithRepresentedFilename (Path.GetFileName(path)); メソッドを使用してウィンドウのタイトルをファイルの名前に設定します (path が開かれているファイルを表す文字列である場合)。 さらに、window.RepresentedUrl = url; メソッドを使用してファイルの URL を設定できます。

URL が OS で認識されているファイルの種類を指している場合、そのアイコンがタイトル バーに表示されます。 ユーザーがアイコンを右クリックすると、ファイルへのパスが表示されます。

AppDelegate.cs ファイルを編集し、次のメソッドを追加します:

[Export ("openDocument:")]
void OpenDialog (NSObject sender)
{
    var dlg = NSOpenPanel.OpenPanel;
    dlg.CanChooseFiles = true;
    dlg.CanChooseDirectories = false;

    if (dlg.RunModal () == 1) {
        // Nab the first file
        var url = dlg.Urls [0];

        if (url != null) {
            var path = url.Path;

            // Get new window
            var storyboard = NSStoryboard.FromName ("Main", null);
            var controller = storyboard.InstantiateControllerWithIdentifier ("MainWindow") as NSWindowController;

            // Display
            controller.ShowWindow(this);

            // Load the text into the window
            var viewController = controller.Window.ContentViewController as ViewController;
            viewController.Text = File.ReadAllText(path);
                    viewController.View.Window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
            viewController.View.Window.RepresentedUrl = url;

        }
    }
}

アプリを実行する場合、[ファイル] メニューから [開く] を選択し、[開く] ダイアログ ボックスからテキスト ファイルを選択して開きます:

[開く] ダイアログ ボックス

ファイルが表示され、タイトルがファイルのアイコンで設定されます:

読み込まれたファイルの内容

プロジェクトへの新しいウィンドウの追加

メイン ドキュメント ウィンドウとは別に、Xamarin.Mac アプリケーションでは、環境設定やインスペクター パネルなど、他の種類のウィンドウをユーザーに表示することが必要になる場合があります。

新しいウィンドウを追加するには、次の操作を行います:

  1. ソリューション エクスプローラーで、Main.storyboard ファイルをダブルクリックして Xcode の Interface Builder で編集するために開きます。

  2. ライブラリから新しいウィンドウ コントローラーをドラッグし、[デザイン サーフェイス] にドロップします:

    ライブラリでの新しいウィンドウ コントローラーの選択

  3. ID インスペクターで、ストーリーボード IDPreferencesWindow を入力します:

    ストーリーボード ID の設定

  4. インターフェイスを設計する:

    UI の設定

  5. [アプリ] メニュー (MacWindows) を開き、[ユーザー設定...] を選択し、Control キーを押しながらクリックし、新しいウィンドウにドラッグします:

    セグエの作成

  6. ポップアップ メニュー [表示] を選択します。

  7. 変更内容を保存し、Visual Studio for Mac に戻って Xcode と同期します。

コードを実行し、[アプリケーション メニュー] から [ユーザー設定...] を選択すると、ウィンドウが表示されます:

[ユーザー設定] メニューのサンプル

パネルの操作

この記事の冒頭で説明したように、パネルは他のウィンドウの上に浮動し、ドキュメントを開いている間にユーザーが操作できるツールまたはコントロールを提供します。

Xamarin.Mac アプリケーションで作成して操作する他の種類のウィンドウと同様に、プロセスは基本的に同じです:

  1. 新しいウィンドウ定義をプロジェクトに追加します。
  2. .xib ファイルをダブルクリックして、Xcode の Interface Builder で編集用のウィンドウ デザインを開きます。
  3. 属性インスペクターサイズ インスペクターで、必要なウィンドウ プロパティを設定します。
  4. インターフェイスを構築するために必要なコントロールをドラッグし、それらを属性インスペクターで構成します。
  5. [サイズ インスペクター] を使用して、UI 要素のサイズ変更を処理します。
  6. アウトレットアクションを使用して、ウィンドウの UI 要素を C# コードに公開します。
  7. 変更を保存し、Visual Studio for Mac に戻って Xcode と同期します。

属性インスペクターには、パネルに固有の次のオプションがあります:

属性インスペクター

  • スタイル - 標準パネル(標準ウィンドウのように見えます)、ユーティリティ パネル (小さいタイトル バーを持つ)、HUD パネル (半透明で、タイトルバーは背景の一部) からパネルのスタイルを調整できます。
  • 非アクティブ化 - パネルでキー ウィンドウになることを決定します。
  • ドキュメント モーダル - ドキュメント モーダルの場合、パネルはアプリケーションのウィンドウの上にのみ浮動し、それ以外の場合は何よりも上に浮動します。

新しいパネルを追加するには、次の操作を行います:

  1. ソリューション エクスプローラーで、[プロジェクト] 上で右クリックし、[追加]>[新しいファイル...] を選択します。

  2. [新しいファイル] ダイアログ ボックスで、[Xamarin.Mac]>[Cocoa Window wth Contoller] の順に選択します:

    新しいウィンドウ コントローラーの追加

  3. [名前] に「DocumentPanel」と入力し、[新規] ボタンをクリックします。

  4. DocumentPanel.xib ファイルをダブルクリックして、Interface Builder で編集用に開きます:

    パネルの編集

  5. 既存のウィンドウを削除し、インターフェイス エディターライブラリ インスペクターからパネルをドラッグします:

    既存のウィンドウの削除

  6. パネルをファイルの所有者 - ウィンドウ - アウトレットに接続します:

    パネルをドラッグして接続

  7. Identity Inspector に切り替え、パネルのクラスを DocumentPanel に設定します:

    パネルのクラスの設定

  8. 変更内容を保存し、Visual Studio for Mac に戻って Xcode と同期します。

  9. DocumentPanel.cs ファイルを編集し、クラス定義を次のように変更します:

    public partial class DocumentPanel : NSPanel

  10. 変更をファイルに保存します。

AppDelegate.cs ファイルを編集し、DidFinishLaunching メソッドを次のようにします:

public override void DidFinishLaunching (NSNotification notification)
{
        // Display panel
    var panel = new DocumentPanelController ();
    panel.Window.MakeKeyAndOrderFront (this);
}

アプリケーションを実行すると、パネルが表示されます:

実行中のアプリのパネル

重要

パネル ウィンドウは Apple によって非推奨とされ、インスペクター インターフェイスに置き換える必要があります。

まとめ

この記事では、Xamarin.Mac アプリケーションでの Windows とパネルの操作について詳しく説明しました。 Windows とパネルのさまざまな種類と用途、Xcode の Interface Builder で Windows とパネルを作成および維持する方法、および C# コードで Windows とパネルを操作する方法について説明しました。