Xamarin.Mac 中的标准控件

本文介绍如何使用标准 AppKit 控件,例如按钮、标签、文本字段、检查框和 Xamarin.Mac 应用程序中的分段控件。 本文介绍如何使用 Interface Builder 将它们添加到接口,并在代码中与其交互。

在 Xamarin.Mac 应用程序中使用 C# 和 .NET 时,可以访问开发人员使用的 Objective-C 同一 AppKit 控件和 Xcode 。 由于 Xamarin.Mac 直接与 Xcode 集成,因此可以使用 Xcode 的 Interface Builder 创建和维护 Appkit 控件(或者选择直接在 C# 代码中创建它们)。

AppKit 控件是用于创建 Xamarin.Mac 应用程序的用户界面的 UI 元素。 它们由按钮、标签、文本字段、复选框和分段控件等元素组成,当用户操作按钮、标签、复选框和分段控件时会导致即时操作或可见结果。

The example app main screen

在本文中,我们将介绍在 Xamarin.Mac 应用程序中使用 AppKit 控件的基础知识。 强烈建议先浏览 Hello、Mac 文章,特别是 Xcode 和 Interface Builder 和输出口和 操作 简介部分,因为它介绍本文中将要使用的关键概念和技术。

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

控件和视图简介

macOS(以前称为 Mac OS X)通过 AppKit 框架提供一组标准的用户界面控件。 它们由按钮、标签、文本字段、复选框和分段控件等元素组成,当用户操作按钮、标签、复选框和分段控件时会导致即时操作或可见结果。

所有 AppKit 控件都具有适用于大多数用途的标准内置外观,有些控件指定在窗口框架区域或 Vibrance 效果上下文中使用的备用外观,例如在边栏区域或通知中心小组件中。

使用 AppKit 控件时,Apple 建议以下准则:

  • 避免在同一视图中混合控件大小。
  • 一般情况下,请避免垂直调整控件的大小。
  • 在控件中使用系统字体和正确的文本大小。
  • 使用控件之间的适当间距。

有关详细信息,请参阅 Apple OS X 人机界面指南关于控件和视图”部分。

在窗口框架中使用控件

AppKit 控件的子集包含一个显示样式,允许它们包含在窗口的框架区域中。 有关示例,请参阅邮件应用的工具栏:

A Mac Window frame

  • 圆形纹理按钮 - 具有样式的 NSTexturedRoundedBezelStyleANSButton
  • 带纹理的圆线段控件 - 具有样式的 NSSegmentStyleTexturedRoundedANSSegmentedControl
  • 带纹理的圆线段控件 - 具有样式的 NSSegmentStyleSeparatedANSSegmentedControl
  • 圆角纹理弹出菜单 - 具有样式的 NSTexturedRoundedBezelStyleANSPopUpButton
  • 圆纹理下拉菜单 - 具有样式的 NSTexturedRoundedBezelStyleANSPopUpButton
  • 搜索栏 - A NSSearchField.

Apple 在窗口框架中使用 AppKit 控件时建议以下准则:

  • 请勿在窗口正文中使用特定于窗口框架的控件样式。
  • 请勿在窗口框架中使用窗口正文控件或样式。

有关详细信息,请参阅 Apple OS X 人机界面指南关于控件和视图”部分。

在 Interface Builder 中创建用户界面

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

Selecting the Main Storyboard in the Solution Explorer

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

Editing the storyboard in Xcode

若要创建用户界面,需要将 UI 元素(AppKit 控件)从 库检查器 拖动到 Interface Builder 中的接口编辑器 。 在下面的示例中,垂直拆分视图控件已来自库检查器,并放置在界面编辑器中的窗口上:

Selecting a Split View from the Library

有关在 Interface Builder 中创建用户界面的详细信息,请参阅我们的 Xcode 和 Interface Builder 简介文档。

大小调整和定位

在用户界面中包含控件后,使用 约束编辑器 手动输入值来设置其位置和大小,并控制控件在调整父窗口或视图大小时如何自动定位和调整其大小:

Setting the constraints

使用自动调整框外部的红色 I-Beam控件粘在给定 (x,y) 位置。 例如:

Editing a constraint

指定所选控件(在 层次结构视图界面编辑器中)将卡在调整或移动窗口或视图的顶部和右侧位置。

编辑器控件属性的其他元素,如 Height 和 Width:

Setting the height

还可以使用对齐编辑器控制元素与约束的对齐方式:

The Alignment Editor

重要

与 iOS 不同,其中(0,0)是屏幕的左上角,在 macOS(0,0)中是左下角。 这是因为 macOS 使用数学坐标系,数字值向上和向右增加。 在用户界面上放置 AppKit 控件时,需要考虑这一点。

设置自定义类

有时,使用 AppKit 控件需要子类和现有控件,并创建该类的自定义版本。 例如,定义源列表的自定义版本:

using System;
using AppKit;
using Foundation;

namespace AppKit
{
    [Register("SourceListView")]
    public class SourceListView : NSOutlineView
    {
        #region Computed Properties
        public SourceListDataSource Data {
            get {return (SourceListDataSource)this.DataSource; }
        }
        #endregion

        #region Constructors
        public SourceListView ()
        {

        }

        public SourceListView (IntPtr handle) : base(handle)
        {

        }

        public SourceListView (NSCoder coder) : base(coder)
        {

        }

        public SourceListView (NSObjectFlag t) : base(t)
        {

        }
        #endregion

        #region Override Methods
        public override void AwakeFromNib ()
        {
            base.AwakeFromNib ();

        }
        #endregion

        #region Public Methods
        public void Initialize() {

            // Initialize this instance
            this.DataSource = new SourceListDataSource (this);
            this.Delegate = new SourceListDelegate (this);

        }

        public void AddItem(SourceListItem item) {
            if (Data != null) {
                Data.Items.Add (item);
            }
        }
        #endregion

        #region Events
        public delegate void ItemSelectedDelegate(SourceListItem item);
        public event ItemSelectedDelegate ItemSelected;

        internal void RaiseItemSelected(SourceListItem item) {
            // Inform caller
            if (this.ItemSelected != null) {
                this.ItemSelected (item);
            }
        }
        #endregion
    }
}

指令 [Register("SourceListView")] 向其中公开 SourceListView 类 Objective-C ,以便在 Interface Builder 中使用。 有关详细信息,请参阅 Xamarin.Mac 内部文档部分的“公开 C# 类/方法Objective-C”,其中介绍了ExportRegister用于将 C# 类Objective-C连接到对象和 UI 元素的命令。

使用上述代码,可以将要扩展的 AppKit 控件的 AppKit 控件拖到设计图面(下面的示例中,源列表),切换到标识检查器,并将自定义类设置为公开Objective-C的名称(示例SourceListView):

Setting a custom class in Xcode

公开出口和操作

在 C# 代码中访问 AppKit 控件之前,需要将其公开为 输出口操作。 为此,请在“接口层次结构”或“接口编辑器”中选择给定控件,并切换到“助理视图”(确保.h已选择窗口进行编辑):

Selecting the correct file to edit

从 AppKit 控件拖动到提供 .h 文件,开始创建 出口操作

Dragging to create an Outlet or Action

选择要创建的曝光类型,并为出口或操作指定名称

Configuring the Outlet or Action

有关使用出口和操作的详细信息,请参阅 Xcode 和 Interface Builder 文档简介的“出口和操作”部分。

与 Xcode 同步更改

从 Xcode 切换回 Visual Studio for Mac 时,Xcode 中所做的任何更改将自动与 Xamarin.Mac 项目同步。

如果选择SplitViewController.designer.cs解决方案资源管理器,你将能够在 C# 代码中查看网口操作有线方式:

Synchronizing Changes with Xcode

请注意文件中的定义 SplitViewController.designer.cs

[Outlet]
AppKit.NSSplitViewItem LeftController { get; set; }

[Outlet]
AppKit.NSSplitViewItem RightController { get; set; }

[Outlet]
AppKit.NSSplitView SplitView { get; set; }

在 Xcode 中 MainWindow.h 与文件中的定义对齐:

@interface SplitViewController : NSSplitViewController {
    NSSplitViewItem *_LeftController;
    NSSplitViewItem *_RightController;
    NSSplitView *_SplitView;
}

@property (nonatomic, retain) IBOutlet NSSplitViewItem *LeftController;

@property (nonatomic, retain) IBOutlet NSSplitViewItem *RightController;

@property (nonatomic, retain) IBOutlet NSSplitView *SplitView;

如你所看到的,Visual Studio for Mac 侦听对文件的更改 .h ,然后自动同步相应 .designer.cs 文件中的这些更改,以向应用程序公开这些更改。 你可能还会注意到 SplitViewController.designer.cs ,这是一个分部类,因此 Visual Studio for Mac 不必修改 SplitViewController.cs ,这将覆盖我们对类所做的任何更改。

你通常永远不需要打开 SplitViewController.designer.cs 自己,它在这里只是为了教育目的。

重要

在大多数情况下,Visual Studio for Mac 将自动看到 Xcode 中所做的任何更改,并将其同步到 Xamarin.Mac 项目。 如果同步不自动进行,请切换回 Xcode,然后再次切换到 Visual Studio for Mac。 这通常会开始同步周期。

使用按钮

AppKit 提供了可在用户界面设计中使用的多种按钮类型。 有关详细信息,请参阅 Apple OS X 人机界面指南的“按钮”部分。

An example of the different button types

如果某个按钮已通过 输出口公开,则以下代码将响应按下按钮:

ButtonOutlet.Activated += (sender, e) => {
        FeedbackLabel.StringValue = "Button Outlet Pressed";
};

对于通过 Actions 公开的按钮,将自动为你创建一个 public partial 方法,其中包含你在 Xcode 中选择的名称。 若要响应 Action,请完成对操作定义的类中的分部方法。 例如:

partial void ButtonAction (Foundation.NSObject sender) {
    // Do something in response to the Action
    FeedbackLabel.StringValue = "Button Action Pressed";
}

对于具有状态(如 On 和 Off)的按钮,可以针对枚举检查或设置NSCellStateValueState状态。 例如:

DisclosureButton.Activated += (sender, e) => {
    LorumIpsum.Hidden = (DisclosureButton.State == NSCellStateValue.On);
};

位置 NSCellStateValue 可以是:

  • 打开 - 按下按钮或选中控件(例如复选框中的检查)。
  • 关闭 - 未按下按钮或未选择控件。
  • 混合 - OnOff 状态的混合体。

将按钮标记为默认值并设置等效键

对于已添加到用户界面设计的任何按钮,可以将该按钮标记为当用户按下键盘上的 Return/Enter 键时将激活的默认按钮。 在 macOS 中,此按钮默认会收到蓝色背景色。

若要将按钮设置为默认值,请在 Xcode 的 Interface Builder 中选择它。 接下来,在 “属性检查器”中,选择 “键等效 ”字段,然后按 Return/Enter 键:

Editing the Key Equivalent

同样,你可以分配任何可用于使用键盘而不是鼠标激活按钮的键序列。 例如,按上图中的 Command-C 键。

当应用运行时,带有按钮的窗口是键和焦点,如果用户按下 Command-C,按钮的操作将激活(就好像用户单击了按钮)。

使用复选框和单选按钮

AppKit 提供可在用户界面设计中使用的多种复选框和单选按钮组。 有关详细信息,请参阅 Apple OS X 人机界面指南的“按钮”部分。

An example of the available checkbox types

复选框和单选按钮(通过出口公开)具有一个状态(如打开关闭),该状态可以检查或设置针对State枚举的属性NSCellStateValue。 例如:

AdjustTime.Activated += (sender, e) => {
    FeedbackLabel.StringValue = string.Format("Adjust Time: {0}",AdjustTime.State == NSCellStateValue.On);
};

位置 NSCellStateValue 可以是:

  • 打开 - 按下按钮或选中控件(例如复选框中的检查)。
  • 关闭 - 未按下按钮或未选择控件。
  • 混合 - OnOff 状态的混合体。

若要选择单选按钮组中的按钮,请公开单选按钮以选择为 出口 并设置其 State 属性。 例如:

partial void SelectCar (Foundation.NSObject sender) {
    TransportationCar.State = NSCellStateValue.On;
    FeedbackLabel.StringValue = "Car Selected";
}

若要获取单选按钮的集合以充当组并自动处理所选状态,请创建一个新的 操作 并将组中的每个按钮附加到其中:

Creating a new Action

接下来,为属性检查器中的每个单选按钮分配唯一的Tag按钮:

Editing a radio button tag

保存更改并返回到 Visual Studio for Mac,添加代码以处理所有单选按钮附加到的操作:

partial void NumberChanged(Foundation.NSObject sender)
{
    var check = sender as NSButton;
    Console.WriteLine("Changed to {0}", check.Tag);
}

可以使用该 Tag 属性查看选择了哪个单选按钮。

使用菜单控件

AppKit 提供了多种可在用户界面设计中使用的菜单控件类型。 有关详细信息,请参阅 Apple OS X 人机界面指南“菜单控件”部分。

Example menu controls

提供菜单控件数据

可以将 macOS 可用的菜单控件设置为从内部列表中填充下拉列表(可以在 Interface Builder 中预定义或通过代码填充),或者提供自己的自定义外部数据源。

使用内部数据

除了在 Interface Builder 中定义项之外,菜单控件(如 NSComboBox)还提供一组完整的方法,允许你从它们维护的内部列表中添加、编辑或删除项:

  • Add - 向列表末尾添加新项。
  • GetItem - 返回给定索引处的项。
  • Insert - 在给定位置的列表中插入新项。
  • IndexOf - 返回给定项的索引。
  • Remove - 从列表中删除给定项。
  • RemoveAll - 从列表中删除所有项。
  • RemoveAt - 删除给定索引处的项。
  • Count - 返回列表中的项数。

重要

如果使用 Extern 数据源(UsesDataSource = true),调用上述任何方法将引发异常。

使用外部数据源

可以选择使用外部数据源并为项(如 SQLite 数据库)提供自己的后盾存储,而不是使用内置内部数据来提供菜单控件的行。

若要使用外部数据源,需要创建菜单控件数据源的实例(NSComboBoxDataSource 例如),并重写多种方法以提供必要的数据:

  • ItemCount - 返回列表中的项数。
  • ObjectValueForItem - 返回给定索引的项的值。
  • IndexOfItem - 返回指定项值的索引。
  • CompletedString - 返回部分类型化项值的第一个匹配项值。 仅当已启用自动完成时Completes = true() 才会调用此方法。

有关更多详细信息,请参阅“使用数据库”文档的“数据库”和“组合框”部分。

调整列表的外观

以下方法可用于调整菜单控件的外观:

  • HasVerticalScroller - 如果 true,控件将显示垂直滚动条。
  • VisibleItems - 调整打开控件时显示的项目数。 默认值为 5 (5)。
  • IntercellSpacing - 通过提供 NSSize 指定左右边距的位置 Width 以及 Height 指定项前后的空间,调整给定项周围的空间量。
  • ItemHeight - 指定列表中每个项的高度。

对于下拉列表类型 NSPopupButtons,第一个菜单项提供控件的标题。 例如:

An example menu control

若要更改标题,请将此项公开为 出口 ,并使用如下所示的代码:

DropDownSelected.Title = "Item 1";

操作所选项

通过以下方法和属性,可以操作菜单控件列表中的选定项:

  • SelectItem - 选择给定索引处的项。
  • Select - 选择给定的项值。
  • DeselectItem - 取消选择给定索引处的项。
  • SelectedIndex - 返回当前选定项的索引。
  • SelectedValue - 返回当前所选项的值。

使用在 ScrollItemAtIndexToTop 列表顶部的给定索引处显示项,并 ScrollItemAtIndexToVisible 滚动到列表,直到给定索引处的项可见。

对事件作出响应

菜单控件提供以下事件来响应用户交互:

  • SelectionChanged - 当用户从列表中选择值时调用。
  • SelectionIsChanging - 在新的用户选定项成为活动选择之前调用。
  • WillPopup - 在显示项下拉列表之前调用。
  • WillDismiss - 在关闭项目下拉列表之前调用。

对于 NSComboBox 控件,它们包括与该控件相同的所有事件 NSTextField,例如 Changed 每当用户编辑组合框中文本的值时调用的事件。

(可选)可以通过将项 附加到操作 来响应接口生成器中定义的内部数据菜单项,并使用如下所示的代码响应 用户触发的操作

partial void ItemOne (Foundation.NSObject sender) {
    DropDownSelected.Title = "Item 1";
    FeedbackLabel.StringValue = "Item One Selected";
}

有关使用菜单和菜单控件的详细信息,请参阅我们的 菜单弹出按钮和下拉列表 文档。

使用选择控件

AppKit 提供了可在用户界面设计中使用的多种选择控件类型。 有关详细信息,请参阅 Apple OS X 人机界面指南“选择控件”部分。

Example selection controls

可通过两种方式跟踪选择控件何时具有用户交互,方法是将其公开为 操作。 例如:

partial void SegmentButtonPressed (Foundation.NSObject sender) {
    FeedbackLabel.StringValue = string.Format("Button {0} Pressed",SegmentButtons.SelectedSegment);
}

或者,将委托附加到事件Activated。 例如:

TickedSlider.Activated += (sender, e) => {
    FeedbackLabel.StringValue = string.Format("Stepper Value: {0:###}",TickedSlider.IntValue);
};

若要设置或读取选择控件的值,请使用 IntValue 该属性。 例如:

FeedbackLabel.StringValue = string.Format("Stepper Value: {0:###}",TickedSlider.IntValue);

特殊控件(如 Color Well 和 Image Well)具有其值类型的特定属性。 例如:

ColorWell.Color = NSColor.Red;
ImageWell.Image = NSImage.ImageNamed ("tag.png");

具有以下 NSDatePicker 用于直接使用日期和时间的属性:

  • DateValue - 当前日期和时间值作为一个 NSDate
  • 本地 - 用户的位置。NSLocal
  • TimeInterval - 时间值作为一个 Double
  • TimeZone - 用户的时区。NSTimeZone

使用指示器控件

AppKit 提供了可在用户界面设计中使用的多种指示器控件类型。 有关详细信息,请参阅 Apple OS X 人机界面指南“指示器控制”部分。

Example indicator controls

可通过两种方式跟踪指示器控件何时具有用户交互,方法是将其公开为操作或出口,并将委托附加到Activated事件。 例如:

LevelIndicator.Activated += (sender, e) => {
    FeedbackLabel.StringValue = string.Format("Level: {0:###}",LevelIndicator.DoubleValue);
};

若要读取或设置指示器控件的值,请使用 DoubleValue 该属性。 例如:

FeedbackLabel.StringValue = string.Format("Rating: {0:###}",Rating.DoubleValue);

显示时应对不确定和异步进度指示器进行动画处理。 StartAnimation使用该方法在显示动画时启动动画。 例如:

Indeterminate.StartAnimation (this);
AsyncProgress.StartAnimation (this);

StopAnimation调用该方法将停止动画。

使用文本控件

AppKit 提供了多种可在用户界面设计中使用的文本控件类型。 有关详细信息,请参阅 Apple OS X 人机界面指南的文本控件部分。

Example text controls

对于文本字段(NSTextField),以下事件可用于跟踪用户交互:

  • 已更改 - 每当用户更改字段的值时,将触发。 例如,在键入的每个字符上。
  • EditingBegan - 当用户选择要编辑的字段时触发。
  • EditingEnded - 当用户按下字段中的 Enter 键或离开字段时。

使用 StringValue 属性读取或设置字段的值。 例如:

FeedbackLabel.StringValue = string.Format("User ID: {0}",UserField.StringValue);

对于显示或编辑数值的字段,可以使用该 IntValue 属性。 例如:

FeedbackLabel.StringValue = string.Format("Number: {0}",NumberField.IntValue);

提供 NSTextView 具有内置格式的特色文本编辑和显示区域。 与 a 类似 NSTextField,使用 StringValue 属性读取或设置区域的值。

有关在 Xamarin.Mac 应用中使用文本视图的复杂示例示例,请参阅 SourceWriter 示例应用。 SourceWriter 是一个非常简单的源代码编辑器,提供代码补全和简单语法突出显示支持。

SourceWriter 代码已经完全注释,且在可用时,提供了相关链接,链接涵盖了从关键技术或方法到 Xamarin.Mac 指南文档中的相关信息。

使用内容视图

AppKit 提供了多种类型的内容视图,可在用户界面设计中使用。 有关详细信息,请参阅 Apple OS X 人机界面指南的内容视图部分。

An example content view

Popovers

弹出窗口是一个暂时性 UI 元素,它提供与特定控件或屏幕上区域直接相关的功能。 弹出窗口浮动在窗口上方,其中包含与其相关的控件或区域,其边框包含一个箭头,用于指示它从中出现的点。

若要创建弹出窗口,请执行以下操作:

  1. .storyboard在解决方案资源管理器中双击弹出窗口,打开要向其添加弹出窗口的文件

  2. 视图控制器从库检查器拖到接口编辑器上:

    Selecting a View Controller from the Library

  3. 定义自定义视图的大小和布局

    Editing the layout

  4. 从弹出窗口的源单击并拖动到 视图控制器上:

    Dragging to create a segue

  5. 从弹出菜单中选择 Popover

    Setting the segue type

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

选项卡视图

选项卡视图由选项卡列表(类似于分段控件)与一组称为 “窗格”的视图组合在一起。 当用户选择新的选项卡时,将显示附加到它的窗格。 每个窗格都包含其自己的一组控件。

使用 Xcode 接口生成器中的选项卡视图时,使用 属性检查器 设置选项卡数:

Editing the number of tabs

选择接口层次结构中的每个选项卡以设置其标题并将 UI 元素添加到其窗格

Editing the tabs in Xcode

数据绑定 AppKit 控件

通过在 Xamarin.Mac 应用程序中使用键值编码和数据绑定技术,可以大大减少必须编写和维护的代码量,以填充和使用 UI 元素。 还可以从前端用户界面(Model-View-Controller)进一步分离支持数据(数据模型),从而更轻松地维护、更灵活的应用程序设计。

键值编码(KVC)是间接访问对象的属性的机制,它使用键(特殊格式的字符串)来标识属性,而不是通过实例变量或访问器方法get/set()访问它们。 通过在 Xamarin.Mac 应用程序中实现符合键值编码的访问器,可以访问其他 macOS 功能,例如键值观察(KVO)、数据绑定、核心数据、Cocoa 绑定和可脚本性。

有关详细信息,请参阅数据绑定和键值编码文档的“简单数据绑定”部分。

总结

本文详细介绍了如何使用 Xamarin.Mac 应用程序中的标准 AppKit 控件,例如按钮、标签、文本字段、复选框和分段控件。 其中介绍了将它们添加到 Xcode 的 Interface Builder 中的用户界面设计中,通过出口和操作向代码公开,并在 C# Code 中使用 AppKit 控件。