Condividi tramite


Menu in Xamarin.Mac

Questo articolo illustra l'uso dei menu in un'applicazione Xamarin.Mac. Descrive come creare e gestire menu e voci di menu in Xcode e Interface Builder e usarli a livello di codice.

Quando si lavora con C# e .NET in un'applicazione Xamarin.Mac, è possibile accedere agli stessi menu Cocoa in Objective-C cui uno sviluppatore lavora e Xcode. Poiché Xamarin.Mac si integra direttamente con Xcode, è possibile usare Interface Builder di Xcode per creare e gestire le barre dei menu, i menu e le voci di menu (o, facoltativamente, crearli direttamente nel codice C#).

I menu sono parte integrante dell'esperienza utente di un'applicazione Mac e vengono comunemente visualizzati in varie parti dell'interfaccia utente:

  • Barra dei menu dell'applicazione: questo è il menu principale visualizzato nella parte superiore della schermata per ogni applicazione Mac.
  • Menu contestuali : questi vengono visualizzati quando l'utente fa clic con il pulsante destro del mouse o fa clic su un elemento in una finestra.
  • La barra di stato: questa è l'area all'estrema destra della barra dei menu dell'applicazione visualizzata nella parte superiore dello schermo (a sinistra dell'orologio della barra dei menu) e cresce a sinistra man mano che vengono aggiunti elementi.
  • Menu Ancoraggio: menu per ogni applicazione nel dock visualizzato quando l'utente fa clic con il pulsante destro del mouse o fa clic sull'icona dell'applicazione oppure quando l'utente fa clic con il pulsante sinistro del mouse sull'icona e tiene premuto il pulsante del mouse.
  • Pulsante popup ed elenchi a discesa: un pulsante a comparsa visualizza un elemento selezionato e presenta un elenco di opzioni da selezionare quando si fa clic dall'utente. Un elenco a discesa è un tipo di pulsante popup usato in genere per la selezione di comandi specifici per il contesto dell'attività corrente. Entrambi possono essere visualizzati ovunque in una finestra.

Menu di esempio

In questo articolo verranno illustrate le nozioni di base sull'uso delle barre dei menu, dei menu e delle voci di menu Cocoa in un'applicazione Xamarin.Mac. È consigliabile usare prima di tutto l'articolo Hello, Mac , in particolare le sezioni Introduzione a Xcode e Interface Builder e Outlet e Actions , in quanto illustra i concetti e le tecniche chiave che verranno usati in questo articolo.

È possibile esaminare anche la sezione Esposizione di classi/metodi C# al Objective-C documento Internals di Xamarin.Mac, che illustra gli Register attributi e Export usati per collegare le classi C# agli oggetti e agli Objective-C elementi dell'interfaccia utente.

Barra dei menu dell'applicazione

A differenza delle applicazioni in esecuzione nel sistema operativo Windows in cui ogni finestra può avere una propria barra dei menu collegata, ogni applicazione in esecuzione in macOS ha una singola barra dei menu che viene eseguita lungo la parte superiore dello schermo usata per ogni finestra dell'applicazione:

Barra dei menu

Le voci della barra dei menu vengono attivate o disattivate in base al contesto o allo stato corrente dell'applicazione e alla relativa interfaccia utente in un determinato momento. Ad esempio: se l'utente seleziona un campo di testo, gli elementi del menu Modifica verranno abilitati, ad esempio Copia e Taglia.

In base ad Apple e per impostazione predefinita, tutte le applicazioni macOS hanno un set standard di menu e voci di menu visualizzate nella barra dei menu dell'applicazione:

  • Menu Apple: questo menu consente di accedere alle voci a livello di sistema disponibili per l'utente in qualsiasi momento, indipendentemente dall'applicazione in esecuzione. Questi elementi non possono essere modificati dallo sviluppatore.
  • Menu Dell'app: questo menu visualizza il nome dell'applicazione in grassetto e consente all'utente di identificare l'applicazione attualmente in esecuzione. Contiene elementi che si applicano all'applicazione nel suo complesso e non a un determinato documento o processo, ad esempio la chiusura dell'applicazione.
  • Menu File: elementi usati per creare, aprire o salvare documenti usati dall'applicazione. Se l'applicazione non è basata su documenti, questo menu può essere rinominato o rimosso.
  • Menu Modifica: contiene comandi come Taglia, Copia e Incolla che vengono usati per modificare o modificare elementi nell'interfaccia utente dell'applicazione.
  • Menu Formato: se l'applicazione funziona con il testo, questo menu contiene i comandi per modificare la formattazione del testo.
  • Menu Visualizza: contiene i comandi che influiscono sulla modalità di visualizzazione del contenuto (visualizzato) nell'interfaccia utente dell'applicazione.
  • Menu specifici dell'applicazione: si tratta di menu specifici dell'applicazione, ad esempio un menu segnalibri per un Web browser. Dovrebbero essere visualizzati tra i menu Visualizza e Finestra sulla barra.
  • Menu Finestra: contiene i comandi per l'uso delle finestre nell'applicazione, nonché un elenco delle finestre aperte correnti.
  • Menu ?: se l'applicazione fornisce la Guida su schermo, il menu ? dovrebbe essere il menu più a destra sulla barra.

Per altre informazioni sulla barra dei menu dell'applicazione e sui menu standard e sulle voci di menu, vedere Linee guida per l'interfaccia umana di Apple.

Barra dei menu dell'applicazione predefinita

Ogni volta che si crea un nuovo progetto Xamarin.Mac, si ottiene automaticamente una barra dei menu dell'applicazione standard e predefinita con gli elementi tipici di un'applicazione macOS (come illustrato nella sezione precedente). La barra dei menu predefinita dell'applicazione è definita nel file Main.storyboard (insieme al resto dell'interfaccia utente dell'app) nel progetto nel riquadro della soluzione:

Selezionare lo storyboard principale

Fare doppio clic sul file Main.storyboard per aprirlo per la modifica in Interface Builder di Xcode e verrà visualizzata l'interfaccia dell'editor di menu:

Modifica dell'interfaccia utente in Xcode, che mostra lo storyboard principale.

Da qui è possibile fare clic su elementi come la voce di menu Apri nel menu File e modificare o regolarne le proprietà in Controllo attributi:

Modifica degli attributi di un menu

Verranno aggiunti, modificati ed eliminando menu ed elementi più avanti in questo articolo. Per il momento vogliamo solo vedere quali menu e voci di menu sono disponibili per impostazione predefinita e come sono stati esposti automaticamente al codice tramite un set di outlet e azioni predefiniti (per altre informazioni vedere la documentazione outlet e azioni ).

Ad esempio, se si fa clic sul controllo Connessione ion per la voce di menu Apri, è possibile visualizzarla automaticamente cablata all'azioneopenDocument::

Visualizzazione dell'azione associata

Se si seleziona il primo risponditore nella gerarchia dell'interfaccia e si scorre verso il basso nel controllo Connessione ion e verrà visualizzata la definizione dell'azione a cui è associata la openDocument:voce di menu Apri (insieme a diverse altre azioni predefinite per l'applicazione che sono e non vengono collegate automaticamente ai controlli):

Visualizzazione di tutte le azioni associate

Perché è così importante? Nella sezione successiva si vedrà come funzionano queste azioni definite automaticamente con altri elementi dell'interfaccia utente Cocoa per abilitare e disabilitare automaticamente le voci di menu, nonché fornire funzionalità predefinite per gli elementi.

Successivamente si useranno queste azioni predefinite per abilitare e disabilitare gli elementi dal codice e fornire le proprie funzionalità quando vengono selezionate.

Funzionalità del menu predefinita

Se si esegue un'applicazione Xamarin.Mac appena creata prima di aggiungere elementi o codice dell'interfaccia utente, si noterà che alcuni elementi vengono collegati automaticamente e abilitati (con funzionalità completamente predefinite), ad esempio la voce Quit nel menu App :

Voce di menu abilitata

Anche se altre voci di menu, ad esempio Taglia, Copia e Incolla , non sono:

Voci di menu disabilitate

Arrestare l'applicazione e fare doppio clic sul file Main.storyboard nel riquadro della soluzione per aprirla per la modifica in Interface Builder di Xcode. Trascinare quindi una visualizzazione testo dalla libreria nel controller di visualizzazione della finestra nell'editor dell'interfaccia:

Selezione di una visualizzazione testo dalla raccolta

Nell'Editor vincoli aggiungere la visualizzazione testo ai bordi della finestra e impostarla dove cresce e si riduce con la finestra facendo clic su tutti e quattro i raggi I rossi nella parte superiore dell'editor e facendo clic sul pulsante Aggiungi 4 vincoli:

Modifica dei controint

Salvare le modifiche apportate alla progettazione dell'interfaccia utente e tornare al Visual Studio per Mac per sincronizzare le modifiche con il progetto Xamarin.Mac. Avviare ora l'applicazione, digitare un testo nella visualizzazione testo, selezionarlo e aprire il menu Modifica :

Le voci di menu vengono abilitate/disabilitate automaticamente

Si noti che gli elementi Taglia, Copia e Incolla vengono abilitati automaticamente e completamente funzionanti, senza scrivere una singola riga di codice.

Cosa avviene qui? Tenere presente le azioni predefinite predefinite che vengono collegate alle voci di menu predefinite (come illustrato in precedenza), la maggior parte degli elementi dell'interfaccia utente Cocoa che fanno parte di macOS ha creato hook per azioni specifiche (ad esempio copy:). Quindi, quando vengono aggiunti a una finestra, attiva e selezionata, la voce di menu o gli elementi corrispondenti associati a tale azione vengono abilitati automaticamente. Se l'utente seleziona tale voce di menu, la funzionalità incorporata nell'elemento dell'interfaccia utente viene chiamata ed eseguita, tutto senza l'intervento dello sviluppatore.

Abilitazione e disabilitazione di menu ed elementi

Per impostazione predefinita, ogni volta che si verifica un evento utente, NSMenu abilita e disabilita automaticamente ogni voce di menu e menu visibile in base al contesto dell'applicazione. Esistono tre modi per abilitare/disabilitare un elemento:

  • Abilitazione automatica del menu: una voce di menu è abilitata se NSMenu è possibile trovare un oggetto appropriato che risponde all'azione a cui l'elemento è collegato. Ad esempio, la visualizzazione di testo precedente con un hook predefinito per l'azione copy: .
  • Azioni personalizzate e validateMenuItem: - Per qualsiasi voce di menu associata a un'azione personalizzata del controller di visualizzazione o finestra, è possibile aggiungere l'azione validateMenuItem: e abilitare o disabilitare manualmente le voci di menu.
  • Abilitazione manuale del menu: impostare manualmente la Enabled proprietà di ogni NSMenuItem elemento per abilitare o disabilitare ogni voce in un menu singolarmente.

Per scegliere un sistema, impostare la AutoEnablesItems proprietà di un oggetto NSMenu. true è automatico (comportamento predefinito) ed false è manuale.

Importante

Se si sceglie di usare l'abilitazione manuale del menu, nessuna delle voci di menu, anche quelle controllate dalle classi AppKit come NSTextView, vengono aggiornate automaticamente. L'utente sarà responsabile dell'abilitazione e della disabilitazione di tutti gli elementi a mano nel codice.

Uso di validateMenuItem

Come indicato in precedenza, per qualsiasi voce di menu associata a un'azione personalizzata window o view controller, è possibile aggiungere l'azione validateMenuItem: e abilitare o disabilitare manualmente le voci di menu.

Nell'esempio seguente, la Tag proprietà verrà usata per decidere il tipo di voce di menu che verrà abilitata/disabilitata dall'azione validateMenuItem: in base allo stato del testo selezionato in un oggetto NSTextView. La Tag proprietà è stata impostata in Interface Builder per ogni voce di menu:

Impostazione della proprietà Tag

E il codice seguente aggiunto al controller di visualizzazione:

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

Quando questo codice viene eseguito e non viene selezionato alcun testo in NSTextView, le due voci di menu a capo sono disabilitate (anche se sono cablate alle azioni nel controller di visualizzazione):

Visualizzazione di elementi disabilitati

Se viene selezionata una sezione di testo e il menu viene riaperto, saranno disponibili le due voci di menu a capo:

Visualizzazione degli elementi abilitati

Abilitazione e risposta alle voci di menu nel codice

Come abbiamo visto in precedenza, solo aggiungendo elementi specifici dell'interfaccia utente Cocoa alla progettazione dell'interfaccia utente (ad esempio un campo di testo), diverse delle voci di menu predefinite verranno abilitate e funzioneranno automaticamente, senza dover scrivere codice. Si esaminerà ora l'aggiunta di codice C# al progetto Xamarin.Mac per abilitare una voce di menu e fornire funzionalità quando l'utente lo seleziona.

Si supponga, ad esempio, che l'utente possa usare l'elemento Apri nel menu File per selezionare una cartella. Poiché si vuole che si tratta di una funzione a livello di applicazione e non limitata a un elemento give window o ui, si aggiungerà il codice per gestirlo al delegato dell'applicazione.

Nel riquadro della soluzione fare doppio clic sul AppDelegate.CS file per aprirlo per la modifica:

Selezione del delegato dell'app

Aggiungere il codice seguente sotto il DidFinishLaunching metodo :

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

Eseguire ora l'applicazione e aprire il menu File :

Menu File

Si noti che la voce di menu Apri è ora abilitata. Se la si seleziona, verrà visualizzata la finestra di dialogo aperta:

Finestra di dialogo aperta

Se si fa clic sul pulsante Apri , verrà visualizzato il messaggio di avviso:

Messaggio di dialogo di esempio

La riga chiave qui è [Export ("openDocument:")], indica NSMenu che AppDelegate ha un metodo void OpenDialog (NSObject sender) che risponde all'azione openDocument: . Se si ricorderà dall'alto, la voce di menu Apri viene collegata automaticamente a questa azione per impostazione predefinita in Interface Builder:

Visualizzazione delle azioni associate

Si esaminerà ora la creazione di menu, voci di menu e azioni e risposte nel codice.

Utilizzo del menu recente aperto

Per impostazione predefinita, il menu File contiene un elemento Apri recente che tiene traccia degli ultimi file aperti dall'utente con l'app. Se si crea un'app NSDocument Xamarin.Mac basata, questo menu verrà gestito automaticamente. Per qualsiasi altro tipo di app Xamarin.Mac, si sarà responsabili della gestione e della risposta manuale a questa voce di menu.

Per gestire manualmente il menu Apri recenti , è prima necessario informarlo che un nuovo file è stato aperto o salvato usando quanto segue:

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

Anche se l'app non usa NSDocuments, si usa comunque per NSDocumentController mantenere il menu Apri recenti inviando un NSUrl oggetto con il percorso del file al NoteNewRecentDocumentURL metodo di SharedDocumentController.

Successivamente, devi eseguire l'override del OpenFile metodo del delegato dell'app per aprire qualsiasi file selezionato dall'utente dal menu Apri recenti . Ad esempio:

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

Restituisce true se il file può essere aperto. In caso contrario false , verrà restituito un avviso predefinito che verrà visualizzato all'utente che non è stato possibile aprire il file.

Poiché il nome file e il percorso restituiti dal menu Apri recenti potrebbero includere uno spazio, è necessario eseguire correttamente l'escape di questo carattere prima di creare un NSUrl oggetto o verrà visualizzato un errore. Questa operazione viene eseguita con il codice seguente:

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

Infine, creiamo un oggetto NSUrl che punta al file e usiamo un metodo helper nel delegato dell'app per aprire una nuova finestra e caricarvi il file:

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

Per raggruppare tutti gli elementi, si esaminerà un'implementazione di esempio in un file 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
    }
}

In base ai requisiti della tua app, potresti non volere che l'utente apra lo stesso file in più finestre contemporaneamente. Nell'app di esempio, se l'utente sceglie un file già aperto (dalle voci di menu Apri recenti o Apri). La finestra che contiene il file viene portata in primo piano.

A tale scopo, è stato usato il codice seguente nel metodo helper:

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

La classe è stata progettata ViewController per contenere il percorso del file nella relativa Path proprietà. Successivamente, si scorre in ciclo tutte le finestre attualmente aperte nell'app. Se il file è già aperto in una delle finestre, viene portato davanti a tutte le altre finestre usando:

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

Se non viene trovata alcuna corrispondenza, viene aperta una nuova finestra con il file caricato e il file viene indicato nel menu Apri recenti :

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

Utilizzo delle azioni della finestra personalizzate

Proprio come le azioni predefinite first responder che vengono pre-cablate alle voci di menu standard, è possibile creare nuove azioni personalizzate e collegarle alle voci di menu in Interface Builder.

Prima di tutto, definire un'azione personalizzata in uno dei controller finestra dell'app. Ad esempio:

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

Fare quindi doppio clic sul file storyboard dell'app nel riquadro della soluzione per aprirlo per la modifica in Interface Builder di Xcode. Selezionare il primo risponditore nella scena dell'applicazione, quindi passare a Controllo attributi:

Controllo attributi

Fare clic sul + pulsante nella parte inferiore di Controllo attributi per aggiungere una nuova azione personalizzata:

Aggiunta di una nuova azione

Assegnargli lo stesso nome dell'azione personalizzata creata nel controller finestra:

Modifica del nome dell'azione

Fare clic sul controllo e trascinare da una voce di menu al primo risponditore nella scena dell'applicazione. Nell'elenco popup selezionare la nuova azione appena creata (defineKeyword: in questo esempio):

Associazione di un'azione

Salvare le modifiche apportate allo storyboard e tornare a Visual Studio per Mac per sincronizzare le modifiche. Se si esegue l'app, la voce di menu a cui è stata connessa l'azione personalizzata verrà abilitata/disabilitata automaticamente (in base alla finestra con l'azione aperta) e selezionando la voce di menu verrà attivata l'azione:

Test della nuova azione

Aggiunta, modifica ed eliminazione di menu

Come abbiamo visto nelle sezioni precedenti, un'applicazione Xamarin.Mac include un numero predefinito di menu e voci di menu predefinite che specifici controlli dell'interfaccia utente attiveranno e risponderanno automaticamente. È stato anche illustrato come aggiungere codice all'applicazione che abiliterà e risponderà anche a questi elementi predefiniti.

In questa sezione verrà esaminata la rimozione delle voci di menu che non sono necessarie, la riorganizzazione dei menu e l'aggiunta di nuovi menu, voci di menu e azioni.

Fare doppio clic sul file Main.storyboard nel riquadro della soluzione per aprirlo per la modifica:

Fare doppio clic sul file storyboard per modificare l'interfaccia utente in Xcode.

Per l'applicazione Xamarin.Mac specifica non verrà usato il menu Visualizzazione predefinito, quindi verrà rimosso. Nella gerarchia dell'interfaccia selezionare la voce di menu Visualizza che fa parte della barra dei menu principale:

Selezione della voce di menu Visualizza

Premere elimina o backspace per eliminare il menu. Successivamente, non useremo tutti gli elementi nel menu Formato e vogliamo spostare gli elementi che useremo da sotto i menu secondari. Nella gerarchia dell'interfaccia selezionare le voci di menu seguenti:

Evidenziazione di più elementi

Trascinare le voci sotto il menu padre dal sottomenu in cui sono attualmente:

Trascinamento delle voci di menu nel menu padre

Il menu dovrebbe ora essere simile al seguente:

Elementi nella nuova posizione

Trascinare quindi il sottomenu Testo dal menu Formato e posizionarlo sulla barra dei menu principale tra i menu Formato e Finestra :

Menu Testo

Torniamo nel menu Formato ed eliminiamo la voce di menu secondario Font . Selezionare quindi il menu Formato e rinominarlo "Font":

Menu Carattere

Creare quindi un menu personalizzato di frasi predefinite che verranno aggiunte automaticamente al testo nella visualizzazione testo quando vengono selezionate. Nella casella di ricerca nella parte inferiore del controllo libreria digitare "menu". In questo modo sarà più semplice trovare e usare tutti gli elementi dell'interfaccia utente del menu:

Controllo libreria

A questo punto, eseguire le operazioni seguenti per creare il menu:

  1. Trascinare una voce di menu da Controllo libreria sulla barra dei menu tra i menu Testo e Finestra :

    Selezione di una nuova voce di menu nella libreria

  2. Rinominare l'elemento "Phrase":

    Impostazione del nome del menu

  3. Trascinare quindi un menu da Controllo libreria:

    Selezione di un menu dalla libreria

  4. Drop then Menu on the new Menu Item we just created and change its name to "Phrase":

    Modifica del nome del menu

  5. Rinominare ora le tre voci di menu predefinite "Indirizzo", "Data" e "Saluto":

    Menu Frasi

  6. Aggiungere una quarta voce di menu trascinando una voce di menu da Controllo libreria e chiamandola "Firma":

    Modifica del nome della voce di menu

  7. Salvare le modifiche apportate alla barra dei menu.

A questo punto si creerà un set di azioni personalizzate in modo che le nuove voci di menu vengano esposte al codice C#. In Xcode passiamo alla visualizzazione Assistente :

Creazione delle azioni necessarie

Eseguire le operazioni seguenti:

  1. Trascinare il controllo dalla voce di menu Indirizzo al file AppDelegate.h .

  2. Impostare il tipo di Connessione ion suAzione:

    Selezione del tipo di azione

  3. Immettere un nome "phraseAddress" e premere il pulsante Connessione per creare la nuova azione:

    Configurazione dell'azione immettendo un nome.

  4. Ripetere i passaggi precedenti per le voci di menu Data, Saluto e Firma :

    Azioni completate

  5. Salvare le modifiche apportate alla barra dei menu.

Successivamente, è necessario creare un punto di uscita per la visualizzazione testo in modo che sia possibile regolarne il contenuto dal codice. Selezionare il file ViewController.h nell'Editor assistente e creare un nuovo outlet denominato documentText:

Creazione di un punto di uscita

Tornare a Visual Studio per Mac per sincronizzare le modifiche da Xcode. Modificare quindi il file ViewController.cs e impostarlo come segue:

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

Questo espone il testo della visualizzazione testo all'esterno della ViewController classe e informa il delegato dell'app quando la finestra ottiene o perde lo stato attivo. Modificare ora il file AppDelegate.cs e renderlo simile al seguente:

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

In questo caso è stata creata una AppDelegate classe parziale in modo da poter usare le azioni e i punti di distribuzione definiti in Interface Builder. Viene inoltre esposto un textEditor oggetto per tenere traccia della finestra attualmente attiva.

I metodi seguenti vengono usati per gestire i menu e le voci di menu personalizzati:

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

A questo punto, se si esegue l'applicazione, tutti gli elementi del menu Frasi saranno attivi e aggiungeranno la frase give alla visualizzazione testo quando è selezionata:

Esempio di esecuzione dell'app

Ora che sono disponibili le nozioni di base sull'uso della barra dei menu dell'applicazione verso il basso, si esaminerà la creazione di un menu contestuale personalizzato.

Creazione di menu dal codice

Oltre a creare menu e voci di menu con Interface Builder di Xcode, potrebbero verificarsi momenti in cui un'app Xamarin.Mac deve creare, modificare o rimuovere un menu, un sottomenu o una voce di menu dal codice.

Nell'esempio seguente viene creata una classe per contenere le informazioni sulle voci di menu e i sottomenu che verranno creati dinamicamente in tempo reale:

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

Aggiunta di menu ed elementi

Con questa classe definita, la routine seguente analizzerà una raccolta di LanguageFormatCommandoggetti e creerà in modo ricorsivo nuovi menu e voci di menu aggiungendoli alla fine del menu esistente (creato in Interface Builder) passato:

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

Per qualsiasi LanguageFormatCommand oggetto con una proprietà vuota Title , questa routine crea una voce di menu Separatore (una linea grigia sottile) tra le sezioni di menu:

menuItem = NSMenuItem.SeparatorItem;

Se viene specificato un titolo, viene creata una nuova voce di menu con tale titolo:

menuItem = new NSMenuItem (command.Title);

Se l'oggetto LanguageFormatCommand contiene oggetti figlio LanguageFormatCommand , viene creato un sottomenu e il AssembleMenu metodo viene chiamato in modo ricorsivo per compilare tale menu:

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

Per qualsiasi nuova voce di menu che non dispone di sottomenu, il codice viene aggiunto per gestire la voce di menu selezionata dall'utente:

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

Test della creazione del menu

Con tutto il codice precedente, se è stata creata la raccolta di LanguageFormatCommand oggetti seguente:

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

E tale AssembleMenu raccolta passata alla funzione (con il menu Formato impostato come base), verranno creati i menu dinamici e le voci di menu seguenti:

Nuove voci di menu nell'app in esecuzione

Rimozione di menu ed elementi

Se devi rimuovere qualsiasi voce di menu o menu dall'interfaccia utente dell'app, puoi usare il RemoveItemAt metodo della NSMenu classe semplicemente assegnando l'indice in base zero dell'elemento da rimuovere.

Ad esempio, per rimuovere i menu e le voci di menu create dalla routine precedente, è possibile usare il codice seguente:

public void UnpopulateFormattingMenu(NSMenu menu) {

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

Nel caso del codice precedente, le prime quattro voci di menu vengono create in Interface Builder di Xcode e non sono disponibili nell'app, quindi non vengono rimosse in modo dinamico.

Menu contestuali

I menu contestuali vengono visualizzati quando l'utente fa clic con il pulsante destro del mouse o fa clic su un elemento in una finestra. Per impostazione predefinita, molti degli elementi dell'interfaccia utente incorporati in macOS dispongono già di menu contestuali collegati, ad esempio la visualizzazione testo. Tuttavia, potrebbero verificarsi momenti in cui si vogliono creare menu contestuali personalizzati per un elemento dell'interfaccia utente aggiunto a una finestra.

Modificare il file Main.storyboard in Xcode e aggiungere una finestra Finestra alla progettazione, impostarne la classe su "NSPanel" in Identity Inspector, aggiungere un nuovo elemento Assistente al menu Finestra e allegarlo alla nuova finestra usando uno Show Segue:

Impostazione del tipo segue nel file dello storyboard principale.

Eseguire le operazioni seguenti:

  1. Trascinare un'etichetta da Controllo libreria nella finestra Pannello e impostarne il testo su "Property":

    Modifica del valore dell'etichetta

  2. Trascinare quindi un menu da Controllo libreria nel controller di visualizzazione nella gerarchia di visualizzazione e rinominare le tre voci di menu predefinite Document, Text e Font:

    Voci di menu necessarie

  3. Trascinare ora dal controllo Etichetta proprietà nel menu:

    Trascinamento per creare una segue

  4. Nella finestra di dialogo popup selezionare Menu:

    Impostando il tipo segue selezionando il menu Outlet nel menu di scelta rapida Etichetta.

  5. Da Identity Inspector impostare la classe del controller di visualizzazione su "PanelViewController":

    Impostazione della classe segue

  6. Tornare a Visual Studio per Mac per la sincronizzazione, quindi tornare a Interface Builder.

  7. Passare all'Editor assistente e selezionare il file PanelViewController.h.

  8. Creare un'azione per la voce di menu Documento denominata propertyDocument:

    Configurazione dell'azione denominata propertyDocument.

  9. Ripetere la creazione di azioni per le voci di menu rimanenti:

    Azioni ripetute per le voci di menu rimanenti.

  10. Creare infine un outlet per l'etichetta di proprietà denominata propertyLabel:

    Configurazione dell'uscita

  11. Salvare le modifiche e tornare a Visual Studio per Mac per la sincronizzazione con Xcode.

Modificare il file PanelViewController.cs e aggiungere il codice seguente:

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

A questo punto, se si esegue l'applicazione e si fa clic con il pulsante destro del mouse sull'etichetta della proprietà nel pannello, verrà visualizzato il menu contestuale personalizzato. Se si seleziona e si seleziona l'elemento dal menu, il valore dell'etichetta cambierà:

Menu contestuale in esecuzione

Si esaminerà ora la creazione di menu della barra di stato.

Menu barra di stato

I menu della barra di stato visualizzano una raccolta di voci di menu di stato che forniscono interazione con o feedback all'utente, ad esempio un menu o un'immagine che riflette lo stato di un'applicazione. Il menu della barra di stato di un'applicazione è abilitato e attivo anche se l'applicazione è in esecuzione in background. La barra di stato a livello di sistema si trova sul lato destro della barra dei menu dell'applicazione ed è l'unica barra di stato attualmente disponibile in macOS.

Modificare il file AppDelegate.cs e impostare il DidFinishLaunching metodo come segue:

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; consente di accedere alla barra di stato a livello di sistema. var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable); crea un nuovo elemento della barra di stato. Da qui creiamo un menu e una serie di voci di menu e allegare il menu alla voce della barra di stato appena creata.

Se si esegue l'applicazione, verrà visualizzato il nuovo elemento della barra di stato. Se si seleziona una voce dal menu, il testo verrà modificato nella visualizzazione testo:

Menu della barra di stato in esecuzione

Si esaminerà quindi la creazione di voci di menu di ancoraggio personalizzate.

Menu di ancoraggio personalizzati

Il menu dock viene visualizzato per l'applicazione Mac quando l'utente fa clic con il pulsante destro del mouse o fa clic sull'icona dell'applicazione nel dock:

Un menu di ancoraggio personalizzato

Per creare un menu dock personalizzato per l'applicazione, seguire questa procedura:

  1. In Visual Studio per Mac fare clic con il pulsante destro del mouse sul progetto dell'applicazione e scegliere Aggiungi>nuovo file... Nella finestra di dialogo nuovo file selezionare Xamarin.Mac Empty Interface Definition (Definizione interfaccia vuota Xamarin.Mac>), usare "DockMenu" per Name (DockMenu) e fare clic sul pulsante Nuovo per creare il nuovo file DockMenu.xib:

    Aggiunta di una definizione di interfaccia vuota

  2. Nel riquadro della soluzione fare doppio clic sul file DockMenu.xib per aprirlo per la modifica in Xcode. Creare un nuovo menu con gli elementi seguenti: Indirizzo, Data, Saluto e Firma

    Creazione del layout dell'interfaccia utente

  3. Successivamente, connettere le nuove voci di menu alle azioni esistenti create per il menu personalizzato nella sezione Aggiunta, modifica ed eliminazione di menu precedente. Passare al controllo Connessione ion e selezionare il primo risponditore nella gerarchia dell'interfaccia. Scorrere verso il basso e trovare l'azione phraseAddress: . Trascinare una riga dal cerchio su tale azione alla voce di menu Indirizzo :

    Trascinando una riga nella voce di menu Indirizzo.

  4. Ripetere per tutte le altre voci di menu che le collegano alle azioni corrispondenti:

    Ripetizione per altre voci di menu che le collegano alle azioni corrispondenti.

  5. Selezionare quindi l'applicazione nella gerarchia dell'interfaccia. Nel controllo Connessione ion trascinare una linea dal cerchio sulla dockMenu presa al menu appena creato:

    Trascinando il filo su l'uscita

  6. Salvare le modifiche e tornare a Visual Studio per Mac per la sincronizzazione con Xcode.

  7. Fare doppio clic sul file Info.plist per aprirlo per la modifica:

    Modifica del file info.plist

  8. Fare clic sulla scheda Origine nella parte inferiore della schermata:

    Selezione della visualizzazione Origine

  9. Fare clic su Aggiungi nuova voce, fare clic sul pulsante con il segno più verde, impostare il nome della proprietà su "AppleDockMenu" e il valore su "DockMenu" (il nome del nuovo file con estensione xib senza estensione):

    Aggiunta dell'elemento DockMenu

Ora, se si esegue l'applicazione e si fa clic con il pulsante destro del mouse sull'icona nel Dock, verranno visualizzate le nuove voci di menu:

Esempio del menu dock in esecuzione

Se si seleziona una delle voci personalizzate dal menu, il testo nella visualizzazione testo verrà modificato.

Pulsante popup ed elenchi a discesa

Un pulsante popup visualizza un elemento selezionato e presenta un elenco di opzioni da selezionare quando si fa clic sull'utente. Un elenco a discesa è un tipo di pulsante popup usato in genere per la selezione di comandi specifici per il contesto dell'attività corrente. Entrambi possono essere visualizzati ovunque in una finestra.

Per creare un pulsante popup personalizzato per l'applicazione, seguire questa procedura:

  1. Modificare il file Main.storyboard in Xcode e trascinare un pulsante Popup da Controllo libreria nella finestra Pannello creato nella sezione Menu contestuali :

    Aggiunta di un pulsante popup

  2. Aggiungere una nuova voce di menu e impostare i titoli degli elementi nel popup su: Indirizzo, Data, Saluto e Firma

    Configurazione delle voci di menu

  3. Successivamente, connettere le nuove voci di menu alle azioni esistenti create per il menu personalizzato nella sezione Aggiunta, modifica ed eliminazione di menu precedente. Passare al controllo Connessione ion e selezionare il primo risponditore nella gerarchia dell'interfaccia. Scorrere verso il basso e trovare l'azione phraseAddress: . Trascinare una riga dal cerchio su tale azione alla voce di menu Indirizzo :

    Trascinamento per collegare un'azione

  4. Ripetere per tutte le altre voci di menu che le collegano alle azioni corrispondenti:

    Tutte le azioni necessarie

  5. Salvare le modifiche e tornare a Visual Studio per Mac per la sincronizzazione con Xcode.

A questo punto, se si esegue l'applicazione e si seleziona un elemento dal popup, il testo nella visualizzazione testo cambierà:

Esempio di esecuzione popup

È possibile creare e usare elenchi a discesa esattamente come i pulsanti popup. Invece di collegarsi all'azione esistente, è possibile creare azioni personalizzate proprio come è stato fatto per il menu contestuale nella sezione Menu contestuali .

Riepilogo

Questo articolo ha esaminato in dettaglio l'uso di menu e voci di menu in un'applicazione Xamarin.Mac. Prima abbiamo esaminato la barra dei menu dell'applicazione, quindi abbiamo esaminato la creazione di menu contestuali, quindi abbiamo esaminato i menu della barra di stato e i menu di ancoraggio personalizzati. Infine, abbiamo trattato i menu a comparsa e gli elenchi a discesa.