Ventanas in Xamarin.Mac

En este artículo, se explica cómo trabajar con ventanas y paneles en una aplicación de Xamarin.Mac. Describe cómo crear ventanas y paneles en Xcode e Interface Builder, cargarlas desde guiones gráficos y archivos .xib y trabajar con ellas mediante programación.

Cuando se trabaja con C# y .NET en una aplicación de Xamarin.Mac, se tiene acceso a las mismas ventanas y paneles que un desarrollador que trabaje en Objective-C y Xcode. Dado que Xamarin.Mac se integra directamente con Xcode, puede usar Interface Builder de Xcode para crear y mantener sus ventanas y paneles (u opcionalmente crearlos directamente en código C#).

Según su finalidad, una aplicación de Xamarin.Mac puede presentar una o varias Ventanas en pantalla para administrar y coordinar la información que se muestra y con la que trabaja. Las funciones principales de una ventana son:

  1. Para proporcionar un área en la que se pueden colocar y administrar vistas y controles.
  2. Para aceptar y responder a eventos en respuesta a la interacción del usuario con el teclado y el ratón.

Se pueden usar ventanas en un estado Modeless (por ejemplo, un editor de texto que puede tener varios documentos abiertos a la vez) o Modal (por ejemplo, un cuadro de diálogo Exportar que se debe descartar antes de que la aplicación pueda continuar).

Los paneles son un tipo especial de Ventana (una subclase de la clase base NSWindow), que normalmente sirve una función auxiliar en una aplicación, como ventanas de utilidad como inspectores de formato de texto y selector de colores del sistema.

Editing a window in Xcode

En este artículo, trataremos los conceptos básicos de trabajar con ventanas y paneles en una aplicación de Xamarin.Mac. Se recomienda encarecidamente trabajar primero en el artículo Hello, Mac, específicamente en las secciones Introducción a Xcode e Interface Builder y Salidas y acciones, ya que trata conceptos clave y técnicas que usaremos en este artículo.

Es posible que quiera echar un vistazo a la sección Exponer clases o métodos de C# a Objective-C del documento sobre Aspecto internos de Xamarin.Mac, ya que explica los comandos Register y Export que se usan para conectar las clases de C# a objetos Objective-C y elementos de la interfaz de usuario.

Introducción a las ventanas

Como se indicó anteriormente, una ventana proporciona un área en la que se pueden colocar y administrar las vistas y los controles y responde a eventos basados en la interacción del usuario (ya sea mediante el teclado o el ratón).

Según Apple, hay cinco tipos principales de ventanas en una aplicación macOS:

  • Ventana de documento: una ventana de documento contiene datos de usuario basados en archivos, como una hoja de cálculo o un documento de texto.
  • Ventana de la aplicación: una ventana de aplicación es la ventana principal de una aplicación que no está basada en documentos (como la aplicación Calendario en un Mac).
  • Panel: un panel flota encima de otras ventanas y proporciona herramientas o controles con los que los usuarios pueden trabajar mientras los documentos están abiertos. En algunos casos, un panel puede ser translúcido (por ejemplo, al trabajar con gráficos grandes).
  • Cuadro de diálogo: aparece un cuadro de diálogo en respuesta a una acción del usuario y normalmente proporciona formas en que los usuarios pueden completar la acción. Un cuadro de diálogo requiere una respuesta del usuario antes de que se pueda cerrar. (Consulte Trabajar con cuadros de diálogo)
  • Alertas: una alerta es un tipo especial de diálogo que aparece cuando se produce un problema grave (por ejemplo, un error) o como una advertencia (por ejemplo, preparar para eliminar un archivo). Dado que una alerta es un cuadro de diálogo, también requiere una respuesta del usuario antes de que se pueda cerrar. (Consulte Trabajar con alertas)

Para obtener más información, consulte la sección Acerca de las ventanas de los temas de diseño de macOS de Apple.

Ventanas principal, clave e inactivas

Las ventanas en una aplicación de Xamarin.Mac pueden tener un aspecto y comportamiento diferente en función de cómo el usuario interactúe actualmente con ellos. La ventana principal del documento o la aplicación que se centra actualmente en la atención del usuario se denomina Ventana principal. En la mayoría de los casos, esta ventana también será la Ventana clave (la ventana que acepta actualmente la entrada del usuario). Pero este no siempre es el caso. Por ejemplo, un Selector de colores podría estar abierto y ser la Ventana clave con la que el usuario interactúa para cambiar el estado de un elemento en la Ventana del documento (que seguiría siendo la Ventana principal).

Las Ventanas principal y clave (si son independientes) siempre están activas, las Ventanas inactivas son ventanas abiertas que no están en primer plano. Por ejemplo, una aplicación de editor de texto podría tener más de un documento abierto a la vez, solo la ventana principal estaría activa, todas las demás estarían inactivas.

Para obtener más información, consulte la sección Acerca de las ventanas de los temas de diseño de macOS de Apple.

Ponerle nombre a las ventanas

Una ventana puede mostrar una barra de título y, cuando se muestra el título, suele ser el nombre de la aplicación, el nombre del documento en el que se trabaja o la función de la ventana (como Inspector). Algunas aplicaciones no muestran una barra de título porque son reconocibles por la vista y no funcionan con documentos.

Apple sugiere las siguientes directrices:

  • Use el nombre de la aplicación para el título de una ventana principal que no sea de documento.
  • Asigne un nombre a una nueva ventana de documento untitled. Para el primer documento nuevo, no anexe un número al título (por ejemplo untitled 1). Si el usuario crea otro nuevo documento antes de guardar y crear el título primero, llame a esa ventana untitled 2, untitled 3, etc.

Para obtener más información, consulte la sección Asignar nombre a las ventanas de los temas de diseño de macOS de Apple.

Ventanas de pantalla completa

En macOS, la ventana de una aplicación puede ponerse en pantalla completa para ocultar todo, lo que incluye la barra de menús de la aplicación (que se puede revelar moviendo el cursor a la parte superior de la pantalla) para proporcionar una interacción libre de distracciones con su contenido.

Apple sugiere las siguientes directrices:

  • Determine si tiene sentido que una ventana se ponga en pantalla completa. Las aplicaciones que proporcionan interacciones breves (como una calculadora) no deben proporcionar un modo de pantalla completa.
  • Muestra la barra de herramientas si la tarea en pantalla completa lo requiere. Normalmente, la barra de herramientas está oculta mientras está en modo de pantalla completa.
  • La ventana en pantalla completa debe tener todas las características que los usuarios necesitan para completar la tarea.
  • Si es posible, evite la interacción de Finder mientras el usuario está en una ventana en pantalla completa.
  • Aproveche el aumento del espacio de pantalla sin cambiar el foco de la tarea principal.

Para obtener más información, consulte la sección Ventanas en pantalla completa de los temas de diseño de macOS de Apple.

Paneles

Un Panel es una ventana auxiliar que contiene controles y opciones que afectan al documento o la selección activos (como el Selector de colores del sistema):

A color panel

Los paneles pueden ser específicos de la aplicación o en todo el sistema. Los paneles específicos de la aplicación flotan sobre la parte superior de las ventanas de documento de la aplicación y desaparecen cuando la aplicación está en segundo plano. Paneles de todo el sistema (como el panel Fuentes), flota en la parte superior de todas las ventanas abiertas, independientemente de la aplicación.

Apple sugiere las siguientes directrices:

  • En general, use un panel estándar, los paneles transparentes solo se deben usar con moderación y para tareas gráficamente intensivas.
  • Considere la posibilidad de usar un panel para proporcionar a los usuarios acceso fácil a controles o información importantes que afectan directamente a su tarea.
  • Oculte y muestre los paneles según sea necesario.
  • Los paneles siempre deben incluir la barra de título.
  • Los paneles no deben incluir un botón de minimización activo.

Inspectores

La mayoría de las aplicaciones macOS modernas presentan controles auxiliares y opciones que afectan al documento o la selección activos como inspectores que forman parte de la Ventana principal (como la aplicación Páginas que se muestra a continuación), en lugar de usar Ventanas de panel:

An example inspector

Para obtener más información, consulte la sección Paneles de los temas de diseño de macOS de Apple y nuestra aplicación de ejemplo MacInspector para obtener una implementación completa de una interfaz de Inspector en una aplicación de Xamarin.Mac.

Creación y mantenimiento de ventanas en Xcode

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 .storyboard incluido automáticamente en el proyecto. Para editar el diseño de las ventanas, en el Explorador de soluciones, haga doble clic en el archivo Main.storyboard:

Selecting the main storyboard

Se abrirá el diseño de la ventana en Interface Builder de Xcode:

Editing the UI in Xcode

En el Inspector de atributos, hay varias propiedades que puede usar para definir y controlar la ventana:

  • Título: este es el texto que se mostrará en la barra de título de la ventana.
  • Autoguardado: esta es la clave que se usará para identificar la ventana cuando se guarde automáticamente la posición y la configuración.
  • Barra de título: hace que la ventana muestre una barra de título.
  • Título unificado y barra de herramientas: si la ventana incluye una barra de herramientas, debe formar parte de la barra de título.
  • Vista de contenido de tamaño completo: permite que el área de contenido de la ventana esté debajo de la barra de título.
  • Sombra: si la ventana tiene una sombra.
  • Textura: las ventanas con textura pueden usar efectos (como la vibración) y se pueden mover arrastrando cualquier lugar del cuerpo.
  • Cerrar: si la ventana tiene un botón de cerrar.
  • Minimizar: si la ventana tiene un botón de minimizar.
  • Cambio de tamaño: si la ventana tiene un control de cambio de tamaño.
  • Botón de barra de herramientas: si tiene la ventana un botón ocultar o mostrar la barra de herramientas.
  • Restauración: es la posición y la configuración de la ventana que se guardan y restauran automáticamente.
  • Visible en el inicio: si la ventana que se muestra automáticamente cuando se carga el archivo .xib.
  • Ocultar al desactivar: si la ventana se oculta cuando la aplicación entra en segundo plano.
  • Liberar cuando se cierra: si la ventana se purga de la memoria cuando se cierra.
  • Mostrar siempre información sobre herramientas: si las informaciones sobre herramientas se muestran constantemente.
  • Recalcula el bucle de vista: es el orden de vista recalculado antes de dibujar la ventana.
  • Espacios, Exposé y Ciclismo: todos definen cómo se comporta la ventana en esos entornos macOS.
  • Pantalla completa: determina si esta ventana puede entrar en el modo de pantalla completa.
  • Animación: controla el tipo de animación disponible para la ventana.
  • Apariencia: controla la apariencia de la ventana. Por ahora solo hay una apariencia, Aqua.

Consulte la documentación de Introducción a las ventanas y NSWindow de Apple para obtener más información.

Establecer el tamaño y la ubicación predeterminados

Para establecer la posición inicial de la ventana y para controlar su tamaño, cambie al Inspector de tamaño:

The default size and location

Desde aquí puede establecer el tamaño inicial de la ventana, asignarle un tamaño mínimo y máximo, establecer la ubicación inicial en la pantalla y controlar los bordes alrededor de la ventana.

Establecer un controlador de ventana principal personalizado

Para poder crear salidas y acciones para exponer elementos de la interfaz de usuario al código de C#, la aplicación de Xamarin.Mac deberá usar un controlador de ventana personalizado.

Haga lo siguiente:

  1. Abra el Guion gráfico de la aplicación en el Interface Builder de Xcode.

  2. Seleccione el NSWindowController en la superficie de diseño.

  3. Cambie a la vista Inspector de identidad y escriba WindowController como Nombre de clase:

    Setting the class name

  4. Guarde los cambios y vuelva a Visual Studio para Mac para sincronizar.

  5. Se agregará un archivo WindowController.cs a su proyecto en el Explorador de soluciones de Visual Studio para Mac:

    Selecting the windows controller

  6. Vuelva a abrir el Guion gráfico en el Interface Builder de Xcode.

  7. El archivo WindowController.h estará disponible para su uso:

    Editing the WindowController.h file

Agregar elementos de UI

Para definir el contenido de una ventana, arrastre los controles del Inspector de biblioteca al Editor de interfaz. Consulte nuestra documentación de Introducción a Xcode e Interface Builder para obtener más información sobre el uso de Interface Builder para crear y habilitar controles.

Por ejemplo, vamos a arrastrar una barra de herramientas desde el Inspector de biblioteca a la ventana en el Editor de interfaz:

Selecting a Toolbar from the Library

A continuación, arrastre una Vista de texto y ajuste el tamaño para rellenar el área de debajo de la barra de herramientas:

Adding a Text View

Dado que queremos que la Vista de texto se reduzca y crezca a medida que cambia el tamaño de la ventana, cambiemos al Editor de restricciones y agreguemos las restricciones siguientes:

Editing constraints

Al hacer clic en las cuatro vigas rojas en la parte superior del editor y al hacer clic en Agregar 4 restricciones, se indica a la vista de texto que se quede en las coordenadas X,Y proporcionadas y que aumente o reduzca horizontal y verticalmente a medida que se cambia el tamaño de la ventana.

Por último, exponga la Vista de texto al código mediante una salida (asegurándose de seleccionar el archivo ViewController.h):

Configuring an outlet

Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarse con Xcode.

Para obtener más información sobre cómo trabajar con salidas y acciones, consulte nuestra documentación de Salida y acción.

Flujo de trabajo de ventana estándar

Para cualquier ventana con la que cree y trabaje en la aplicación de Xamarin.Mac, el proceso es básicamente el mismo que el que acabamos de hacer anteriormente:

  1. Para las nuevas ventanas que no son el valor predeterminado agregado automáticamente al proyecto, agregue una nueva definición de ventana al proyecto. Esto se tratará con detalle a continuación.
  2. Haga doble clic en el archivo Main.storyboard para abrir el diseño de la ventana para su edición en el Interface Builder de Xcode.
  3. Arrastre una nueva ventana al diseño de la interfaz de usuario y enganche la ventana a la ventana principal mediante Segues (para obtener más información, consulte la sección Segues de nuestra documentación Trabajar con guiones gráficos).
  4. Establezca las propiedades de ventana necesarias en el Inspector de atributos y en el Inspector de tamaño.
  5. Arrastre los controles necesarios para compilar la interfaz y configúrelos en el Inspector de atributos.
  6. Use el Inspector de tamaño para controlar el cambio de tamaño de los elementos de la interfaz de usuario.
  7. Exponga los elementos de la interfaz de usuario de la ventana al código de C# a través de salidas y acciones.
  8. Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarse con Xcode.

Ahora que tenemos una ventana básica creada, veremos los procesos típicos que realiza una aplicación de Xamarin.Mac al trabajar con ventanas.

Mostrar la ventana predeterminada

De forma predeterminada, una nueva aplicación de Xamarin.Mac mostrará automáticamente la ventana definida en el archivo MainWindow.xib cuando se inicie:

An example window running

Puesto que hemos modificado el diseño de esa ventana anterior, ahora incluye un control predeterminado de Barra de herramientas y Vista de texto. La siguiente sección del archivo Info.plist es responsable de mostrar esta ventana:

Editing Info.plist

La lista desplegable de Interfaz principal se usa para seleccionar el Guion gráfico que se usará como la interfaz de usuario de la aplicación principal (en este caso Main.storyboard).

Un controlador de vista se agrega automáticamente al proyecto para controlar que se muestran las Ventanas principales (junto con su Vista principal). Se define en el archivo ViewController.cs y se adjunta al propietario del archivo en Interface Builder en el Inspector de identidad:

Setting the file's owner

Para nuestra ventana, nos gustaría que tenga un título de untitled cuando se abre por primera vez, por lo que vamos a invalidar el método ViewWillAppear en el ViewController.cs para que tenga un aspecto similar al siguiente:

public override void ViewWillAppear ()
{
    base.ViewWillAppear ();

    // Set Window Title
    this.View.Window.Title = "untitled";
}

Nota:

La propiedad Title de la ventana se establece en el método ViewWillAppear en lugar del método ViewDidLoad porque, mientras que la vista podría cargarse en la memoria, aún no se ha creado una instancia completa. Acceso a la propiedad Title en el método ViewDidLoad obtendremos una excepción null, ya que la ventana aún no se ha construido y conectado a la propiedad.

Cierre de una ventana emergente mediante programación

Puede haber ocasiones en las que quiera cerrar una ventana en una aplicación de Xamarin.Mac mediante programación, en vez de hacer que el usuario haga clic en el botón Cerrar de la ventana o mediante un elemento de menú. macOS proporciona dos maneras diferentes de cerrar un objeto NSWindow mediante programación: PerformClose y Close.

PerformClose

Al llamar al método PerformClose de un NSWindow simula que el usuario hace clic en el botón Cerrar de la ventana resaltando momentáneamente el botón y cerrando la ventana.

Si la aplicación implementa el evento WillClose de NSWindow, se generará antes de que se cierre la ventana. Si el evento devuelve false, la ventana no se cerrará. Si la ventana no tiene un botón Cerrar o no se puede cerrar por ningún motivo, el sistema operativo emitirá el sonido de la alerta.

Por ejemplo:

MyWindow.PerformClose(this);

Intentaría cerrar la instancia MyWindowNSWindow. Si se realizó correctamente, se cerrará la ventana; de lo contrario, se emitirá el sonido de alerta y permanecerá abierto.

Cerrado

Al llamar al método Close de un NSWindow no simula que el usuario hace clic en el botón Cerrar de la ventana resaltando momentáneamente el botón, simplemente cierra la ventana.

No es necesario que una ventana esté visible para cerrarse y se publicará una notificación NSWindowWillCloseNotification en el Centro de notificaciones predeterminado para que se cierre la ventana.

El método Close difiere de dos maneras importantes del método PerformClose:

  1. No intenta generar el evento WillClose.
  2. No simula que el usuario haga clic en el botón Cerrar resaltando momentáneamente el botón.

Por ejemplo:

MyWindow.Close();

Se cerraría la instancia MyWindowNSWindow.

Contenido modificado de la ventana

En macOS, Apple ha proporcionado una manera de informar al usuario de que el usuario ha modificado el contenido de una ventana (NSWindow) y debe guardarse. Si la ventana contiene contenido modificado, se mostrará un pequeño punto negro en el widget Cerrar:

A window with the modified marker

Si el usuario intenta cerrar la ventana o salir de la aplicación de Mac mientras hay cambios no guardados en el contenido de la ventana, debe presentar un Cuadro de diálogo u Hoja modal y permitir al usuario guardar primero sus cambios:

A save sheet being shown when the window is closed

Marcar una ventana como modificada

Para marcar una ventana como un contenido modificado, use el código siguiente:

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

Y una vez guardado el cambio, desactive la marca modificada mediante:

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

Guardar cambios antes de cerrar una ventana

Para ver al usuario cerrar una ventana y permitirle guardar el contenido modificado de antemano, deberá crear una subclase de NSWindowDelegate e invalidar su método WindowShouldClose. Por ejemplo:

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

Use el código siguiente para adjuntar una instancia de este delegado a la ventana:

// Set delegate
Window.Delegate = new EditorWindowDelegate(Window);

Guardar cambios antes de cerrar la aplicación

Por último, la aplicación de Xamarin.Mac debe comprobar si alguna de sus Ventanas contiene contenido modificado y permite al usuario guardar los cambios antes de salir. Para ello, edite el archivo AppDelegate.cs, invalide el método ApplicationShouldTerminate y haga que tenga un aspecto similar al siguiente:

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

Trabajar con varios ventanas

La mayoría de las aplicaciones basadas en Mac en documentos pueden editar varios documentos al mismo tiempo. Por ejemplo, un editor de texto puede tener abiertos varios archivos de texto para su edición al mismo tiempo. De forma predeterminada, una nueva aplicación de Xamarin.Mac tiene un menú Archivo con un nuevo elemento conectado automáticamente a la newDocument:acción.

El código siguiente activará este nuevo elemento y permitirá al usuario abrir varias copias de la ventana principal para editar varios documentos a la vez.

Edite el archivo AppDelegate.cs y agregue la siguiente propiedad del proceso:

public int UntitledWindowCount { get; set;} =1;

Úselo para realizar un seguimiento del número de archivos no guardados para que podamos enviar comentarios al usuario (según las directrices de Apple, como se ha descrito anteriormente).

Después, agregue el método siguiente:

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

Este código crea una nueva versión de nuestro controlador de ventanas, carga la nueva ventana, la convierte en ventana principal y clave y la establece como título. Ahora, si ejecutamos nuestra aplicación y seleccionamos Nuevo en el menú Archivo, se abrirá una nueva ventana del editor y se mostrará:

A new untitled window was added

Si abrimos el menú Ventanas, puede ver que la aplicación realiza un seguimiento automático y controla nuestras ventanas abiertas:

The windows menu

Para obtener más información sobre cómo trabajar con Menús en una aplicación de Xamarin.Mac, vea nuestra documentación de Trabajar con menús.

Obtener la ventana activa actualmente

En una aplicación de Xamarin.Mac que puede abrir varias ventanas (documentos), hay ocasiones en las que tendrá que obtener la ventana actual y superior (la ventana clave). El código siguiente devolverá la ventana clave:

var window = NSApplication.SharedApplication.KeyWindow;

Se puede llamar a en cualquier clase o método que necesite tener acceso a la ventana clave actual. Si no hay ninguna ventana abierta actualmente, devolverá null.

Acceso a todas las ventanas de la aplicación

Es posible que haya ocasiones en las que necesite acceder a todas las ventanas que la aplicación de Xamarin.Mac tiene abiertas actualmente. Por ejemplo, para ver si un archivo que el usuario quiera abrir ya está abierto en una ventana de salida.

El NSApplication.SharedApplication mantiene una propiedad Windows que contiene una matriz de todas las ventanas abiertas de la aplicación. Puede iterar en esta matriz para acceder a todas las ventanas actuales de la aplicación. Por ejemplo:

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

En el código de ejemplo se convierte cada ventana devuelta a la clase personalizada ViewController de nuestra aplicación y se prueba el valor de una propiedad personalizada Path en la ruta de acceso de un archivo que el usuario quiere abrir. Si el archivo ya está abierto, vamos a traer esa ventana al frente.

Ajustar el tamaño de la ventana en el código

Hay ocasiones en las que la aplicación necesita cambiar el tamaño de una ventana en el código. Para cambiar el tamaño y cambiar la posición de una ventana, ajuste su propiedad Frame. Al ajustar el tamaño de una ventana, normalmente debe ajustar también su origen para mantener la ventana en la misma ubicación debido al sistema de coordenadas de macOS.

A diferencia de iOS, donde la esquina superior izquierda representa (0,0), macOS usa un sistema de coordenadas matemáticas donde la esquina inferior izquierda de la pantalla representa (0,0). En iOS, las coordenadas aumentan a medida que avanza hacia abajo y hacia la derecha. En macOS, las coordenadas aumentan de valor hacia arriba y hacia la derecha.

El código de ejemplo siguiente cambia el tamaño de una ventana:

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

Importante

Al ajustar un tamaño y una ubicación de ventanas en el código, debe asegurarse de respetar los tamaños mínimo y máximo que ha establecido en Interface Builder. Esto no se respetará automáticamente y podrá hacer que la ventana sea más grande o más pequeña que estos límites.

Supervisor cambios de tamaño de ventana

Es posible que haya ocasiones en las que necesite supervisar los cambios en el tamaño de una ventana dentro de la aplicación Xamarin.Mac. Por ejemplo, para volver a dibujar el contenido para ajustarse al nuevo tamaño.

Para supervisar los cambios de tamaño, asegúrese primero de que ha asignado una clase personalizada para el controlador de ventanas en el Interface Builder de Xcode. Por ejemplo, MasterWindowController en el siguiente:

The Identity Inspector

A continuación, edite la clase personalizada del controlador de ventana y supervise el evento DidResize en la ventana del controlador para recibir una notificación de los cambios de tamaño activos. Por ejemplo:

public override void WindowDidLoad ()
{
    base.WindowDidLoad ();

    Window.DidResize += (sender, e) => {
        // Do something as the window is being live resized
    };
}

Opcionalmente, puede usar el evento DidEndLiveResize para recibir una notificación solo después de que el usuario haya terminado de cambiar el tamaño de la ventana. Por ejemplo:

public override void WindowDidLoad ()
{
    base.WindowDidLoad ();

        Window.DidEndLiveResize += (sender, e) => {
        // Do something after the user's finished resizing
        // the window
    };
}

Establecer el título y el archivo representado de una ventana

Cuando se trabaja con ventanas que representan documentos, NSWindow tiene una propiedad DocumentEdited que si se establece en true muestra un pequeño punto en el botón Cerrar para dar al usuario una indicación de que el archivo se ha modificado y se debe guardar antes de cerrar.

Vamos a editar nuestro archivo ViewController.cs y realizar los siguientes cambios:

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

}

También estamos supervisando el evento WillClose en la ventana y comprobando el estado de la propiedad DocumentEdited. Si es true, tenemos que proporcionar al usuario la capacidad de guardar los cambios en el archivo. Si ejecutamos nuestra aplicación y escribimos texto, se mostrará el punto:

A changed window

Si intenta cerrar la ventana, recibirá una alerta:

Displaying a save dialog

Si va a cargar un documento desde un archivo, establezca el título de la ventana en el nombre del archivo mediante el método window.SetTitleWithRepresentedFilename (Path.GetFileName(path)); (dado que path es una cadena que representa el archivo que se va a abrir). Además, puede establecer la dirección URL del archivo mediante el método window.RepresentedUrl = url;.

Si la dirección URL apunta a un tipo de archivo conocido por el sistema operativo, su icono se mostrará en la barra de título. Si el usuario hace clic con el botón derecho en el icono, se mostrará la ruta de acceso al archivo.

Abra el archivo AppDelegate.cs y agregue el método siguiente:

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

        }
    }
}

Ahora, si ejecutamos nuestra aplicación, seleccione Abrir... en el menú Archivo, seleccione un archivo de texto en el cuadro de diálogo Abrir y ábralo:

An open dialog box

El archivo se mostrará y el título se establecerá con el icono del archivo:

The contents of a file loaded

Agregar una nueva ventana a un proyecto

Aparte de la ventana principal del documento, es posible que una aplicación de Xamarin.Mac tenga que mostrar otros tipos de ventanas al usuario, como Preferencias o Paneles de inspectores.

Para agregar una nueva ventana, haga lo siguiente:

  1. En el Explorador de soluciones, haga doble clic en el archivo Main.storyboard para abrirlo y editarlo en Interface Builder de Xcode.

  2. Arrastre un nuevo controlador de ventana desde la biblioteca y colóquelo en la superficie de diseño:

    Selecting a new Window Controller in the Library

  3. En el Inspector de identidad, escriba PreferencesWindow para el Id. del guion gráfico:

    Setting the storyboard ID

  4. Diseñe la interfaz:

    Designing the UI

  5. Abra el menú de la aplicación (MacWindows), seleccione Preferencias..., Control+Clic y arrástrelo a la nueva ventana:

    Creating a segue

  6. En el menú emergente, seleccione Mostrar.

  7. Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.

Si ejecutamos el código y selecciona lasPreferencias... en el Menú aplicación, se mostrará la ventana:

A sample preferences menu

Trabajar con paneles

Como se indica al principio de este artículo, un panel flota sobre otras ventanas y proporciona herramientas o controles con los que los usuarios pueden trabajar mientras los documentos están abiertos.

Igual que en cualquier otro tipo de ventana con la que cree y trabaje en la aplicación de Xamarin.Mac, el proceso es básicamente el mismo:

  1. Agregue una nueva definición de ventana al proyecto.
  2. Haga doble clic en el archivo .xib para abrir el diseño de la ventana para su edición en el Interface Builder de Xcode.
  3. Establezca las propiedades de ventana necesarias en el Inspector de atributos y en el Inspector de tamaño.
  4. Arrastre los controles necesarios para compilar la interfaz y configúrelos en el Inspector de atributos.
  5. Use el Inspector de tamaño para controlar el cambio de tamaño de los elementos de la interfaz de usuario.
  6. Exponga los elementos de la interfaz de usuario de la ventana al código de C# a través de salidas y acciones.
  7. Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarse con Xcode.

En el Inspector de atributos, tiene las siguientes opciones específicas de Paneles:

The Attribute Inspector

  • Estilo: permite ajustar el estilo del panel desde: Panel regular (parece una ventana estándar), Panel de utilidad (tiene una barra de título más pequeña), Panel HUD (es translúcido y la barra de título forma parte del fondo).
  • No activando: determina si en el panel se convierte en la ventana clave.
  • Modal de documento: si selecciona modal del documento, el panel solo flotará encima de las ventanas de la aplicación, de lo contrario, flota sobre todo.

Para agregar un nuevo panel, haga lo siguiente:

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>Nuevo archivo....

  2. En el cuadro de diálogo Nuevo archivo, seleccione Xamarin.Mac>Ventana de Cocoa con controlador:

    Adding a new window controller

  3. Escriba DocumentPanel para el Nombre y haga clic en el botón Nuevo.

  4. Haga doble clic en el archivo DocumentPanel.xib para abrirlo para editarlo en Interface Builder:

    Editing the panel

  5. Elimine la ventana existente y arrastre un panel desde el Inspector de biblioteca en el Editor de interfaz:

    Deleting the existing window

  6. Enlace el panel hasta la salida de la - Ventana - Propietario del archivo:

    Dragging to wire up the panel

  7. Cambie al Inspector de identidad y establezca la clase del Panel en DocumentPanel:

    Setting the panel's class

  8. Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.

  9. Edite el archivo DocumentPanel.cs y cambie la definición de clase a lo siguiente:

    public partial class DocumentPanel : NSPanel

  10. Guarde los cambios en el archivo.

Edite el archivo AppDelegate.cs y haga que el método DidFinishLaunching se parezca a lo siguiente:

public override void DidFinishLaunching (NSNotification notification)
{
        // Display panel
    var panel = new DocumentPanelController ();
    panel.Window.MakeKeyAndOrderFront (this);
}

Si ejecutamos nuestra aplicación, se mostrará el panel:

The panel in a running app

Importante

Apple ha dejado de usar las ventanas de panel y deben reemplazarse por las interfaces del inspector. Para obtener un ejemplo completo de cómo crear un Inspector en una aplicación de Xamarin.Mac, consulte nuestra aplicación de ejemplo MacInspector.

Resumen

En este artículo se ha explicado con detalle cómo se trabaja con ventanas y paneles en una aplicación de Xamarin.Mac. Hemos visto los diferentes tipos y usos de las ventanas y los paneles, cómo crear y mantener ventanas y paneles en el Interface Builder de Xcode y cómo trabajar con ventanas y paneles en código de C#.