Menús en Xamarin.Mac

En este artículo se explica cómo trabajar con menús en una aplicación de Xamarin.Mac. Describe cómo crear y mantener menús y elementos de menú en Xcode Interface Builder y trabajar con ellos mediante programación.

Al trabajar con C# y .NET en una aplicación de Xamarin.Mac, tiene acceso a los mismos menús de Cocoa en los que trabaja un desarrollador Objective-C y Xcode. Dado que Xamarin.Mac se integra directamente con Xcode, puede usar el Interface Builder de Xcode para crear y mantener las barras de menús, los menús y los elementos de menú (o, opcionalmente, crearlos directamente en código de C#).

Los menús son una parte integral de la experiencia de usuario de una aplicación Mac y normalmente aparecen en varias partes de la interfaz de usuario:

  • Barra de menús de la aplicación: este es el menú principal que aparece en la parte superior de la pantalla de cada aplicación Mac.
  • Menús contextuales: aparecen cuando el usuario hace clic con el botón derecho o hace clic con el botón derecho en un elemento de una ventana.
  • La barra de estado: es el área del lado derecho de la barra de menús de la aplicación que aparece en la parte superior de la pantalla (a la izquierda del reloj de la barra de menús) y crece a la izquierda a medida que se agregan elementos a ella.
  • Menú Acoplar: menú de cada aplicación del dock que aparece cuando el usuario hace clic con el botón derecho o hace clic con el botón derecho en el icono de la aplicación, o cuando el usuario hace clic con el botón izquierdo en el icono y mantiene presionado el botón del mouse.
  • Botón emergente y listas desplegables: un botón emergente muestra un elemento seleccionado y presenta una lista de opciones para seleccionar cuando el usuario hace clic en él. Una lista desplegable es un tipo de botón emergente que normalmente se usa para seleccionar comandos específicos del contexto de la tarea actual. Ambos pueden aparecer en cualquier lugar de una ventana.

Menú de ejemplo Un

En este artículo, se tratarán los conceptos básicos del trabajo con barras de menús, menús y elementos de menú de Cocoa en una aplicación xamarin.mac. Se recomienda encarecidamente que trabaje primero en el artículo Hello, Mac, en concreto en las secciones Introduction to Xcode and Interface Builder and Outlets and Actions (Introducción a Xcode y Interface Builder y salidas y acciones), ya que trata los conceptos y técnicas clave que se usarán en este artículo.

También puede echar un vistazo a la sección del documento Xamarin.Mac Internals (Aspectos internos de Exposing C# classes / methods to Objective-CExposing C# classes / methods to Objective-C donde se explican los atributos y que se usan para conectar las clases de C# a objetos y elementos de interfaz de RegisterExportObjective-C usuario.

Barra de menús de la aplicación

A diferencia de las aplicaciones que se ejecutan en el sistema operativo Windows, donde cada ventana puede tener su propia barra de menús asociada, cada aplicación que se ejecuta en macOS tiene una sola barra de menús que se ejecuta en la parte superior de la pantalla que se usa para cada ventana de esa aplicación:

Una barra de menús

Los elementos de esta barra de menús se activan o desactivan en función del contexto o el estado actual de la aplicación y su interfaz de usuario en un momento dado. Por ejemplo: si el usuario selecciona un campo de texto, los elementos del menú Editar estarán habilitados, como Copiar y Cortar.

Según Apple y de forma predeterminada, todas las aplicaciones macOS tienen un conjunto estándar de menús y elementos de menú que aparecen en la barra de menús de la aplicación:

  • Menú de Apple: este menú proporciona acceso a elementos de todo el sistema que están disponibles para el usuario en todo momento, independientemente de la aplicación que se esté ejecutando. El desarrollador no puede modificar estos elementos.
  • Menú Aplicación: este menú muestra el nombre de la aplicación en negrita y ayuda al usuario a identificar qué aplicación se está ejecutando actualmente. Contiene elementos que se aplican a la aplicación en su conjunto y no a un documento o proceso determinado, como salir de la aplicación.
  • Menú Archivo: elementos usados para crear, abrir o guardar documentos con los que funciona la aplicación. Si la aplicación no está basada en documentos, se puede cambiar el nombre de este menú o quitarlo.
  • Menú Editar: contiene comandos como Cortar,Copiary Pegar que se usan para editar o modificar elementos en la interfaz de usuario de la aplicación.
  • Menú Formato: si la aplicación funciona con texto, este menú contiene comandos para ajustar el formato de ese texto.
  • Menú Ver: contiene comandos que afectan a cómo se muestra (se ve) el contenido en la interfaz de usuario de la aplicación.
  • Menús específicos de la aplicación: son los menús específicos de la aplicación (por ejemplo, un menú de marcadores para un explorador web). Deben aparecer entre los menúsVer y Ventana de la barra.
  • Menú Ventana: contiene comandos para trabajar con ventanas en la aplicación, así como una lista de ventanas abiertas actuales.
  • Menú Ayuda: si la aplicación proporciona ayuda en pantalla, el menú Ayuda debe ser el menú más a la derecha de la barra.

Para obtener más información sobre la barra de menús de la aplicación y los menús estándar y los elementos de menú, vea Instrucciones de interfaz humana deApple.

Barra de menús de la aplicación predeterminada

Cada vez que crea un nuevo proyecto de Xamarin.Mac, obtiene automáticamente una barra de menús de aplicación predeterminada estándar que tiene los elementos típicos que normalmente tendría una aplicación macOS (como se describe en la sección anterior). La barra de menús predeterminada de la aplicación se define en el archivo Main.storyboard (junto con el resto de la interfaz de usuario de la aplicación) en el proyecto de la Panel de solución:

Selección del guión gráfico principal

Haga doble clic en el archivo Main.storyboard para abrirlo para editarlo en el archivo Interface Builder Xcode y se le mostrará la interfaz del editor de menús:

Edición de la interfaz de usuario en Xcode, que muestra el guión gráfico de puntos principal.

Desde aquí podemos hacer clic en elementos como el elemento de menú Abrir del menú Archivo y editar o ajustar sus propiedades en el Inspector de atributos:

Edición de los atributos de un menú

Más adelante en este artículo, veremos cómo agregar, editar y eliminar menús y elementos. Por ahora, solo queremos ver qué menús y elementos de menú están disponibles de forma predeterminada y cómo se han expuesto automáticamente al código a través de un conjunto de salidas y acciones predefinidas (para más información, consulte nuestra documentación de salidas y acciones).

Por ejemplo, si hacemos clic en el Inspector de conexión para el elemento de menú Abrir, podemos ver que se cablea automáticamente a la acción:

Visualización de la acción adjuntaVisualización de la acción

Si selecciona el primer respondedor en la jerarquía de interfaz y se desplaza hacia abajo en el Inspectorde conexión , y verá la definición de la acción a la que está asociado el elemento de menú Abrir (junto con otras acciones predeterminadas para la aplicación que están y no se cablean automáticamente a los controles):

Visualización de todas las acciones adjuntas

¿Por qué esto es importante? En la sección siguiente verá cómo funcionan estas acciones definidas automáticamente con otros elementos de la interfaz de usuario de Cocoa para habilitar y deshabilitar automáticamente los elementos de menú, así como proporcionar funcionalidad integrada para los elementos.

Más adelante usaremos estas acciones integradas para habilitar y deshabilitar elementos del código y proporcionar nuestra propia funcionalidad cuando se seleccionen.

Funcionalidad de menú integrada

Si ha ejecutado una aplicación xamarin.Mac recién creada antes de agregar elementos de interfaz de usuario o código, observará que algunos elementos se cablean automáticamente y se habilitan automáticamente (con la funcionalidad integrada automáticamente), como el elemento Salir del menú Aplicación:

Un elemento de menú habilitado Unelemento de menú

Mientras que otros elementos de menú, como Cortar,Copiary Pegar no son:

Elementos de menú deshabilitados Elementos

Vamos a detener la aplicación y hacer doble clic en el archivo Main.storyboard de la Panel de solución abrirla para editarla en la página de Xcode Interface Builder. A continuación, arrastre una vista de texto desde la biblioteca al controlador de vista de la ventana en el Editor de interfaces:

Seleccionar una vista de texto de la bibliotecaSeleccionar una vista de texto de la

En el Editor de restricciones vamos a anclar la vista de texto a los bordes de la ventana y establecerla donde crece y se reduce con la ventana haciendo clic en los cuatro haces de I rojos en la parte superior del editor y haciendo clic en el botón Agregar 4 restricciones:

Edición de los contraints

Guarde los cambios en el diseño de la interfaz de usuario y vuelva a Visual Studio para Mac para sincronizar los cambios con el proyecto de Xamarin.Mac. Ahora, inicie la aplicación, escriba texto en la vista de texto, selecciónelo y abra el menú Editar:

Los elementos de menú se habilitan o deshabilitan automáticamente.Los elementos de

Observe cómo los elementos Cortar,Copiary Pegar se habilitan automáticamente y son totalmente funcionales, sin necesidad de escribir una sola línea de código.

¿Qué ocurre aquí? Recuerde las acciones de predefinición integradas que vienen conectadas a los elementos de menú predeterminados (como se ha presentado anteriormente), la mayoría de los elementos de la interfaz de usuario de Cocoa que forman parte de macOS tienen enlaces integrados a acciones específicas (como copy: ). Por lo tanto, cuando se agregan a una ventana, activa y seleccionada, el elemento de menú correspondiente o los elementos asociados a esa acción se habilitan automáticamente. Si el usuario selecciona ese elemento de menú, se llama y ejecuta la funcionalidad integrada en el elemento de interfaz de usuario, todo ello sin intervención del desarrollador.

Habilitación y deshabilitación de menús y elementos

De forma predeterminada, cada vez que se produce un evento de usuario, habilita y deshabilita automáticamente cada elemento de menú y menú visible en función del NSMenu contexto de la aplicación. Hay tres maneras de habilitar o deshabilitar un elemento:

  • Habilitación automática del menú: se habilita un elemento de menú si puede encontrar un objeto adecuado que responda a la acción a la que está conectado el elemento. Por ejemplo, la vista de texto anterior que tenía un enlace integrado a la copy: acción.
  • Acciones personalizadas y validateMenuItem: : para cualquier elemento de menú enlazado auna acción personalizada de controlador de ventana o vista, puede agregar la acción y habilitar o deshabilitar manualmente los elementos de menú.
  • Habilitación manual del menú: se establece manualmente la propiedad de cada elemento para habilitar o deshabilitar cada elemento de un menú NSMenuItem individualmente.

Para elegir un sistema, establezca la AutoEnablesItems propiedad de NSMenu . true es automático (el comportamiento predeterminado) y false es manual.

Importante

Si decide usar la habilitación manual del menú, ninguno de los elementos de menú, ni siquiera los controlados por clases de AppKit como NSTextView , se actualizan automáticamente. Será responsable de habilitar y deshabilitar todos los elementos a mano en el código.

Uso de validateMenuItem

Como se indicó anteriormente, para cualquier elemento de menú enlazado auna acción personalizada de controlador de ventana o vista , puede agregar la acción y habilitar o deshabilitar manualmente los elementos de menú.

En el ejemplo siguiente, la propiedad se usará para decidir el tipo de elemento de menú que la acción habilitará o deshabilitará en función del estado del texto seleccionado en TagvalidateMenuItem:NSTextView . La Tag propiedad se ha establecido en Interface Builder para cada elemento de menú:

Establecer la propiedad Tag Estableciendo

Y el código siguiente agregado al controlador de vistas:

[Action("validateMenuItem:")]
public bool ValidateMenuItem (NSMenuItem item) {

    // Take action based on the menu item type
    // (As specified in its Tag)
    switch (item.Tag) {
    case 1:
        // Wrap menu items should only be available if
        // a range of text is selected
        return (TextEditor.SelectedRange.Length > 0);
    case 2:
        // Quote menu items should only be available if
        // a range is NOT selected.
        return (TextEditor.SelectedRange.Length == 0);
    }

    return true;
}

Cuando se ejecuta este código y no se selecciona ningún texto en , los dos elementos de menú encapsular se deshabilitan (aunque estén conectados a acciones en el NSTextView controlador de vista):

Mostrar elementos deshabilitados Mostrando

Si se selecciona una sección de texto y se vuelve a abrir el menú, estarán disponibles los dos elementos de menú de ajuste:

Mostrar elementos habilitados Mostrando

Habilitación y respuesta a elementos de menú en el código

Como hemos visto anteriormente, con solo agregar elementos específicos de la interfaz de usuario de Cocoa al diseño de la interfaz de usuario (por ejemplo, un campo de texto), varios de los elementos de menú predeterminados se habilitarán y funcionarán automáticamente, sin tener que escribir código. A continuación, veamos cómo agregar nuestro propio código de C# a nuestro proyecto de Xamarin.Mac para habilitar un elemento de menú y proporcionar funcionalidad cuando el usuario lo selecciona.

Por ejemplo, supongamos que queremos que el usuario pueda usar el elemento Abrir del menú Archivo para seleccionar una carpeta. Puesto que queremos que sea una función para toda la aplicación y no se limite a una ventana de entrega o un elemento de interfaz de usuario, vamos a agregar el código para controlar esto a nuestro delegado de aplicación.

En el Panel de solución, haga doble clic en el archivo para abrirlo para su edición:

Selección del delegado de aplicaciónSelección del delegado de

Agregue el código siguiente al método DidFinishLaunching:

[Export ("openDocument:")]
void OpenDialog (NSObject sender)
{
    var dlg = NSOpenPanel.OpenPanel;
    dlg.CanChooseFiles = false;
    dlg.CanChooseDirectories = true;

    if (dlg.RunModal () == 1) {
        var alert = new NSAlert () {
            AlertStyle = NSAlertStyle.Informational,
            InformativeText = "At this point we should do something with the folder that the user just selected in the Open File Dialog box...",
            MessageText = "Folder Selected"
        };
        alert.RunModal ();
    }
}

Vamos a ejecutar la aplicación ahora y abrir el menú Archivo:

Menú Archivo Menú

Observe que el elemento de menú Abrir ahora está habilitado. Si la seleccionamos, se mostrará el cuadro de diálogo abierto:

Un cuadro de diálogo abierto Uncuadro de diálogo

Si hacemos clic en el botón Abrir, se mostrará el mensaje de alerta:

Un mensaje de diálogo de ejemplo Unmensaje de diálogo de

La línea clave aquí era [Export ("openDocument:")] , indica que NSMenu[Export ("openDocument:")] tiene un método void OpenDialog (NSObject sender) que responde a la openDocument: acción. Si recuerda lo anterior, el elemento de menú Abrir se conectará automáticamente a esta acción de forma predeterminada en Interface Builder:

Visualización de las acciones adjuntas

A continuación, echemos un vistazo a la creación de nuestro propio menú, elementos de menú y acciones y a responder a ellos en el código.

Trabajar con el menú abrir reciente

De forma predeterminada, el menú Archivo contiene un elemento Abrir reciente que realiza un seguimiento de los últimos archivos que el usuario ha abierto con la aplicación. Si va a crear una NSDocument aplicación basada en Xamarin.Mac, este menú se controlará automáticamente. Para cualquier otro tipo de aplicación de Xamarin.Mac, será responsable de administrar y responder manualmente a este elemento de menú.

Para controlar manualmente el menú Abrir reciente, primero debe informarle de que se ha abierto o guardado un archivo nuevo mediante lo siguiente:

// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);

Aunque la aplicación no usa , sigue usando para mantener el menú Abrir reciente mediante el envío de un elemento con la ubicación del archivo al NSDocumentsNSDocumentController método de NSDocumentsNSUrlNoteNewRecentDocumentURLSharedDocumentController .

A continuación, debe invalidar el método del delegado de aplicación para abrir cualquier archivo que el usuario seleccione OpenFile en el OpenFile Abrir reciente. Por ejemplo:

public override bool OpenFile (NSApplication sender, string filename)
{
    // Trap all errors
    try {
        filename = filename.Replace (" ", "%20");
        var url = new NSUrl ("file://"+filename);
        return OpenFile(url);
    } catch {
        return false;
    }
}

Devuelve si se puede abrir el archivo; de lo contrario, devuelve y se muestra una advertencia integrada al usuario de que no se pudo truefalse abrir el archivo.

Dado que el nombre de archivo y la ruta de acceso devueltos desde el menú Abrir reciente pueden incluir un espacio, es necesario escapar correctamente este carácter antes de crear o se producirá un error. Lo hacemos con el código siguiente:

filename = filename.Replace (" ", "%20");

Por último, creamos un que apunta al archivo y usamos un método auxiliar en el delegado de aplicación para abrir una nueva ventana y NSUrl cargar el archivo en él:

var url = new NSUrl ("file://"+filename);
return OpenFile(url);

Para reunir todo, echemos un vistazo a una implementación de ejemplo en un archivo AppDelegate.cs:

using AppKit;
using Foundation;
using System.IO;
using System;

namespace MacHyperlink
{
    [Register ("AppDelegate")]
    public class AppDelegate : NSApplicationDelegate
    {
        #region Computed Properties
        public int NewWindowNumber { get; set;} = -1;
        #endregion

        #region Constructors
        public AppDelegate ()
        {
        }
        #endregion

        #region Override Methods
        public override void DidFinishLaunching (NSNotification notification)
        {
            // Insert code here to initialize your application
        }

        public override void WillTerminate (NSNotification notification)
        {
            // Insert code here to tear down your application
        }

        public override bool OpenFile (NSApplication sender, string filename)
        {
            // Trap all errors
            try {
                filename = filename.Replace (" ", "%20");
                var url = new NSUrl ("file://"+filename);
                return OpenFile(url);
            } catch {
                return false;
            }
        }
        #endregion

        #region Private Methods
        private bool OpenFile(NSUrl url) {
            var good = false;

            // Trap all errors
            try {
                var path = url.Path;

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

                // 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.SetLanguageFromPath(path);
                viewController.View.Window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
                viewController.View.Window.RepresentedUrl = url;

                // Add document to the Open Recent menu
                NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);

                // Make as successful
                good = true;
            } catch {
                // Mark as bad file on error
                good = false;
            }

            // Return results
            return good;
        }
        #endregion

        #region actions
        [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) {
                    // Open the document in a new window
                    OpenFile (url);
                }
            }
        }
        #endregion
    }
}

En función de los requisitos de la aplicación, es posible que no quiera que el usuario abra el mismo archivo en más de una ventana al mismo tiempo. En nuestra aplicación de ejemplo, si el usuario elige un archivo que ya está abierto (ya sea en los elementos de menú Abrir reciente u Abrir..), la ventana que contiene el archivo se lleva al frente.

Para ello, hemos usado el código siguiente en nuestro método auxiliar:

var path = url.Path;

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

Hemos diseñado nuestra ViewController clase para contener la ruta de acceso al archivo en su propiedad Path . A continuación, recorreremos en bucle todas las ventanas abiertas actualmente en la aplicación. Si el archivo ya está abierto en una de las ventanas, se lleva al frente de todas las demás ventanas mediante:

NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);

Si no se encuentra ninguna coincidencia, se abre una nueva ventana con el archivo cargado y el archivo se indica en el menú Abrir reciente:

// 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.SetLanguageFromPath(path);
viewController.View.Window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
viewController.View.Window.RepresentedUrl = url;

// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);

Trabajar con acciones de ventana personalizadas

Al igual que las acciones integradas del primer respondedor que vienen conectadas previamente a elementos de menú estándar, puede crear nuevas acciones personalizadas y conectarlas a elementos de menú en Interface Builder.

En primer lugar, defina una acción personalizada en uno de los controladores de ventana de la aplicación. Por ejemplo:

[Action("defineKeyword:")]
public void defineKeyword (NSObject sender) {
    // Preform some action when the menu is selected
    Console.WriteLine ("Request to define keyword");
}

A continuación, haga doble clic en el archivo de guión gráfico de la Panel de solución para abrirlo para editarlo en la página de Xcode Interface Builder. Seleccione el primer respondedor en la escena de la aplicacióny, a continuación, cambie a Attributes Inspector:

El inspector de atributos delinspector de

Haga clic + en el botón situado en la parte inferior del Inspector + atributos para agregar una nueva acción personalizada:

Agregar una nueva acción Adición

Asíóntele el mismo nombre que la acción personalizada que creó en el controlador de ventana:

Edición del nombre de acción

Haga clic con el botón de control y arrástrelo desde un elemento de menú al Primer respondedor en la escena de la aplicación. En la lista emergente, seleccione la nueva acción que acaba de crear ( defineKeyword: en este ejemplo):

Adjuntar una acción Adjuntar

Guarde los cambios en el guión gráfico y vuelva a Visual Studio para Mac para sincronizar los cambios. Si ejecuta la aplicación, el elemento de menú al que conectó la acción personalizada se habilitará o deshabilitará automáticamente (en función de la ventana con la acción abierta) y al seleccionar el elemento de menú se activará la acción:

Prueba de la nueva acción Pruebade la nueva

Agregar, editar y eliminar menús

Como hemos visto en las secciones anteriores, una aplicación de Xamarin.Mac incluye un número preestablecido de menús predeterminados y elementos de menú a los que determinados controles de interfaz de usuario se activarán automáticamente y responderán. También hemos visto cómo agregar código a la aplicación que también habilitará y responderá a estos elementos predeterminados.

En esta sección se van a quitar los elementos de menú que no necesitamos, reorganizar los menús y agregar nuevos menús, elementos de menú y acciones.

Haga doble clic en el archivo Main.storyboard del Panel de solución para abrirlo para su edición:

Haga doble clic en el archivo de guión gráfico para editar la interfaz de usuario en Xcode.

Para nuestra aplicación específica de Xamarin.Mac, no vamos a usar el menú Vista predeterminado, por lo que vamos a quitarlo. En Jerarquía de interfaz, seleccione el elemento de menú Ver que forma parte de la barra de menús principal:

Seleccionar el elemento de menú Ver Seleccionarel elemento de menú

Presione Eliminar o retroceso para eliminar el menú. A continuación, no vamos a usar todos los elementos del menú Formato y queremos mover los elementos que vamos a usar en los submenús. En Jerarquía de interfaces, seleccione los siguientes elementos de menú:

Resaltar varios elementos Resaltando

Arrastre los elementos bajo el menú primario desde el submenú donde se encuentran actualmente:

Arrastrar elementos de menú al menú primario Arrastrando

El menú debería tener ahora el siguiente aspecto:

Elementos de la nueva ubicación Loselementos de la nueva

A continuación, vamos a arrastrar el submenú Texto desde debajo del menú Formato y colocarlo en la barra de menús principal entre los menús Formato y Ventana:

Menú Texto El

Volvamos al menú Formato y eliminaremos el elemento de submenú Fuente. A continuación, seleccione el menú Formato y cambie su nombre a "Fuente":

Menú Fuente Menú

A continuación, vamos a crear un menú personalizado de frases predefinidas que se anexarán automáticamente al texto de la vista de texto cuando se seleccionen. En el cuadro de búsqueda de la parte inferior del inspector de biblioteca, escriba "menú". Esto facilitará la búsqueda y el trabajo con todos los elementos de la interfaz de usuario del menú:

Inspector de biblioteca:

Ahora vamos a hacer lo siguiente para crear el menú:

  1. Arrastre un elemento de menú desde el Inspector de biblioteca a la barra de menús entre los menúsTexto y Ventana:

    Selección de un nuevo elemento de menú en la bibliotecaSelección de un nuevo elemento de menú en la

  2. Cambie el nombre del elemento "Phrases":

    Establecer el nombre del menúEstablecer el nombre del

  3. A continuación, arrastre un menú desde el Inspector de biblioteca:

    Selección de un menú de la bibliotecaSelección de un menú de la

  4. Coloque Menú en el nuevo elemento de menú que acaba de crear y cambie su nombre a "Frases":

    Edición del nombre del menú

  5. Ahora vamos a cambiar el nombre de los tres elementos de menú predeterminados "Address", "Date" y "Greeting":

    Menú Frases El

  6. Vamos a agregar un cuarto elemento de menú arrastrando un elemento de menú desde el Inspector de biblioteca y llamándolo "Firma":

    Edición del nombre del elemento de menú

  7. Guarde los cambios en la barra de menús.

Ahora vamos a crear un conjunto de acciones personalizadas para que nuestros nuevos elementos de menú se exponán al código de C#. En Xcode, vamos a cambiar a la vista Asistente:

Creación de las acciones necesarias Creaciónde las acciones

Vamos a hacer lo siguiente:

  1. Arrastre el control desde el elemento de menú Dirección al archivo AppDelegate.h.

  2. Cambie el tipo de conexión a Acción:

    Selección del tipo de acciónSelección del tipo de

  3. Escriba un nombre de "phraseAddress" y presione el Conectar para crear la nueva acción:

    Para configurar la acción, escriba un nombre.

  4. Repita los pasos anteriores para los elementos de menú Fecha,Saludoy Firma:

    Acciones completadas Acciones

  5. Guarde los cambios en la barra de menús.

A continuación, tenemos que crear una salida para nuestra vista de texto para que podamos ajustar su contenido a partir del código. Seleccione el archivo ViewController.h en el Editor del asistente y cree una nueva salida denominada :

Creación de una salida Creaciónde una

Vuelva a Visual Studio para Mac para sincronizar los cambios de Xcode. A continuación, edite el archivo ViewController.cs y haga que sea parecido al siguiente:

using System;

using AppKit;
using Foundation;

namespace MacMenus
{
    public partial class ViewController : NSViewController
    {
        #region Application Access
        public static AppDelegate App {
            get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
        }
        #endregion

        #region Computed Properties
        public override NSObject RepresentedObject {
            get {
                return base.RepresentedObject;
            }
            set {
                base.RepresentedObject = value;
                // Update the view, if already loaded.
            }
        }

        public string Text {
            get { return documentText.Value; }
            set { documentText.Value = value; }
        } 
        #endregion

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

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

            // Do any additional setup after loading the view.
        }

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

            App.textEditor = this;
        }

        public override void ViewWillDisappear ()
        {
            base.ViewDidDisappear ();

            App.textEditor = null;
        }
        #endregion
    }
}

Esto expone el texto de nuestra vista de texto fuera de la clase e informa al delegado de aplicación cuando la ventana obtiene ViewController o pierde el foco. Ahora edite el archivo AppDelegate.cs y haga que sea parecido al siguiente:

using AppKit;
using Foundation;
using System;

namespace MacMenus
{
    [Register ("AppDelegate")]
    public partial class AppDelegate : NSApplicationDelegate
    {
        #region Computed Properties
        public ViewController textEditor { get; set;} = null;
        #endregion

        #region Constructors
        public AppDelegate ()
        {
        }
        #endregion

        #region Override Methods
        public override void DidFinishLaunching (NSNotification notification)
        {
            // Insert code here to initialize your application
        }

        public override void WillTerminate (NSNotification notification)
        {
            // Insert code here to tear down your application
        }
        #endregion

        #region Custom actions
        [Export ("openDocument:")]
        void OpenDialog (NSObject sender)
        {
            var dlg = NSOpenPanel.OpenPanel;
            dlg.CanChooseFiles = false;
            dlg.CanChooseDirectories = true;

            if (dlg.RunModal () == 1) {
                var alert = new NSAlert () {
                    AlertStyle = NSAlertStyle.Informational,
                    InformativeText = "At this point we should do something with the folder that the user just selected in the Open File Dialog box...",
                    MessageText = "Folder Selected"
                };
                alert.RunModal ();
            }
        }

        partial void phrasesAddress (Foundation.NSObject sender) {

            textEditor.Text += "Xamarin HQ\n394 Pacific Ave, 4th Floor\nSan Francisco CA 94111\n\n";
        }

        partial void phrasesDate (Foundation.NSObject sender) {

            textEditor.Text += DateTime.Now.ToString("D");
        }

        partial void phrasesGreeting (Foundation.NSObject sender) {

            textEditor.Text += "Dear Sirs,\n\n";
        }

        partial void phrasesSignature (Foundation.NSObject sender) {

            textEditor.Text += "Sincerely,\n\nKevin Mullins\nXamarin,Inc.\n";
        }
        #endregion
    }
}

Aquí hemos realizado una clase parcial para que podamos usar las acciones y salidas que AppDelegate definimos en Interface Builder. También exponemos un para textEditor realizar un seguimiento de qué ventana está actualmente en el foco.

Los métodos siguientes se usan para controlar nuestros elementos de menú y menú personalizados:

partial void phrasesAddress (Foundation.NSObject sender) {

    if (textEditor == null) return;
    textEditor.Text += "Xamarin HQ\n394 Pacific Ave, 4th Floor\nSan Francisco CA 94111\n\n";
}

partial void phrasesDate (Foundation.NSObject sender) {

    if (textEditor == null) return;
    textEditor.Text += DateTime.Now.ToString("D");
}

partial void phrasesGreeting (Foundation.NSObject sender) {

    if (textEditor == null) return;
    textEditor.Text += "Dear Sirs,\n\n";
}

partial void phrasesSignature (Foundation.NSObject sender) {

    if (textEditor == null) return;
    textEditor.Text += "Sincerely,\n\nKevin Mullins\nXamarin,Inc.\n";
}

Ahora, si ejecutamos la aplicación, todos los elementos del menú Frase estarán activos y agregarán la frase give a la vista de texto cuando se seleccione:

Un ejemplo de la aplicación que ejecutaUn ejemplo de la aplicación en

Ahora que tenemos los conceptos básicos de trabajar con la barra de menús de la aplicación, echemos un vistazo a la creación de un menú contextual personalizado.

Creación de menús a partir del código

Además de crear menús y elementos de menú con el Interface Builder de Xcode, puede haber ocasiones en las que una aplicación de Xamarin.Mac necesite crear, modificar o quitar un menú, un submenú o un elemento de menú del código.

En el ejemplo siguiente, se crea una clase para contener la información sobre los elementos de menú y los submenúes que se crearán dinámicamente sobre la marcha:

using System;
using System.Collections.Generic;
using Foundation;
using AppKit;

namespace AppKit.TextKit.Formatter
{
    public class LanguageFormatCommand : NSObject
    {
        #region Computed Properties
        public string Title { get; set; } = "";
        public string Prefix { get; set; } = "";
        public string Postfix { get; set; } = "";
        public List<LanguageFormatCommand> SubCommands { get; set; } = new List<LanguageFormatCommand>();
        #endregion

        #region Constructors
        public LanguageFormatCommand () {

        }

        public LanguageFormatCommand (string title)
        {
            // Initialize
            this.Title = title;
        }

        public LanguageFormatCommand (string title, string prefix)
        {
            // Initialize
            this.Title = title;
            this.Prefix = prefix;
        }

        public LanguageFormatCommand (string title, string prefix, string postfix)
        {
            // Initialize
            this.Title = title;
            this.Prefix = prefix;
            this.Postfix = postfix;
        }
        #endregion
    }
}

Agregar menús y elementos

Con esta clase definida, la siguiente rutina analizará una colección de objetos y creará de forma recursiva nuevos menús y elementos de menú anexándolos a la parte inferior del menú existente (creado en Interface Builder) que se ha LanguageFormatCommand pasado:

private void AssembleMenu(NSMenu menu, List<LanguageFormatCommand> commands) {
    NSMenuItem menuItem;

    // Add any formatting commands to the Formatting menu
    foreach (LanguageFormatCommand command in commands) {
        // Add separator or item?
        if (command.Title == "") {
            menuItem = NSMenuItem.SeparatorItem;
        } else {
            menuItem = new NSMenuItem (command.Title);

            // Submenu?
            if (command.SubCommands.Count > 0) {
                // Yes, populate submenu
                menuItem.Submenu = new NSMenu (command.Title);
                AssembleMenu (menuItem.Submenu, command.SubCommands);
            } else {
                // No, add normal menu item
                menuItem.Activated += (sender, e) => {
                    // Apply the command on the selected text
                    TextEditor.PerformFormattingCommand (command);
                };
            }
        }
        menu.AddItem (menuItem);
    }
}

Para cualquier objeto que tenga una propiedad en blanco, esta rutina crea un elemento de menú Separador (una línea gris LanguageFormatCommandTitle fina) entre secciones de menú: LanguageFormatCommand

menuItem = NSMenuItem.SeparatorItem;

Si se proporciona un título, se crea un nuevo elemento de menú con ese título:

menuItem = new NSMenuItem (command.Title);

Si el objeto contiene objetos secundarios, se crea un submenú y se llama al método de forma LanguageFormatCommandLanguageFormatCommandAssembleMenu recursiva para compilar ese menú:

menuItem.Submenu = new NSMenu (command.Title);
AssembleMenu (menuItem.Submenu, command.SubCommands);

Para cualquier nuevo elemento de menú que no tenga submenúes, se agrega código para controlar el elemento de menú seleccionado por el usuario:

menuItem.Activated += (sender, e) => {
    // Do something when the menu item is selected
    ...
};

Prueba de la creación del menú

Con todo el código anterior en su lugar, si se creó la siguiente colección LanguageFormatCommand de objetos:

// Define formatting commands
FormattingCommands.Add(new LanguageFormatCommand("Strong","**","**"));
FormattingCommands.Add(new LanguageFormatCommand("Emphasize","_","_"));
FormattingCommands.Add(new LanguageFormatCommand("Inline Code","`","`"));
FormattingCommands.Add(new LanguageFormatCommand("Code Block","```\n","\n```"));
FormattingCommands.Add(new LanguageFormatCommand("Comment","<!--","-->"));
FormattingCommands.Add (new LanguageFormatCommand ());
FormattingCommands.Add(new LanguageFormatCommand("Unordered List","* "));
FormattingCommands.Add(new LanguageFormatCommand("Ordered List","1. "));
FormattingCommands.Add(new LanguageFormatCommand("Block Quote","> "));
FormattingCommands.Add (new LanguageFormatCommand ());

var Headings = new LanguageFormatCommand ("Headings");
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 1","# "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 2","## "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 3","### "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 4","#### "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 5","##### "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 6","###### "));
FormattingCommands.Add (Headings);

FormattingCommands.Add(new LanguageFormatCommand ());
FormattingCommands.Add(new LanguageFormatCommand("Link","[","]()"));
FormattingCommands.Add(new LanguageFormatCommand("Image","![](",")"));
FormattingCommands.Add(new LanguageFormatCommand("Image Link","[![](",")](LinkImageHere)"));

Y esa colección pasada a la función (con el menú Formato establecido como base), se crearían los siguientes AssembleMenu menús dinámicos y elementos AssembleMenu de menú:

Los nuevos elementos de menú de la aplicación en ejecuciónLos nuevos elementos de menú de la aplicación en

Quitar menús y elementos

Si necesita quitar cualquier menú o elemento de menú de la interfaz de usuario de la aplicación, puede usar el método de la clase simplemente para darle el índice de base cero del elemento que se va a RemoveItemAtNSMenu quitar.

Por ejemplo, para quitar los menús y los elementos de menú creados por la rutina anterior, puede usar el código siguiente:

public void UnpopulateFormattingMenu(NSMenu menu) {

    // Remove any additional items
    for (int n = (int)menu.Count - 1; n > 4; --n) {
        menu.RemoveItemAt (n);
    }
}

En el caso del código anterior, los cuatro primeros elementos de menú se crean en los elementos Interface Builder y lejos de Xcode disponibles en la aplicación, por lo que no se quitan dinámicamente.

Menús contextuales

Los menús contextuales aparecen cuando el usuario hace clic con el botón derecho o hace clic con el botón derecho en un elemento de una ventana. De forma predeterminada, varios de los elementos de la interfaz de usuario integrados en macOS ya tienen menús contextuales asociados (como la vista de texto). Sin embargo, puede haber ocasiones en las que queremos crear nuestros propios menús contextuales personalizados para un elemento de la interfaz de usuario que hemos agregado a una ventana.

Vamos a editar el archivo Main.storyboard en Xcode y agregar una ventana de ventana a nuestro diseño, establecer su clase en "NSPanel" en el Inspectorde identidad, agregar un nuevo elemento del Asistente al menú Ventana y adjuntarlo a la nueva ventana mediante un objeto Show Segue:

Establecer el tipo de segue en el archivo de guión gráfico de puntos principal.

Vamos a hacer lo siguiente:

  1. Arrastre una etiqueta desde el Inspector de biblioteca a la ventana Panel y establezca su texto en "Propiedad":

    Edición del valor de la etiqueta

  2. A continuación, arrastre un menú desde el Inspector de biblioteca al Controlador de vistas en la jerarquía de vistas y cambie el nombre de los tres elementos de menú predeterminados Document, Text y Font:

    Elementos de menú necesarios Loselementos de menú

  3. Ahora, controle y arrastre desde la etiqueta de propiedad al menú:

    Arrastrar para crear un segue Arrastrando

  4. En el cuadro de diálogo emergente, seleccione Menú:

    Para establecer el tipo de segue, seleccione menú en Salidas en el menú contextual Etiqueta.

  5. Desde el Inspector de identidad,establezca la clase del controlador de vista en "PanelViewController":

    Establecimiento de la clase segue Establecimiento

  6. Vuelva a la Visual Studio para Mac para sincronizar y, a continuación, vuelva a Interface Builder.

  7. Cambie al Editor del asistente y seleccione el archivo PanelViewController.h.

  8. Cree una acción para el elemento de menú Documento denominada :

    Configuración de la acción denominada propertyDocument.

  9. Repita la creación de acciones para los elementos de menú restantes:

    Acciones repetitivas para los elementos de menú restantes.

  10. Por último, cree una salida para la etiqueta de propiedad denominada :

    Configuración de la salida Configuraciónde la

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

Edite el archivo PanelViewController.cs y agregue el código siguiente:

partial void propertyDocument (Foundation.NSObject sender) {
    propertyLabel.StringValue = "Document";
}

partial void propertyFont (Foundation.NSObject sender) {
    propertyLabel.StringValue = "Font";
}

partial void propertyText (Foundation.NSObject sender) {
    propertyLabel.StringValue = "Text";
}

Ahora, si ejecutamos la aplicación y hacemos clic con el botón derecho en la etiqueta de propiedad en el panel, veremos nuestro menú contextual personalizado. Si seleccionamos y item en el menú, el valor de la etiqueta cambiará:

Menú contextual que ejecuta Elmenú contextual en

A continuación, echemos un vistazo a la creación de menús de la barra de estado.

Menús de la barra de estado

Los menús de la barra de estado muestran una colección de elementos de menú de estado que proporcionan interacción con el usuario o comentarios, como un menú o una imagen que refleja el estado de una aplicación. El menú de la barra de estado de una aplicación está habilitado y activo incluso si la aplicación se ejecuta en segundo plano. La barra de estado de todo el sistema reside en el lado derecho de la barra de menús de la aplicación y es la única barra de estado disponible actualmente en macOS.

Vamos a editar el archivo AppDelegate.cs y hacer que el método sea parecido al siguiente:

public override void DidFinishLaunching (NSNotification notification)
{
    // Create a status bar menu
    NSStatusBar statusBar = NSStatusBar.SystemStatusBar;

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

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

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

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

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

NSStatusBar statusBar = NSStatusBar.SystemStatusBar; nos proporciona acceso a la barra de estado de todo el sistema. var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable); crea un nuevo elemento de barra de estado. A partir de ahí, creamos un menú y varios elementos de menú y adjuntamos el menú al elemento de la barra de estado que acaba de crear.

Si ejecutamos la aplicación, se mostrará el nuevo elemento de la barra de estado. Al seleccionar un elemento en el menú, se cambiará el texto en la vista de texto:

Menú de la barra de estado que ejecuta Elmenú de la barra de estado en

A continuación, echemos un vistazo a la creación de elementos de menú de acoplamiento personalizados.

Menús de acoplamiento personalizados

El menú acoplar aparece para la aplicación Mac cuando el usuario hace clic con el botón derecho o hace clic con el botón derecho en el icono de la aplicación en el dock:

Un menú de acoplamiento personalizado Unmenú de acoplamiento

Vamos a crear un menú de acoplamiento personalizado para nuestra aplicación haciendo lo siguiente:

  1. En Visual Studio para Mac, haga clic con el botón derecho en el proyecto de la aplicación y seleccione Agregarnuevo archivo... En el cuadro de diálogo nuevo archivo, seleccione Definición de interfaz vacía de Xamarin.Mac,use "DockMenu" como Nombre y haga clic en el botón Nuevo para crear el nuevo archivo DockMenu.xib:

    Agregar una definición de interfaz vacía Adición

  2. En el Panel de solución, haga doble clic en el archivo DockMenu.xib para abrirlo para editarlo en Xcode. Cree un nuevo menú con los siguientes elementos: Dirección,Fecha,Saludoy Firma

    Laying out the UI de la interfaz de usuario)

  3. A continuación, vamos a conectar nuestros nuevos elementos de menú a nuestras acciones existentes que creamos para nuestro menú personalizado en la sección Agregar, editar y eliminar menús anterior. Cambie al Inspector de conexión y seleccione el primer respondedor en la jerarquía de interfaz. Desplácese hacia abajo y busque la phraseAddress: acción. Arrastre una línea desde el círculo de esa acción hasta el elemento de menú Dirección:

    Arrastre una línea hasta el elemento de menú Dirección.

  4. Repita el procedimiento para todos los demás elementos de menú que los adjuntan a sus acciones correspondientes:

    Repetir para otros elementos de menú que los adjuntan a sus acciones correspondientes.

  5. A continuación, seleccione la aplicación en la jerarquía de interfaz. En el Inspector de conexión,arrastre una línea desde el círculo de la salida hasta el menú que acaba de crear:

    Arrastrar el cable hacia arriba en la salida

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

  7. Haga doble clic en el archivo Info.plist para abrirlo para su edición:

    Edición del archivo Info.plist Edición

  8. Haga clic en la pestaña Origen en la parte inferior de la pantalla:

    Selección de la vista Origen Selecciónde la vista

  9. Haga clic en Agregar nueva entrada, haga clic en el botón más verde, establezca el nombre de propiedad en "AppleDockMenu" y el valor en "DockMenu" (el nombre del nuevo archivo .xib sin la extensión):

    Agregar el elemento DockMenu Agregar

Ahora, si ejecutamos la aplicación y hacemos clic con el botón derecho en su icono en dock, se mostrarán los nuevos elementos de menú:

Un ejemplo del menú de acoplamiento en el que se ejecutaUn ejemplo del menú de acoplamiento en

Si seleccionamos uno de los elementos personalizados en el menú, se modificará el texto de la vista de texto.

Botón emergente y listas desplegables

Un botón emergente muestra un elemento seleccionado y presenta una lista de opciones para seleccionar cuando el usuario hace clic en él. Una lista desplegable es un tipo de botón emergente que normalmente se usa para seleccionar comandos específicos del contexto de la tarea actual. Ambos pueden aparecer en cualquier lugar de una ventana.

Vamos a crear un botón emergente personalizado para nuestra aplicación haciendo lo siguiente:

  1. Edite el archivo Main.storyboard en Xcode y arrastre un botón emergente desde el Inspector de biblioteca a la ventana Panel que creamos en la sección Menús contextuales:

    Agregar un botón emergente Agregarun botón

  2. Agregue un nuevo elemento de menú y establezca los títulos de los elementos del elemento emergente en: Dirección, Fecha,Saludoy Firma

    Configuración de los elementos de menúConfiguración de los elementos de

  3. A continuación, vamos a conectar nuestros nuevos elementos de menú a las acciones existentes que creamos para nuestro menú personalizado en la sección Agregar, editar y eliminar menús anterior. Cambie al Inspector de conexión y seleccione el primer respondedor en la jerarquía de interfaz. Desplácese hacia abajo y busque la phraseAddress: acción. Arrastre una línea desde el círculo de esa acción hasta el elemento de menú Dirección:

    Arrastrar para conectar una acción Arrastrando

  4. Repita con todos los demás elementos de menú que los adjuntan a sus acciones correspondientes:

    Todas las acciones necesarias Todaslas acciones

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

Ahora, si ejecutamos la aplicación y seleccionamos un elemento del elemento emergente, el texto de la vista de texto cambiará:

Un ejemplo del elemento emergente que ejecutaUn ejemplo del elemento emergente que se

Puede crear y trabajar con listas desplegables exactamente igual que los botones emergentes. En lugar de adjuntar a una acción existente, podría crear sus propias acciones personalizadas como hicimos para nuestro menú contextual en la sección Menús contextuales.

Resumen

En este artículo se ha detallado cómo trabajar con menús y elementos de menú en una aplicación de Xamarin.Mac. En primer lugar, examinamos la barra de menús de la aplicación, después analizamos la creación de menús contextuales y, a continuación, examinamos los menús de la barra de estado y los menús de acoplamiento personalizados. Por último, hemos abordado los menús emergentes y las listas desplegables.