Конструктор пользовательского интерфейса .storyboard/.xib-less в Xamarin.Mac

В этой статье описывается создание пользовательского интерфейса приложения Xamarin.Mac непосредственно из кода C# без файлов раскадровки, XIB-файлов или построителя интерфейсов.

Обзор

При работе с C# и .NET в приложении Xamarin.Mac у вас есть доступ к тем же элементам пользовательского интерфейса и средствам, в которых работает Objective-C разработчик, и Xcode . Как правило, при создании приложения Xamarin.Mac вы будете использовать построитель интерфейсов Xcode с раскадровкой или XIB-файлами для создания и поддержания пользовательского интерфейса приложения.

Вы также можете создавать некоторые или все пользовательские интерфейсы приложения Xamarin.Mac непосредственно в коде C#. В этой статье мы рассмотрим основы создания пользовательских интерфейсов и элементов пользовательского интерфейса в коде C#.

Редактор кода Visual Studio для Mac

Переключение окна на использование кода

При создании нового приложения Xamarin.Mac Cocoa вы получаете стандартное пустое окно по умолчанию. Эти окна определяются в файле Main.storyboard (или традиционно в файле MainWindow.xib), который автоматически включается в проект. Это также включает в себя файл ViewController.cs , который управляет основным представлением приложения (или снова традиционно MainWindow.cs и файлом MainWindowController.cs ).

Чтобы перейти к окну Xibless для приложения, выполните следующие действия.

  1. Откройте приложение, которое вы хотите прекратить использовать .storyboard или XIB-файлы, чтобы определить пользовательский интерфейс в Visual Studio для Mac.

  2. На панели решений щелкните правой кнопкой мыши файл Main.storyboard или MainWindow.xib и выберите "Удалить".

    Удаление основной раскадровки или окна

  3. В диалоговом окне "Удалить" нажмите кнопку "Удалить", чтобы полностью удалить раскадровку или XIB из проекта:

    Подтверждение удаления

Теперь необходимо изменить файл MainWindow.cs, чтобы определить макет окна и изменить файл ViewController.cs или MainWindowController.cs, чтобы создать экземпляр классаMainWindow, так как мы больше не используем раскадровку или XIB-файл.

Современные приложения Xamarin.Mac, использующие раскадровки для пользовательского интерфейса, могут не включать файлы MainWindow.cs, ViewController.cs или MainWindowController.cs . По мере необходимости просто добавьте в проект новый пустой класс C# (добавить>новый файл...>Общий>пустой класс) и назовите его таким же, как отсутствующий файл.

Определение окна в коде

Затем измените файл MainWindow.cs и сделайте его следующим образом:

using System;
using Foundation;
using AppKit;
using CoreGraphics;

namespace MacXibless
{
    public partial class MainWindow : NSWindow
    {
        #region Private Variables
        private int NumberOfTimesClicked = 0;
        #endregion

        #region Computed Properties
        public NSButton ClickMeButton { get; set;}
        public NSTextField ClickMeLabel { get ; set;}
        #endregion

        #region Constructors
        public MainWindow (IntPtr handle) : base (handle)
        {
        }

        [Export ("initWithCoder:")]
        public MainWindow (NSCoder coder) : base (coder)
        {
        }

        public MainWindow(CGRect contentRect, NSWindowStyle aStyle, NSBackingStore bufferingType, bool deferCreation): base (contentRect, aStyle,bufferingType,deferCreation) {
            // Define the user interface of the window here
            Title = "Window From Code";

            // Create the content view for the window and make it fill the window
            ContentView = new NSView (Frame);

            // Add UI elements to window
            ClickMeButton = new NSButton (new CGRect (10, Frame.Height-70, 100, 30)){
                AutoresizingMask = NSViewResizingMask.MinYMargin
            };
            ContentView.AddSubview (ClickMeButton);

            ClickMeLabel = new NSTextField (new CGRect (120, Frame.Height - 65, Frame.Width - 130, 20)) {
                BackgroundColor = NSColor.Clear,
                TextColor = NSColor.Black,
                Editable = false,
                Bezeled = false,
                AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.MinYMargin,
                StringValue = "Button has not been clicked yet."
            };
            ContentView.AddSubview (ClickMeLabel);
        }
        #endregion

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

            // Wireup events
            ClickMeButton.Activated += (sender, e) => {
                // Update count
                ClickMeLabel.StringValue = (++NumberOfTimesClicked == 1) ? "Button clicked one time." : string.Format("Button clicked {0} times.",NumberOfTimesClicked);
            };
        }
        #endregion

    }
}

Давайте обсудим несколько ключевых элементов.

Во-первых, мы добавили несколько вычисляемых свойств , которые будут действовать как точки (как если бы окно было создано в раскадровке или XIB-файле):

public NSButton ClickMeButton { get; set;}
public NSTextField ClickMeLabel { get ; set;}

Они помогут нам получить доступ к элементам пользовательского интерфейса, которые будут отображаться в окне. Так как окно не раздувается из раскадровки или XIB-файла, нам нужен способ создания экземпляра MainWindowController (как мы увидим далее в классе). Вот что делает этот новый метод конструктора:

public MainWindow(CGRect contentRect, NSWindowStyle aStyle, NSBackingStore bufferingType, bool deferCreation): base (contentRect, aStyle,bufferingType,deferCreation) {
    ...
}

Здесь мы создадим макет окна и поместим все элементы пользовательского интерфейса, необходимые для создания требуемого пользовательского интерфейса. Прежде чем добавлять элементы пользовательского интерфейса в окно, он должен содержать элементы в представлении содержимого:

ContentView = new NSView (Frame);

При этом создается представление содержимого, которое заполняет окно. Теперь мы добавим первый элемент пользовательского интерфейса , в NSButtonокно:

ClickMeButton = new NSButton (new CGRect (10, Frame.Height-70, 100, 30)){
    AutoresizingMask = NSViewResizingMask.MinYMargin
};
ContentView.AddSubview (ClickMeButton);

Первое, что следует отметить здесь, заключается в том, что в отличие от iOS, macOS использует математические нотации для определения системы координат окна. Таким образом, точка источника находится в левом нижнем углу окна, со значениями справа и в правом верхнем углу окна. При создании нового NSButtonмы учитываем это, как мы определяем его положение и размер на экране.

Свойство AutoresizingMask = NSViewResizingMask.MinYMargin сообщает кнопке, что она будет оставаться в том же расположении в верхней части окна, когда окно изменяется по вертикали. Опять же, это необходимо, так как (0,0) находится в нижней левой части окна.

Наконец, метод добавляет NSButton в представление содержимого, ContentView.AddSubview (ClickMeButton) чтобы оно отображалось на экране при запуске приложения и окне.

Затем в окно добавляется метка, в которую будет отображаться количество щелчков NSButton :

ClickMeLabel = new NSTextField (new CGRect (120, Frame.Height - 65, Frame.Width - 130, 20)) {
    BackgroundColor = NSColor.Clear,
    TextColor = NSColor.Black,
    Editable = false,
    Bezeled = false,
    AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.MinYMargin,
    StringValue = "Button has not been clicked yet."
};
ContentView.AddSubview (ClickMeLabel);

Так как macOS не имеет определенного элемента пользовательского интерфейса Label , мы добавили специально стильную, не редактируемую NSTextField для действия в качестве метки. Как и кнопка раньше, размер и расположение учитывает, что (0,0) находится в нижней левой части окна. Свойство AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.MinYMargin использует или оператор для объединения двух NSViewResizingMask функций. Это приведет к тому, что метка остается в том же расположении в верхней части окна, когда окно изменяется по вертикали и увеличивается по ширине, так как окно изменяется по горизонтали.

Опять же, метод добавляет NSTextField в представление содержимого, ContentView.AddSubview (ClickMeLabel) чтобы он отображался на экране при запуске приложения и открываемом окне.

Настройка контроллера окна

Так как дизайн больше не загружается из раскадровки MainWindow или XIB-файла, нам потребуется внести некоторые изменения в контроллер окна. Измените файл MainWindowController.cs и сделайте его следующим образом:

using System;

using Foundation;
using AppKit;
using CoreGraphics;

namespace MacXibless
{
    public partial class MainWindowController : NSWindowController
    {
        public MainWindowController (IntPtr handle) : base (handle)
        {
        }

        [Export ("initWithCoder:")]
        public MainWindowController (NSCoder coder) : base (coder)
        {
        }

        public MainWindowController () : base ("MainWindow")
        {
            // Construct the window from code here
            CGRect contentRect = new CGRect (0, 0, 1000, 500);
            base.Window = new MainWindow(contentRect, (NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Resizable), NSBackingStore.Buffered, false);

            // Simulate Awaking from Nib
            Window.AwakeFromNib ();
        }

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

        public new MainWindow Window {
            get { return (MainWindow)base.Window; }
        }
    }
}

Давайте обсудим ключевые элементы этого изменения.

Сначала мы определим новый экземпляр MainWindow класса и назначим его свойству базового контроллера Window окна:

CGRect contentRect = new CGRect (0, 0, 1000, 500);
base.Window = new MainWindow(contentRect, (NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Resizable), NSBackingStore.Buffered, false);

Мы определяем расположение окна экрана с помощью .CGRect Как и система координат окна, экран определяет (0,0) в левом нижнем углу. Далее мы определим стиль окна с помощью оператора Or для объединения двух или нескольких NSWindowStyle функций:

... (NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Resizable) ...

Доступны следующие NSWindowStyle функции:

  • Без границы — окно не будет иметь границы.
  • Заголовок — окно будет иметь строку заголовка.
  • Closable — окно имеет кнопку закрытия и может быть закрыта.
  • Минимализируемый — окно имеет кнопку миниатуризации и может быть свернута.
  • Можно изменить размер. Окно будет иметь кнопку изменения размера и изменить размер.
  • Служебная программа — это окно стиля служебной программы (панель).
  • DocModal — если окно является панелью, она будет модальным документом вместо модала системы.
  • NonactivatingPanel — если окно является панелью, она не будет выполнена в главном окне.
  • TexturedBackground — окно будет иметь текстурированный фон.
  • Немасштабированное — окно не будет масштабироваться.
  • UnifiedTitleAndToolbar — области заголовка окна и панели инструментов будут присоединены.
  • Hud — окно будет отображаться как панель отображения головы.
  • FullScreenWindow — окно может входить в полноэкранный режим.
  • FullSizeContentView — представление содержимого окна находится за заголовком и областью панели инструментов.

Последние два свойства определяют тип буферизации для окна и если рисунок окна будет отложен. Дополнительные сведения NSWindowsсм. в документации Apple по Windows .

Наконец, так как окно не раздувается из раскадровки или XIB-файла, необходимо имитировать его в нашем MainWindowController.cs путем вызова метода Windows AwakeFromNib :

Window.AwakeFromNib ();

Это позволит кодировать окно так же, как стандартное окно, загруженное из раскадровки или XIB-файла.

Отображение окна

При удалении раскадровки или XIB-файла и изменения MainWindow.cs и MainWindowController.cs-файлов вы будете использовать окно так же, как и любое нормальное окно, созданное в построителе интерфейсов Xcode с Xcode с Xcode-файлом.

Ниже будет создан новый экземпляр окна и его контроллера и отобразится окно на экране:

private MainWindowController mainWindowController;
...

mainWindowController = new MainWindowController ();
mainWindowController.Window.MakeKeyAndOrderFront (this);

На этом этапе, если приложение запущено, а кнопка щелкнула пару раз, отобразится следующее:

Пример запуска приложения

Добавление только окна кода

Если мы хотим добавить только код, окно xibless в существующее приложение Xamarin.Mac, щелкните правой кнопкой мыши проект на панели решения и выберите "Добавить>новый файл..".В диалоговом окне "Создать файл" выберите окно Xamarin.Mac>Cocoa с контроллером, как показано ниже:

Добавление нового контроллера окна

Как и раньше, мы удалим файл раскадровки по умолчанию или XIB-файла из проекта (в данном случае SecondWindow.xib) и выполните действия, описанные в разделе "Переключение окна", чтобы использовать приведенный выше раздел "Код " для покрытия определения окна для кода.

Добавление элемента пользовательского интерфейса в окно в коде

Если окно было создано в коде или загружено из раскадровки или XIB-файла, иногда требуется добавить элемент пользовательского интерфейса в окно из кода. Например:

var ClickMeButton = new NSButton (new CGRect (10, 10, 100, 30)){
    AutoresizingMask = NSViewResizingMask.MinYMargin
};
MyWindow.ContentView.AddSubview (ClickMeButton);

Приведенный выше код создает новый NSButton и добавляет его в MyWindow экземпляр окна для отображения. В основном любой элемент пользовательского интерфейса, который можно определить в конструкторе интерфейсов Xcode в раскадровки или XIB-файле, можно создать в коде и отобразить в окне.

Определение строки меню в коде

Из-за текущих ограничений в Xamarin.Mac не рекомендуется создавать строку меню приложения Xamarin.Mac,NSMenuBar но продолжать использовать файл Main.storyboard или MainMenu.xib для его определения. Таким образом, вы можете добавлять и удалять меню и элементы меню в коде C#.

Например, измените файл AppDelegate.cs и сделайте DidFinishLaunching метод следующим:

public override void DidFinishLaunching (NSNotification notification)
{
    mainWindowController = new MainWindowController ();
    mainWindowController.Window.MakeKeyAndOrderFront (this);

    // Create a Status Bar Menu
    NSStatusBar statusBar = NSStatusBar.SystemStatusBar;

    var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable);
    item.Title = "Phrases";
    item.HighlightMode = true;
    item.Menu = new NSMenu ("Phrases");

    var address = new NSMenuItem ("Address");
    address.Activated += (sender, e) => {
        Console.WriteLine("Address Selected");
    };
    item.Menu.AddItem (address);

    var date = new NSMenuItem ("Date");
    date.Activated += (sender, e) => {
        Console.WriteLine("Date Selected");
    };
    item.Menu.AddItem (date);

    var greeting = new NSMenuItem ("Greeting");
    greeting.Activated += (sender, e) => {
        Console.WriteLine("Greetings Selected");
    };
    item.Menu.AddItem (greeting);

    var signature = new NSMenuItem ("Signature");
    signature.Activated += (sender, e) => {
        Console.WriteLine("Signature Selected");
    };
    item.Menu.AddItem (signature);
}

В приведенном выше меню "Строка состояния" создается из кода и отображается при запуске приложения. Дополнительные сведения о работе с меню см. в нашей документации по меню.

Итоги

В этой статье подробно рассматривается создание пользовательского интерфейса приложения Xamarin.Mac в коде C# в отличие от использования конструктора интерфейсов Xcode с раскадровкой или Xib-файлами.