Compartir vía


Diseño de la interfaz de usuario sin .storyboard/.xib en Xamarin.Mac

En este artículo, se describe cómo crear la interfaz de usuario de una aplicación de Xamarin.Mac directamente desde el código de C# sin usar Interface Builder, archivos .storyboard o .xib.

Información general

Cuando se trabaja con C# y .NET en una aplicación de Xamarin.Mac, se tiene acceso a los mismos elementos y herramientas de interfaz de usuario que un desarrollador que trabaje en Objective-C y Xcode. Normalmente, al crear una aplicación de Xamarin.Mac, usará el Interface Builder de Xcode con archivos .storyboard o .xib para crear y mantener la interfaz de usuario de la aplicación.

También tiene la opción de crear parte o toda la interfaz de usuario de la aplicación de Xamarin.Mac directamente en código de C#. En este artículo, trataremos los conceptos básicos de la creación y elementos de interfaz de usuario en código de C#.

Editor de código de Visual Studio para Mac

Cambiar una ventana para usar código

Al crear una nueva aplicación de Cocoa de Xamarin.Mac, obtendrá una ventana estándar en blanco de forma predeterminada. Esta ventana se define en un archivo Main.storyboard (o tradicionalmente un archivo MainWindow.xib) incluido automáticamente en el proyecto. Esto también incluye un archivo de ViewController.cs que administra la vista principal de la aplicación (o de nuevo tradicionalmente un MainWindow.cs y un archivo MainWindowController.cs).

Para cambiar a una ventana de Xibless para una aplicación, haga lo siguiente:

  1. Abra la aplicación que desea dejar de usar .storyboard o archivos .xib para definir la interfaz de usuario en Visual Studio para Mac.

  2. En el Panel de solución, haga clic con el botón derecho en el archivo Main.storyboard o MainWindow.xib y seleccione Quitar:

    Eliminación del guion gráfico principal o la ventana

  3. En el Diálogo Quitar, haga clic en el botón Eliminar para quitar completamente el .storyboard o .xib del proyecto:

    Confirmación de la eliminación

Ahora tendremos que modificar el archivo MainWindow.cs para definir el diseño de la ventana y modificar el archivo ViewController.cs o MainWindowController.cs para crear una instancia de nuestra clase MainWindow, ya que no usamos el archivo .storyboard o .xib.

Es posible que las aplicaciones modernas de Xamarin.Mac que usen Guiones gráficos para su interfaz de usuario no incluyan automáticamente los archivos de MainWindow.cs, ViewController.cs o MainWindowController.cs. Según sea necesario, basta con agregar una nueva clase vacía de C# al proyecto (Agregar >Nuevo archivo...>General>Clase vacía) y asígnele el mismo nombre que el archivo que falta.

Definición de la ventana en el código

A continuación, edite el archivo MainWindow.cs y haga que se parezca a lo siguiente:

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 a analizar algunos de los elementos clave.

En primer lugar, agregamos algunas Propiedades calculadas que actuarán como salidas (como si la ventana se creara en un archivo .storyboard o .xib):

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

Esto nos dará acceso a los elementos de la interfaz de usuario que vamos a mostrar en la ventana. Dado que la ventana no se infla desde un archivo .storyboard o .xib, necesitamos una manera de crear una instancia de ella (como veremos más adelante en la clase MainWindowController). Eso es lo que hace este nuevo método de constructor:

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

Aquí es donde realizaremos el diseño de la ventana y colocaremos los elementos de la interfaz de usuario necesarios para crearla. Antes de que podamos agregar cualquier elemento de interfaz de usuario a una ventana, necesita una Vista de contenido para contener los elementos:

ContentView = new NSView (Frame);

Esto crea una Vista de contenido que rellenará la ventana. Ahora, agregamos nuestro primer elemento de interfaz de usuario, un NSButton, a la ventana:

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

Lo primero que hay que tener en cuenta aquí es que, a diferencia de iOS, macOS usa notación matemática para definir su sistema de coordenadas de ventana. Por lo tanto, el punto de origen está en la esquina inferior izquierda de la ventana, con valores que aumentan a la derecha y hacia la esquina superior derecha de la ventana. Cuando creamos el nuevo NSButton, tenemos esto en cuenta a medida que definimos su posición y tamaño en pantalla.

La propiedad AutoresizingMask = NSViewResizingMask.MinYMargin indica al botón que queremos que permanezca en la misma ubicación desde la parte superior de la ventana cuando se cambia el tamaño de la ventana verticalmente. De nuevo, esto es necesario porque (0,0) está en la parte inferior izquierda de la ventana.

Por último, el método ContentView.AddSubview (ClickMeButton) agrega NSButton a la Vista de contenido para que se muestre en pantalla cuando se ejecute la aplicación y se muestre la ventana.

A continuación, se agrega una etiqueta a la ventana que mostrará el número de veces que se ha realizado clic en 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);

Dado que macOS no tiene un elemento de interfaz de usuario de Etiqueta específico, hemos agregado un NSTextField especialmente diseñado, no editable para actuar como Etiqueta. Al igual que el botón anterior, el tamaño y la ubicación tienen en cuenta que (0,0) está en la parte inferior izquierda de la ventana. La propiedad AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.MinYMargin usa el operador o para combinar dos características NSViewResizingMask. Esto hará que la etiqueta permanezca en la misma ubicación desde la parte superior de la ventana cuando esta cambie de tamaño verticalmente, y se reduzca y aumente de ancho, debido a que cambia de tamaño horizontalmente.

De nuevo, el métodoContentView.AddSubview (ClickMeLabel) agrega el NSTextField a la Vista de contenido para que se muestre en pantalla cuando se ejecute la aplicación y se abra la ventana.

Ajustar el controlador de ventana

Dado que el diseño de MainWindow ya no se carga desde un archivo .storyboard o .xib, es necesario realizar algunos ajustes en el controlador de ventana. Edite el archivo MainWindowController y haga que se parezca a lo siguiente:

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

Veamos los elementos clave de esta modificación.

En primer lugar, definimos una nueva instancia de la claseMainWindow y la asignamos a la propiedad Window del controlador de ventana 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 la ubicación de la ventana de la pantalla con un CGRect. Al igual que el sistema de coordenadas de la ventana, la pantalla define (0,0) como la esquina inferior izquierda. A continuación, definimos el estilo de la ventana mediante el operador Or para combinar dos o más características NSWindowStyle:

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

Las características NSWindowStyle están disponibles:

  • Sin borde: la ventana no tendrá ningún borde.
  • Titulada: la ventana tendrá una barra de título.
  • Se puede cerrar: la ventana tiene un botón Cerrar y se puede cerrar.
  • Miniaturizable: la ventana tiene un botón Miniaturizar y se puede minimizar.
  • Cambiar tamaño: la ventana tendrá un botón Cambiar tamaño y se podrá cambiar el tamaño.
  • Utilidad: es una ventana de estilo de Utilidad (panel).
  • DocModal: si la ventana es un Panel, será Modal de documento en lugar de Modal del sistema.
  • NonactivatingPanel: si la ventana es un Panel, no se convertirá en la ventana principal.
  • TexturedBackground: la ventana tendrá un fondo con textura.
  • Sin escalado: la ventana no se escalará.
  • UnifiedTitleAndToolbar: las áreas de título y barra de herramientas de la ventana se unirán.
  • Hud: la ventana se mostrará como panel de visualización frontal.
  • FullScreenWindow: la ventana puede entrar en modo de pantalla completa.
  • FullSizeContentView: la vista de contenido de la ventana está detrás del título y el área de la barra de herramientas.

Las dos últimas propiedades definen el Tipo de almacenamiento en búfer de la ventana y si se aplazará el dibujo de la ventana. Para obtener más información sobre NSWindows, consulte la documentación de Introducción a Windows de Apple.

Por último, dado que la ventana no se infla desde un archivo .storyboard o .xib, es necesario simularla en nuestro MainWindowController.cs llamando al método de windows AwakeFromNib:

Window.AwakeFromNib ();

Esto le permitirá programar en la ventana igual que una ventana estándar cargada desde un archivo .storyboard o .xib.

Mostrar la ventana

Con el archivo .storyboard o .xib eliminados y los archivos MainWindow.cs y MainWindowController.csmodificados, usará la ventana igual que lo haría con cualquier ventana normal que se hubiera creado en el Interface Builder de Xcode con un archivo .xib.

A continuación, se creará una nueva instancia de la ventana y su controlador, y se mostrará en pantalla:

private MainWindowController mainWindowController;
...

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

En este punto, si se ejecuta la aplicación y se hace clic en el botón un par de veces, se mostrará lo siguiente:

Una ejecución de aplicación de ejemplo

Adición de una ventana solo de código

Si solo queremos agregar, una ventana xibless de código a una aplicación de Xamarin.Mac existente, haga clic con el botón derecho en el proyecto en el Panel de solución y seleccione Agregar>Nuevo archivo..... En el cuadro de diálogo Nuevo archivo, elija Xamarin.Mac> Ventana de Cocoa con controlador, como se muestra a continuación:

Adición de un nuevo controlador de ventana

Al igual que antes, eliminaremos el archivo .storyboard o .xib predeterminado del proyecto (en este caso SecondWindow.xib) y seguiremos los pasos descritos en la sección anterior Cambiar una ventana para usar código para cubrir la definición de la ventana para programar.

Adición de un elemento de interfaz de usuario a una ventana en el código

Tanto si se creó una ventana en código como si se cargó desde un archivo .storyboard o .xib, puede haber ocasiones en las que queremos agregar un elemento de interfaz de usuario a una ventana desde el código. Por ejemplo:

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

El código anterior crea un nuevo NSButton y lo agrega a la instancia de ventana MyWindow para mostrarlo. Básicamente, cualquier elemento de interfaz de usuario que se pueda definir en el Interface Builder de Xcode en un archivo .storyboard o .xib se puede crear en código y mostrarse en una ventana.

Definición de la barra de menús en el código

Debido a las limitaciones actuales de Xamarin.Mac, no se recomienda crear la barra de menús de la aplicación de Xamarin.Mac –NSMenuBar–, en código en su lugar seguir usando el archivo Main.storyboard o MainMenu.xib para definirlo. Dicho esto, puede agregar y quitar menús y elementos de menú en código de C#.

Por ejemplo, edite el archivo AppDelegate.cs y haga que el método DidFinishLaunching tenga un aspecto similar al siguiente:

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

Lo anterior crea un menú Barra de estado a partir del código y lo muestra cuando se inicia la aplicación. Para obtener más información sobre cómo trabajar con menús, consulte nuestra documentación de Menús.

Resumen

Este artículo ha tomado una mirada detallada a la creación de una interfaz de usuario de la aplicación de Xamarin.Mac en el código de C# en lugar de usar el Interface Builder de Xcode con archivos .storyboard o .xib.