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 应用程序可以在屏幕上显示一个或多个 Windows,以管理和协调它显示和处理的信息。 窗口的主体函数包括:

  1. 提供可在其中放置和管理视图和控件的区域。
  2. 接受和响应事件,以响应用户与键盘和鼠标的交互。

Windows 可以在无模式状态 (使用,例如可以同时打开多个文档的文本编辑器) 或模式 ((例如,必须先关闭导出对话框),然后应用程序才能继续) 。

面板是一种特殊的 Window (基 NSWindow 类) 的子类,通常在应用程序中提供辅助功能,例如文本格式检查器和系统颜色选取器等实用工具窗口。

在 Xcode 中编辑窗口

本文介绍在 Xamarin.Mac 应用程序中使用 Windows 和面板的基础知识。 强烈建议先完成 Hello, Mac 文章,特别是 Xcode 和接口生成器简介 以及 插座和操作 部分,因为它涵盖了我们将在本文中使用的关键概念和技术。

您可能还希望查看 Xamarin.Mac Internals 文档的公开 C# 类/方法Objective-C部分,其中介绍了Register用于将 C# 类Objective-C连接到对象和 UI 元素的 和 Export 命令。

Windows 简介

如上所述,窗口提供了一个区域,可以在其中放置和管理视图和控件,并根据用户交互 (通过键盘或鼠标) 响应事件。

根据 Apple 的说法,macOS 应用中有五种main类型的 Windows:

  • 文档窗口 - 文档窗口包含基于文件的用户数据,例如电子表格或文本文档。
  • 应用窗口 - 应用窗口是非基于文档 (的应用程序的main窗口,如 Mac) 上的“日历”应用。
  • 面板 - 面板浮动在其他窗口上方,并提供用户在打开文档时可以使用的工具或控件。 在某些情况下,面板可以是半透明的 (,例如处理大型图形) 。
  • 对话框 - 对话框显示以响应用户操作,通常提供用户完成操作的方式。 对话框需要用户做出响应才能关闭。 (请参阅 使用对话框)
  • 警报 - 警报是一种特殊类型的对话框,当发生严重问题 ((例如错误) )或警告 ((例如准备) 删除文件)时会出现。 由于警报是对话,因此还需要用户响应才能关闭警报。 (请参阅 使用警报)

有关详细信息,请参阅 Apple macOS 设计主题“关于 Windows”部分。

主窗口、键窗口和非活动窗口

Xamarin.Mac 应用程序中的 Windows 的外观和行为可能会因用户当前与其交互的方式而有所不同。 用户当前关注的最重要文档或应用窗口称为 “主窗口”。 在大多数情况下,此窗口也是当前接受用户输入) 窗口 ( 窗口。 但并非总是如此,例如,颜色选取器可以打开,并且是用户与之交互的键窗口,以更改文档窗口中项的状态 (该窗口仍然是主窗口) 。

主窗口和密钥窗口 (如果它们是单独的) 始终处于活动状态, 则非活动窗口 是不在前台的打开窗口。 例如,文本编辑器应用程序一次可以打开多个文档,只有主窗口处于活动状态,所有其他窗口将处于非活动状态。

有关详细信息,请参阅 Apple macOS 设计主题“关于 Windows”部分。

命名窗口

窗口可以显示标题栏,显示标题时,它通常是应用程序的名称、正在处理的文档的名称或窗口 (的功能,例如 Inspector) 。 某些应用程序不显示标题栏,因为它们可通过视觉识别,并且不适用于文档。

Apple 建议遵循以下准则:

  • 使用应用程序名称作为main非文档窗口的标题。
  • 将新文档窗口 untitled命名为 。 对于第一个新文档,不要将数字追加到标题 (,例如 untitled 1) 。 如果用户在保存并命名第一个文档之前创建另一个新文档,请调用该窗口 untitled 2untitled 3等。

有关详细信息,请参阅 Apple macOS 设计主题命名 Windows 部分。

全屏窗口

在 macOS 中,应用程序的窗口可以全屏隐藏所有内容,包括应用程序菜单栏 (可以通过将光标移动到屏幕顶部) 来显示,以提供与其内容的无干扰交互。

Apple 建议遵循以下准则:

  • 确定窗口全屏是否有意义。 提供简短交互 (的应用程序(如计算器) )不应提供全屏模式。
  • 如果全屏任务需要工具栏,则显示工具栏。 通常,工具栏在全屏模式下处于隐藏状态。
  • 全屏窗口应具有用户完成任务所需的所有功能。
  • 如果可能,请在用户处于全屏窗口中时避免 Finder 交互。
  • 利用增加的屏幕空间,而无需将焦点从main任务移开。

有关详细信息,请参阅 Apple macOS 设计主题全屏 Windows 部分。

面板

面板是一个辅助窗口,其中包含影响活动文档或选择 ((例如系统颜色选取器) )的控件和选项:

颜色面板

面板可以是 特定于应用系统范围的。 App-Specific面板浮动在应用程序的文档窗口顶部,当应用程序在后台时会消失。 系统范围的面板 (,如 字体 面板) ,浮动在所有打开的窗口顶部,无论应用程序如何。

Apple 建议遵循以下准则:

  • 一般情况下,使用标准面板,透明面板仅应谨慎使用,并且用于图形密集型任务。
  • 请考虑使用面板让用户轻松访问直接影响其任务的重要控件或信息。
  • 根据需要隐藏和显示面板。
  • 面板应始终包含标题栏。
  • 面板不应包含活动最小化按钮。

检查器

大多数新式 macOS 应用程序将影响活动文档或选择的辅助控件和选项作为主窗口 (的 检查器 提供,如下面所示的 “页面” 应用) ,而不是使用面板 Windows:

示例检查器

有关详细信息,请参阅 Apple macOS 设计主题面板部分和我们的 MacInspector 示例应用,了解 Xamarin.Mac 应用中检查器接口的完整实现。

在 Xcode 中创建和维护窗口

创建新的 Xamarin.Mac Cocoa 应用程序时,默认情况下会获得标准空白窗口。 此窗口在 .storyboard 项目中自动包含的文件中定义。 若要编辑 Windows 设计,请在解决方案资源管理器中双击Main.storyboard文件:

选择main情节提要

这将在 Xcode 的接口生成器中打开窗口设计:

在 Xcode 中编辑 UI

属性检查器中,可以使用多个属性来定义和控制窗口:

  • 标题 - 这是将在窗口标题栏中显示的文本。
  • 自动保存 - 这是在窗口位置和设置自动保存时用于标识窗口的
  • 标题栏 - 窗口是否显示标题栏。
  • 统一标题和工具栏 - 如果窗口包含工具栏,应为标题栏的一部分。
  • 完整大小的内容视图 - 允许窗口的内容区域位于标题栏下。
  • 阴影 - 窗口是否具有阴影。
  • 纹理 - 纹理窗口可以使用效果 ((如活力) )并且可以通过拖动其正文上的任意位置来移动。
  • 关闭 - 窗口是否具有“关闭”按钮。
  • 最小化 - 窗口是否具有最小化按钮。
  • 调整大小 - 窗口是否具有调整大小控件。
  • 工具栏按钮 - 窗口是否具有隐藏/显示工具栏按钮。
  • 可还原 - 窗口的位置和设置是否自动保存和还原。
  • 启动时可见 - 加载文件时自动显示的 .xib 窗口。
  • 停用时隐藏 - 当应用程序进入后台时,窗口处于隐藏状态。
  • 关闭时释放 - 窗口在关闭时从内存中清除。
  • 始终显示工具提示 - 工具提示是否不断显示。
  • 重新计算视图循环 - 在绘制窗口之前重新计算视图顺序。
  • 空间公开循环 - 全部定义窗口在这些 macOS 环境中的行为方式。
  • 全屏 - 确定此窗口是否可以进入全屏模式。
  • 动画 - 控制可用于窗口的动画类型。
  • 外观 - 控制窗口的外观。 目前只有一个外观,水。

有关更多详细信息,请参阅 Apple 的 Windows 和 NSWindow简介文档。

设置默认大小和位置

若要设置窗口的初始位置并控制窗口的大小,请切换到 “大小检查器”:

默认大小和位置

在这里,可以设置窗口的初始大小,为其指定最小和最大大小,在屏幕上设置初始位置,并控制窗口周围的边框。

设置自定义main窗口控制器

为了能够创建输出口和操作以向 C# 代码公开 UI 元素,Xamarin.Mac 应用需要使用自定义窗口控制器。

请执行以下操作:

  1. 在 Xcode 的 Interface Builder 中打开应用的情节提要。

  2. NSWindowController 设计图面中选择 。

  3. 切换到 “标识检查器” 视图,输入 WindowController 作为 “类名”

    设置类名

  4. 保存更改并返回到Visual Studio for Mac进行同步。

  5. 文件WindowController.cs将添加到项目中Visual Studio for Mac解决方案资源管理器

    选择 Windows 控制器

  6. 在 Xcode 的 Interface Builder 中重新打开情节提要。

  7. 该文件 WindowController.h 将可供使用:

    编辑 WindowController.h 文件

添加 UI 元素

若要定义窗口的内容,请将控件从 库检查器 拖到 接口编辑器上。 有关使用接口生成器创建和启用控件的详细信息 ,请参阅我们的 Xcode 和 Interface Builder 简介 文档。

例如,将工具栏从 库检查器 拖动到 “接口编辑器”中的窗口中:

从库中选择工具栏

接下来,在 “文本视图” 中拖动并调整其大小以填充工具栏下的区域:

添加文本视图

由于我们希望 文本视图 随着窗口大小的变化而缩小和增长,因此让我们切换到 约束编辑器 并添加以下约束:

编辑约束

通过单击编辑器顶部的四个 红色 I 形梁 并单击“ 添加 4 个约束”,我们告知文本视图坚持给定的 X、Y 坐标,并在调整窗口大小时水平和垂直方向增长或收缩。

最后,使用输出口 (确保选择ViewController.h文件) 向代码公开文本视图

配置插座

保存更改并切换回 Visual Studio for Mac 以与 Xcode 同步。

有关使用 输出口操作的详细信息,请参阅输出 口和操作 文档。

标准窗口工作流

对于在 Xamarin.Mac 应用程序中创建和使用的任何窗口,此过程与上面刚刚完成的过程基本相同:

  1. 对于不是默认自动添加到项目的新窗口,请向项目添加新的窗口定义。 下面将详细介绍这一点。
  2. 双击 Main.storyboard 文件以打开在 Xcode 的 Interface Builder 中编辑的窗口设计。
  3. 将新窗口拖动到用户界面的设计中,并使用 Segues (将窗口挂钩到主窗口,有关详细信息,请参阅使用情节提要文档) 的 Segues 部分。
  4. 属性检查器和大小检查器中设置任何必需的窗口属性。
  5. 拖入生成接口所需的控件,并在 属性检查器中对其进行配置。
  6. 使用 大小检查器 处理 UI 元素的大小调整。
  7. 通过 输出口操作向 C# 代码公开窗口的 UI 元素。
  8. 保存更改并切换回 Visual Studio for Mac 以与 Xcode 同步。

创建基本窗口后,我们将查看 Xamarin.Mac 应用程序在使用窗口时执行的典型过程。

显示默认窗口

默认情况下,新的 Xamarin.Mac 应用程序会在启动时自动显示文件中定义的 MainWindow.xib 窗口:

运行的示例窗口

由于我们修改了上述窗口的设计,因此它现在包含默认的工具栏和 文本视图 控件。 文件中的 Info.plist 以下部分负责显示此窗口:

编辑 Info.plist

界面下拉列表用于选择将用作main应用 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 提供了一种通知用户 Windows (NSWindow) 的内容已被用户修改并需要保存的方法。 如果窗口包含修改后的内容,则会在“ 关闭 ”小组件中显示一个小黑点:

具有修改后的标记的窗口

如果用户尝试关闭窗口或退出 Mac 应用,而窗口的内容存在未保存的更改,则应显示 对话框模式工作表 ,并允许用户首先保存其更改:

关闭窗口时显示的保存工作表

将窗口标记为已修改

若要将窗口标记为已修改内容,请使用以下代码:

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

保存更改后,使用以下命令清除修改后的标志:

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

在关闭窗口之前保存更改

若要为关闭 Window 的用户watch并允许他们事先保存修改后的内容,需要创建 的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 在以下代码中:

标识检查器

接下来,编辑自定义窗口控制器类并监视 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
    };
}

设置窗口的标题和表示的文件

使用表示文档的窗口时, 具有一个 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 窗口中的事件并检查 属性的状态 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;

        }
    }
}

现在,如果我们运行应用,请从“文件”菜单中选择“打开...”,从“打开”对话框中选择一个文本文件并将其打开:

打开的对话框

将显示文件,并使用文件的图标设置标题:

加载的文件的内容

向项目添加新窗口

除了main文档窗口之外,Xamarin.Mac 应用程序可能需要向用户显示其他类型的窗口,例如“首选项”或“检查器面板”。

若要添加新窗口,请执行以下操作:

  1. 解决方案资源管理器,双击Main.storyboard该文件以在 Xcode 的 Interface Builder 中将其打开进行编辑。

  2. 库中拖动新的窗口控制器,并将其拖放到设计图面上:

    在库中选择新的窗口控制器

  3. 标识检查器中,输入 PreferencesWindow 作为 情节提要 ID

    设置情节提要 ID

  4. 设计接口:

    设计 UI

  5. 打开“应用菜单” (MacWindows) ,选择 “首选项...”,Control-Click并拖动到新窗口:

    创建 segue

  6. 从弹出菜单中选择“ 显示 ”。

  7. 保存更改并返回到 Visual Studio for Mac 以与 Xcode 同步。

如果我们运行代码并从“应用程序”菜单中选择“首选项...”,则会显示窗口:

示例首选项菜单

使用面板

如本文开头所述,面板浮动在其他窗口上方,并提供用户在打开文档时可以使用的工具或控件。

与在 Xamarin.Mac 应用程序中创建和使用的任何其他类型的窗口一样,该过程基本相同:

  1. 向项目添加新的窗口定义。
  2. 双击 .xib 文件以打开在 Xcode 的 Interface Builder 中编辑的窗口设计。
  3. 属性检查器和大小检查器中设置任何必需的窗口属性。
  4. 拖入生成接口所需的控件,并在 属性检查器中对其进行配置。
  5. 使用 大小检查器 处理 UI 元素的大小调整。
  6. 通过 输出口操作向 C# 代码公开窗口的 UI 元素。
  7. 保存更改并切换回 Visual Studio for Mac 以与 Xcode 同步。

属性检查器中,有以下特定于面板的选项:

属性检查器

  • 样式 - 允许你调整面板的样式:常规面板 (看起来像标准窗口) ,实用工具面板 (具有较小的标题栏) ,HUD 面板 (半透明,标题栏是背景) 的一部分。
  • 未激活 - 面板中的“确定”成为关键窗口。
  • 文档模式 - 如果文档模式,面板将仅浮动在应用程序窗口上方,否则它浮动在全部之上。

若要添加新面板,请执行以下操作:

  1. 解决方案资源管理器中,右键单击“项目”,然后选择“添加新>文件...”

  2. 在“新建文件”对话框中,选择“带控制器的Xamarin.Mac> Cocoa 窗口”:

    添加新的窗口控制器

  3. 对“名称”输入 DocumentPanel,然后单击“新建”按钮。

  4. 双击 DocumentPanel.xib 文件以将其打开,以便在 Interface Builder 中编辑:

    编辑面板

  5. 删除现有窗口,并从“接口编辑器”中的“库检查器”拖动面板:

    删除现有窗口

  6. 将面板挂钩到 “文件所有者 - ”窗口 - 的“输出口”:

    拖动以将面板连接起来

  7. 切换到 标识检查器 ,并将 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);
}

如果运行应用程序,将显示面板:

正在运行的应用中的面板

重要

Apple 已弃用面板 Windows,应将其替换为 检查器接口。 有关在 Xamarin.Mac 应用中创建 Inspector 的完整示例,请参阅 MacInspector 示例应用。

总结

本文详细介绍了在 Xamarin.Mac 应用程序中使用 Windows 和面板。 我们了解了 Windows 和面板的不同类型和用法,如何在 Xcode 的 Interface Builder 中创建和维护 Windows 和面板,以及如何在 C# 代码中使用 Windows 和面板。