Xamarin.Mac 中的 Windows

本文涵蓋在 Xamarin.Mac 應用程式中使用視窗和面板。 它描述在 Xcode 和 Interface Builder 中建立視窗和面板、從分鏡腳本和 .xib 檔案載入視窗和面板,並以程式設計方式加以使用。

在 Xamarin.Mac 應用程式中使用 C# 和 .NET 時,您可以存取開發人員在 和 XcodeObjective-C運作的相同 Windows 和面板。 由於 Xamarin.Mac 直接與 Xcode 整合,因此您可以使用 Xcode 的 Interface Builder 來建立和維護您的 Windows 和面板(或選擇性地直接在 C# 程式代碼中建立它們)。

根據其用途,Xamarin.Mac 應用程式可以在畫面上呈現一或多個 Windows,以管理和協調其顯示及使用的資訊。 視窗的主要函式如下:

  1. 提供可放置及管理檢視和控件的區域。
  2. 若要接受和回應事件,以回應使用者與鍵盤和滑鼠的互動。

Windows 可以處於無模式狀態(例如可以一次開啟多個檔的文字編輯器)或強制回應(例如,在應用程式可以繼續之前必須關閉的導出對話框)。

面板是一種特殊的 Window(基 NSWindow 類的子類別),通常會在應用程式中提供輔助功能,例如文字格式偵測器和系統色彩選擇器等公用程序視窗。

在 Xcode 中編輯視窗

在本文中,我們將討論在 Xamarin.Mac 應用程式中使用 Windows 和面板的基本概念。 強烈建議您先完成 Hello,Mac 文章,特別是 Xcode 和 Interface Builder 和 Outlets 和 Actions 簡介小節,因為它涵蓋我們將在本文中使用的重要概念和技術。

您可能也想要查看 Xamarin.Mac Internals 檔的公開 C# 類別/方法Objective-C一節,它也會說明 Register 用來將 C# 類別連接至Objective-C物件和 UI 元素的 和 Export 命令。

視窗簡介

如上所述,Window 提供一個區域,其中檢視和控件可以根據用戶互動(透過鍵盤或滑鼠)來放置及管理及回應事件。

根據 Apple 的說法,macOS 應用程式中有五種主要的 Windows 類型:

  • 文件視窗 - 文件視窗 包含檔案型用戶數據,例如電子錶格或文字檔。
  • 應用程式視窗 - 應用程式視窗是非檔案型應用程式的主要視窗(例如 Mac 上的行事曆應用程式)。
  • 面板 - 面板會浮動在其他視窗上方,並提供使用者可在開啟檔時使用的工具或控件。 在某些情況下,面板可以是半透明(例如使用大型圖形時)。
  • 對話框 - 對話框隨即出現,以回應使用者動作,且通常會提供使用者完成動作的方式。 對話框需要用戶回應,才能關閉。 (請參閱 使用對話框
  • 警示 - 警示是特殊類型的對話框,會在發生嚴重問題時出現(例如錯誤)或警告(例如準備刪除檔案)。 因為警示是對話框,所以也需要用戶回應才能關閉。 (請參閱 使用警示

如需詳細資訊,請參閱 Apple macOS 設計主題About Windows 一節。

主要、索引鍵和非使用中的視窗

Xamarin.Mac 應用程式中的 Windows 可以根據使用者目前與其互動的方式,以不同的方式外觀和行為。 目前使用者關注焦點的最重要檔或應用程式視窗稱為 主視窗。 在大部分情況下,此視窗也會是 [索引鍵視窗 ] (目前接受使用者輸入的視窗)。 但這種情況不一定如此,例如,色彩選擇器可以開啟,而且是使用者正在與之互動的索引鍵視窗,以變更文檔視窗中專案的狀態(這仍然是主視窗)。

Main 和 Key Windows(如果個別的視窗)一律為作用中, 非作用中的 Windows 是不在前景的視窗。 例如,文本編輯器應用程式一次可以開啟一個以上的檔,只有主視窗會使用中,所有其他檔都會處於非使用中狀態。

如需詳細資訊,請參閱 Apple macOS 設計主題About Windows 一節。

命名視窗

視窗可以顯示標題列,而且當標題顯示時,通常是應用程式的名稱、正在處理的檔名稱或視窗的函式(例如 Inspector)。 某些應用程式不會顯示標題列,因為它們可透過視線辨識,而且無法與檔搭配使用。

Apple 建議下列指導方針:

  • 使用您的應用程式名稱作為主要非文檔視窗的標題。
  • 將新的文件視窗 untitled命名為 。 對於第一份新檔,請勿將數位附加至標題(例如 untitled 1)。 如果使用者在儲存第一個檔之前建立另一個新檔,請呼叫該視窗 untitled 2untitled 3等。

如需詳細資訊,請參閱Apple macOS設計主題命名Windows 一節。

全螢幕視窗

在macOS中,應用程式的視窗可以全螢幕隱藏所有內容,包括應用程式功能表欄(可藉由將游標移至螢幕頂端來顯示),以提供與內容分心的免費互動。

Apple 建議下列指導方針:

  • 判斷視窗是否適合全螢幕。 提供簡短互動的應用程式(例如計算機)不應該提供全螢幕模式。
  • 如果全螢幕工作需要工具列,則顯示工具列。 一般而言,在全螢幕模式中隱藏工具列。
  • 全螢幕視窗應該具有使用者完成工作所需的所有功能。
  • 可能的話,請避免使用者在全螢幕視窗中進行 Finder 互動。
  • 利用增加的螢幕空間,而不將焦點從主要工作移開。

如需詳細資訊,請參閱Apple macOS設計主題全螢幕 Windows 一節。

窗格

面板是一個輔助視窗,其中包含會影響使用中檔或選取範圍的控件和選項(例如系統色彩選擇器):

色彩面板

面板可以是 應用程式特定全系統。 應用程式特定面板會浮動在應用程式文檔視窗的頂端,並在應用程式處於背景時消失。 全系統面板(例如 型面板),無論應用程式為何,都會浮動在所有開啟的視窗上。

Apple 建議下列指導方針:

  • 一般而言,使用標準面板時,透明面板應該只謹慎使用,並用於圖形密集的工作。
  • 請考慮使用面板讓用戶輕鬆存取直接影響其工作的重要控件或資訊。
  • 視需要隱藏和顯示面板。
  • 面板應該一律包含標題列。
  • 面板不應包含作用中的最小化按鈕。

督察

大部分的新式 macOS 應用程式都會呈現會影響使用中檔或選取項目的輔助控件和選項,做為 主視窗的一部分的偵測器 (例如如下所示的 Pages 應用程式),而不是使用 Panel Windows:

範例偵測器

如需詳細資訊,請參閱Apple macOS設計主題面板一節。

在 Xcode 中建立和維護視窗

當您建立新的 Xamarin.Mac Cocoa 應用程式時,預設會取得標準空白視窗。 這個視窗會在項目中自動包含的檔案中定義 .storyboard 。 若要編輯您的視窗設計,請在 方案總管 中按兩下Main.storyboard檔案:

選取主分鏡腳本

這會在 Xcode 的 Interface Builder 中開啟視窗設計:

在 Xcode 中編輯 UI

在屬性 偵測器中,有數個屬性可用來定義及控制視窗:

  • 標題 - 這是將在視窗標題列中顯示的文字。
  • 自動儲存 - 這是 當視窗的位置和設定自動儲存時,用來識別元視窗的索引鍵
  • 標題列 - 視窗是否顯示標題列。
  • 統一標題和工具列 - 如果視窗包含工具列 ,則應該是標題列的一部分。
  • 完整大小的內容檢視 - 允許視窗的內容區域在標題列下。
  • 陰影 - 視窗是否有陰影。
  • 紋理 - 紋理視窗可以使用效果(如活力),並可透過拖曳其身體上的任何位置來移動。
  • 關閉 -視窗是否有關閉按鈕。
  • 最小化 - 視窗是否有最小化按鈕。
  • 重設大小 - 視窗是否有重設大小控制件。
  • 工具列按鈕 - 視窗是否有隱藏/顯示工具列按鈕。
  • 還原 - 視窗的位置和設定會自動儲存和還原。
  • 在啟動時 可見 - 載入檔案時 .xib 會自動顯示視窗。
  • 在停用 時隱藏 - 當應用程式進入背景時,視窗會隱藏。
  • 關閉 時釋放 - 視窗是否在關閉時從記憶體中清除。
  • 永遠顯示工具提示 - 工具提示是否持續顯示。
  • 重新計算檢視迴圈 - 這是在繪製視窗之前重新計算的檢視順序。
  • 空格公開迴圈 - 所有都會定義視窗在這些 macOS 環境中的行為方式。
  • 全螢幕 - 判斷此視窗是否可以進入全螢幕模式。
  • 動畫 -控制視窗可用的動畫類型。
  • 外觀 -控制窗口的外觀。 目前只有一個外觀,水。

如需詳細資訊,請參閱Apple 的 WindowsNSWindow 簡介檔。

設定預設大小和位置

若要設定視窗的初始位置並控制其大小,請切換至 [大小偵測器]:

默認大小和位置

您可以從這裡設定視窗的初始大小、提供最小和大小上限、在畫面上設定初始位置,以及控制視窗周圍的框線。

設定自定義主視窗控制器

若要能夠建立輸出和動作,將UI元素公開至 C# 程式代碼,Xamarin.Mac 應用程式必須使用自定義視窗控制器。

執行下列操作:

  1. 在 Xcode 的 Interface Builder 中開啟應用程式的分鏡腳本。

  2. NSWindowController在[設計介面] 中選取 。

  3. 切換至 [ 身分識別偵測器 ] 檢視,然後輸入 WindowController 作為 [類別名稱]:

    設定類別名稱

  4. 儲存變更並返回 Visual Studio for Mac 進行同步處理。

  5. WindowController.cs Visual Studio for Mac 中的 方案總管 中,將會將檔案新增至您的 Project

    選取 Windows 控制器

  6. 在 Xcode 的介面產生器中重新開啟分鏡腳本。

  7. 檔案 WindowController.h 將可供使用:

    編輯 WindowController.h 檔案

新增UI元素

若要定義視窗的內容,請將控件從 [鏈接庫偵測器 ] 拖曳至 [介面編輯器]。 如需使用介面產生器建立和啟用控件的詳細資訊,請參閱 Xcode 和 Interface Builder 簡介檔。

例如,讓我們將工具列從連結庫偵測器拖曳到介面編輯器中的視窗:

從文檔庫選取工具列

接下來,拖曳文字 檢視 並調整其大小,以填滿工具欄底下的區域:

新增文字檢視

由於我們希望 文字檢視 隨著視窗大小變更而縮小和成長,因此讓我們切換至 [條件約束編輯器 ],並新增下列條件約束:

編輯條件約束

按兩下編輯器頂端的四 個紅色 I-Beam, 然後按兩下 [新增4個條件約束],我們會告訴文字檢視要堅持指定的 X、Y 座標,並在視窗重設大小時水準和垂直放大或縮小。

最後,使用輸出將文字檢視公開為程式代碼(請務必選取檔案ViewController.h):

設定輸出

儲存變更並切換回 Visual Studio for Mac 以與 Xcode 同步。

如需使用輸出和動作的詳細資訊,請參閱我們的輸出和動作檔。

標準視窗工作流程

對於您在 Xamarin.Mac 應用程式中建立及使用的任何視窗,此程式基本上與我們先前所做的相同:

  1. 對於非預設新增至專案的新視窗,請將新的視窗定義新增至專案。 以下將詳細討論這一點。
  2. 按兩下 Main.storyboard 檔案,開啟在 Xcode 介面產生器中編輯的窗口設計。
  3. 將新的視窗拖曳至使用者介面的設計,並使用 Segues 將視窗連結至主視窗(如需詳細資訊,請參閱使用分鏡腳本檔的 Segues 一節)。
  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並附加至身分識別偵測器介面產生器的檔案擁有者

設定檔案的擁有者

針對我們的窗口,我們想要在它第一次開啟時擁有的 untitled 標題,因此讓我們覆寫 ViewWillAppear 中的 ViewController.cs 方法,如下所示:

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

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

注意

視窗的 Title 屬性是在 方法中 ViewWillAppear 設定,而不是 ViewDidLoad 方法,因為雖然檢視可能會載入記憶體中,但尚未完全具現化。 在 Title 方法中 ViewDidLoad 存取 屬性時,我們將會收到 null 例外狀況,因為視窗尚未建構並連到 屬性。

以程式設計方式關閉視窗

您可能想要以程式設計方式關閉 Xamarin.Mac 應用程式中的視窗,而不是讓使用者按兩下視窗的 [關閉 ] 按鈕或使用選單項。 macOS 提供兩種不同的方式,以程式設計方式關閉 NSWindowPerformCloseClose

PerformClose

PerformClose呼叫 的 方法NSWindow會模擬使用者按兩下視窗的 [關閉] 按鈕,方法是暫時反白顯示按鈕,然後關閉視窗。

如果應用程式實作 NSWindowWillClose 事件,則會在關閉視窗之前引發它。 如果事件傳 false回 ,則不會關閉視窗。 如果視窗沒有 [關閉 ] 按鈕或因任何原因而無法關閉,則OS會發出警示音效。

例如:

MyWindow.PerformClose(this);

會嘗試關閉 MyWindowNSWindow 實例。 如果成功,視窗將會關閉,否則會發出警示音效,且 會保持開啟狀態。

關閉

Close呼叫 的 方法NSWindow不會模擬使用者按兩下視窗的 [關閉] 按鈕,方法是暫時醒目提示按鈕,而只會關閉視窗。

視窗不需要顯示關閉,而且 NSWindowWillCloseNotification 通知會張貼到關閉視窗的默認通知中心。

方法 Close 與 方法不同,有兩個重要方式 PerformClose

  1. 它不會嘗試引發 WillClose 事件。
  2. 它不會模擬使用者按兩下 [關閉 ] 按鈕,方法是暫時醒目提示按鈕。

例如:

MyWindow.Close();

會關閉 MyWindowNSWindow 實例。

修改過的窗口內容

在macOS中,Apple提供了一種方式來通知使用者 Window (NSWindow) 的內容已由使用者修改,而且需要儲存。 如果 Window 包含修改的內容,則會在 [ 關閉 ] 小工具中顯示一個小黑點:

具有修改標記的視窗

如果使用者嘗試關閉視窗或結束 Mac 應用程式,而視窗內容有未儲存的變更,您應該先顯示 對話框強制回應表 ,並允許使用者先儲存變更:

關閉視窗時顯示的儲存工作表

將視窗標示為已修改

若要將 Window 標示為已修改的內容,請使用下列程式代碼:

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

一旦儲存變更之後,請使用下列專案清除已修改的旗標:

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

關閉視窗之前儲存變更

若要監看使用者關閉 Window 並允許他們事先儲存修改的內容,您必須建立 的 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 應用程式內 Window 大小的變更。 例如,若要重繪內容以符合新的大小。

若要監視大小變更,請先確定您已在 Xcode 的 Interface Builder 中指派視窗控制器的自定義類別。 例如, MasterWindowController 在下列內容中:

身分識別偵測器

接下來,編輯自定義的 Window 控制器類別,並監視 DidResize 控制器視窗上的事件,以通知即時大小變更。 例如:

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

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

您可以選擇性地使用 DidEndLiveResize 事件,只有在使用者完成變更 Window 的大小之後,才會收到通知。 例如:

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

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

設定視窗的標題和表示的檔案

使用代表文件的視窗時,具有 DocumentEdited 屬性,NSWindow如果設定為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 事件,並檢查屬性的狀態 DocumentEditedtrue如果需要讓用戶能夠將變更儲存至檔案。 如果我們執行應用程式並輸入文字,則會顯示點:

已變更的視窗

如果您嘗試關閉視窗,您會收到警示:

顯示儲存對話框

如果您要從檔案載入檔,請使用 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. 在身 分識別偵測器中,輸入 PreferencesWindow鏡腳本標識碼

    設定分鏡腳本標識碼

  4. 設計介面:

    設計 UI

  5. 開啟應用程式功能表 (MacWindows),選取 [喜好設定...],單擊控件並拖曳至新的視窗:

    建立 segue

  6. 從快捷功能表選取 [ 顯示 ]。

  7. 儲存變更並返回 Visual Studio for Mac 以與 Xcode 同步。

如果我們執行程式代碼,並從應用程式選單選取 [喜好設定...],則會顯示視窗:

範例喜好設定功能表

使用面板

如本文開頭所述,面板會浮動在其他視窗上方,並提供使用者可在開啟檔時使用的工具或控件。

就像您在 Xamarin.Mac 應用程式中建立和使用的任何其他視窗類型一樣,程式基本上相同:

  1. 將新的視窗定義新增至專案。
  2. 按兩下 .xib 檔案,開啟在 Xcode 介面產生器中編輯的窗口設計。
  3. 在屬性偵測器和大小偵測器設定任何必要的視窗屬性。
  4. 拖曳至建置介面所需的控件,並在屬性偵測器設定它們。
  5. 使用大小偵測器來處理UI元素的大小調整。
  6. 透過 輸出動作,將視窗的UI元素公開至 C# 程式代碼。
  7. 儲存變更並切換回 Visual Studio for Mac 以與 Xcode 同步。

在屬性偵測器,您有下列面板專屬的選項:

屬性偵測器

  • 樣式 - 可讓您調整面板的樣式:一般面板(看起來像標準視窗)、公用程式面板(具有較小的標題欄)、HUD 面板(是半透明,標題欄是背景的一部分)。
  • 非啟用 - 在面板中判斷會成為金鑰視窗。
  • 文件強制回應 - 如果 Document Modal ,面板只會浮動在應用程式的視窗上方,否則會浮動在全部上方。

若要新增面板,請執行下列動作:

  1. 在 方案總管 中,以滑鼠右鍵按兩下 [專案],然後選取 [新增>檔案...]。

  2. 在 [新增檔案] 對話框中,選取具有控制器的 Xamarin.Mac>Cocoa Window:

    新增視窗控制器

  3. 輸入 DocumentPanel 作為 [名稱],然後按一下 [新增] 按鈕。

  4. 按兩下 DocumentPanel.xib 檔案,以在介面產生器中開啟它以進行編輯:

    編輯面板

  5. 刪除現有的視窗,並從介面編輯器中的 [連結庫偵測器] 拖曳面板

    刪除現有的視窗

  6. 將面板連結至檔案的擁有者 - 窗口 - 輸出:

    拖曳以連接面板

  7. 切換至 Identity Inspector ,並將 Panel 的類別設定為 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);
}

如果我們執行應用程式,將會顯示面板:

執行中應用程式的面板

重要

面板 Windows 已被 Apple 取代,應該取代為 Inspector 介面

摘要

本文已詳細探討在 Xamarin.Mac 應用程式中使用 Windows 和面板。 我們看到了 Windows 和面板的不同類型和用法、如何在 Xcode 的 Interface Builder 中建立和維護 Windows 和面板,以及如何在 C# 程式代碼中使用 Windows 和面板。