Design de interface do usuário .storyboard/.xib-less no Xamarin.Mac

Este artigo aborda a criação da interface do usuário de um aplicativo Xamarin.Mac diretamente do código C#, sem arquivos .storyboard, .xib ou Construtor de Interfaces.

Visão geral

Ao trabalhar com C# e .NET em um aplicativo Xamarin.Mac, você tem acesso aos mesmos elementos e ferramentas da interface do usuário que um desenvolvedor que trabalha no Objective-C Xcode faz. Normalmente, ao criar um aplicativo Xamarin.Mac, você usará o Construtor de Interface do Xcode com arquivos .storyboard ou .xib para criar e manter a interface do usuário do aplicativo.

Você também tem a opção de criar parte ou toda a interface do usuário do seu aplicativo Xamarin.Mac diretamente no código C#. Neste artigo, abordaremos os conceitos básicos da criação de interfaces de usuário e elementos de interface do usuário em código C#.

The Visual Studio for Mac code editor

Alternando uma janela para usar código

Ao criar um novo aplicativo Xamarin.Mac Cocoa, você obtém uma janela padrão em branco por padrão. Essa janela é definida em um arquivo Main.storyboard (ou tradicionalmente um MainWindow.xib) incluído automaticamente no projeto. Isso também inclui um arquivo ViewController.cs que gerencia a exibição principal do aplicativo (ou novamente tradicionalmente um MainWindow.cs e um arquivo MainWindowController.cs ).

Para alternar para uma janela Xibless para um aplicativo, faça o seguinte:

  1. Abra o aplicativo que você deseja parar de usar .storyboard ou arquivos .xib para definir a interface do usuário no Visual Studio para Mac.

  2. No Solution Pad, clique com o botão direito do mouse no arquivo Main.storyboard ou MainWindow.xib e selecione Remover:

    Removing the main storyboard or window

  3. Na caixa de diálogo Remover, clique no botão Excluir para remover o .storyboard ou .xib completamente do projeto:

    Confirming the deletion

Agora precisaremos modificar o arquivo MainWindow.cs para definir o layout da janela e modificar o arquivo ViewController.cs ou MainWindowController.cs para criar uma instância de nossa MainWindow classe, já que não estamos mais usando o arquivo .storyboard ou .xib.

Os aplicativos Xamarin.Mac modernos que usam Storyboards para sua interface de usuário podem não incluir automaticamente os arquivos MainWindow.cs, ViewController.cs ou MainWindowController.cs . Conforme necessário, basta adicionar uma nova classe C# vazia ao projeto (Adicionar>novo arquivo...>General>Empty Class) e nomeie-o da mesma forma que o arquivo ausente.

Definindo a janela no código

Em seguida, edite o arquivo MainWindow.cs e torne-o parecido com o seguinte:

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

    }
}

Vamos discutir alguns dos principais elementos.

Primeiro, adicionamos algumas Propriedades Computadas que funcionarão como saídas (como se a janela fosse criada em um arquivo .storyboard ou .xib):

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

Isso nos dará acesso aos elementos da interface do usuário que exibiremos na janela. Como a janela não está sendo inflada de um arquivo .storyboard ou .xib, precisamos de uma maneira de instanciá-la (como veremos mais adiante MainWindowController na aula). É isso que este novo método de construtor faz:

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

É aqui que projetaremos o layout da janela e colocaremos todos os elementos de interface do usuário necessários para criar a interface do usuário necessária. Antes de podermos adicionar qualquer elemento da interface do usuário a uma janela, ela precisa de uma Exibição de conteúdo para conter os elementos:

ContentView = new NSView (Frame);

Isso cria um Modo de Exibição de Conteúdo que preencherá a janela. Agora adicionamos nosso primeiro elemento da interface do usuário, um NSButton, à janela:

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

A primeira coisa a notar aqui é que, ao contrário do iOS, o macOS usa notação matemática para definir seu sistema de coordenadas de janela. Assim, o ponto de origem está no canto inferior esquerdo da janela, com os valores aumentando para a direita e em direção ao canto superior direito da janela. Quando criamos o novo NSButton, levamos isso em conta ao definirmos sua posição e tamanho na tela.

A AutoresizingMask = NSViewResizingMask.MinYMargin propriedade informa ao botão que queremos que ele permaneça no mesmo local a partir da parte superior da janela quando a janela for redimensionada verticalmente. Novamente, isso é necessário porque (0,0) está na parte inferior esquerda da janela.

Finalmente, o ContentView.AddSubview (ClickMeButton) método adiciona o NSButton à exibição de conteúdo para que ele seja exibido na tela quando o aplicativo for executado e a janela exibida.

Em seguida, um rótulo é adicionado à janela que exibirá o número de vezes que o NSButton foi clicado:

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);

Como o macOS não tem um elemento específico da interface do usuário do Label , adicionamos um especialmente estilizado e não editável NSTextField para atuar como um Label. Assim como o botão anterior, o tamanho e a localização levam em conta que (0,0) está na parte inferior esquerda da janela. A AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.MinYMargin propriedade está usando o operador or para combinar dois NSViewResizingMask recursos. Isso fará com que o rótulo permaneça no mesmo local a partir da parte superior da janela quando a janela for redimensionada verticalmente e diminuir e aumentar em largura à medida que a janela for redimensionada horizontalmente.

Novamente, o ContentView.AddSubview (ClickMeLabel) método adiciona o NSTextField ao Modo de Exibição de Conteúdo para que ele seja exibido na tela quando o aplicativo for executado e a janela aberta.

Ajustando o controlador de janela

Como o design do não está mais sendo carregado a partir de um arquivo .storyboard ou .xib, precisaremos fazer alguns ajustes no controlador de MainWindow janela. Edite o arquivo MainWindowController.cs e faça com que ele tenha a seguinte aparência:

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; }
        }
    }
}

Vamos discutir os elementos-chave dessa modificação.

Primeiro, definimos uma nova instância da MainWindow classe e a atribuímos à propriedade do Window controlador de janela base:

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

Definimos a localização da janela de tela com um CGRectarquivo . Assim como o sistema de coordenadas da janela, a tela define (0,0) como o canto inferior esquerdo. Em seguida, definimos o estilo da janela usando o operador Or para combinar dois ou mais NSWindowStyle recursos:

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

Os seguintes NSWindowStyle recursos estão disponíveis:

  • Sem Fronteiras - A janela não terá borda.
  • Intitulado - A janela terá uma barra de título.
  • Closable - A janela tem um botão Fechar e pode ser fechada.
  • Miniaturizável - A janela tem um botão Miniaturize e pode ser minimizada.
  • Redimensionável - A janela terá um botão Redimensionar e será redimensionável.
  • Utilitário - A janela é uma janela de estilo Utilitário (painel).
  • DocModal - Se a janela for um Painel, ela será Modal de Documento em vez de Modo de Sistema.
  • NonactivatingPanel - Se a janela for um Painel, não será feita a janela principal.
  • TexturedBackground - A janela terá um fundo texturizado.
  • Sem escala - A janela não será dimensionada.
  • UnifiedTitleAndToolbar - As áreas de título e barra de ferramentas da janela serão unidas.
  • Hud - A janela será exibida como um painel de heads-up display.
  • FullScreenWindow - A janela pode entrar no modo de tela cheia.
  • FullSizeContentView - A exibição de conteúdo da janela está atrás do título e da área da barra de ferramentas.

As duas últimas propriedades definem o Tipo de Buffer para a janela e se o desenho da janela será adiado. Para obter mais informações sobre NSWindowso , consulte a documentação de Introdução ao Windows da Apple.

Finalmente, como a janela não está sendo inflada a partir de um arquivo .storyboard ou .xib, precisamos simulá-la em nosso MainWindowController.cs chamando o método windows AwakeFromNib :

Window.AwakeFromNib ();

Isso permitirá que você codifique na janela como uma janela padrão carregada de um arquivo .storyboard ou .xib.

Exibindo a janela

Com o arquivo .storyboard ou .xib removido e os arquivos MainWindow.cs e MainWindowController.cs modificados, você usará a janela como faria com qualquer janela normal criada no Construtor de Interfaces do Xcode com um arquivo .xib.

O seguinte criará uma nova instância da janela e seu controlador e exibirá a janela na tela:

private MainWindowController mainWindowController;
...

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

Neste ponto, se o aplicativo for executado e o botão clicado algumas vezes, o seguinte será exibido:

An example app run

Adicionando uma janela somente código

Se quisermos adicionar apenas um código, janela xibless a um aplicativo Xamarin.Mac existente, clique com o botão direito do mouse no projeto no Solution Pad e selecione Add>New File... Na caixa de diálogo Novo arquivo, escolha Xamarin.Mac>Cocoa Window with Controller, conforme ilustrado abaixo:

Adding a new window controller

Assim como antes, excluiremos o arquivo .storyboard ou .xib padrão do projeto (neste caso , SecondWindow.xib) e seguiremos as etapas na seção Alternando uma janela para usar código acima para cobrir a definição da janela para código.

Adicionando um elemento de interface do usuário a uma janela no código

Se uma janela foi criada em código ou carregada a partir de um arquivo .storyboard ou .xib, pode haver momentos em que desejamos adicionar um elemento de interface do usuário a uma janela a partir do código. Por exemplo:

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

O código acima cria um novo NSButton e o adiciona à instância da MyWindow janela para exibição. Basicamente, qualquer elemento da interface do usuário que pode ser definido no Construtor de Interface do Xcode em um arquivo .storyboard ou .xib pode ser criado em código e exibido em uma janela.

Definindo a barra de menus no código

Devido às limitações atuais no Xamarin.Mac, não é sugerido que você crie a barra de menus do aplicativo Xamarin.Mac no código,NSMenuBar mas continue usando o arquivo Main.storyboard ou MainMenu.xib para defini-lo. Dito isso, você pode adicionar e remover menus e itens de menu no código C#.

Por exemplo, edite o arquivo AppDelegate.cs e faça com que o DidFinishLaunching método tenha a seguinte aparência:

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);
}

O acima cria um menu da barra de status a partir do código e o exibe quando o aplicativo é iniciado. Para obter mais informações sobre como trabalhar com menus, consulte nossa documentação de menus .

Resumo

Este artigo deu uma olhada detalhada na criação da interface do usuário de um aplicativo Xamarin.Mac em código C#, em vez de usar o Construtor de Interface do Xcode com arquivos .storyboard ou .xib.