Menus dans Xamarin.Mac

Cet article traite de l’utilisation des menus dans une application Xamarin.Mac. Il décrit la création et la maintenance des menus et des éléments de menu dans Xcode et Interface Builder et leur utilisation par programmation.

Lorsque vous utilisez C# et .NET dans une application Xamarin.Mac, vous avez accès aux mêmes menus Cocoa qu’un développeur travaillant dans Objective-C et Xcode. Étant donné que Xamarin.Mac s’intègre directement à Xcode, vous pouvez utiliser le Générateur d’interface de Xcode pour créer et gérer vos barres de menus, menus et éléments de menu (ou éventuellement les créer directement dans le code C#).

Les menus font partie intégrante de l’expérience utilisateur d’une application Mac et apparaissent généralement dans différentes parties de l’interface utilisateur :

  • Barre de menus de l’application : il s’agit du menu principal qui apparaît en haut de l’écran pour chaque application Mac.
  • Menus contextuels : ceux-ci s’affichent lorsque l’utilisateur clique avec le bouton droit ou contrôle sur un élément dans une fenêtre.
  • Barre d’état : il s’agit de la zone située à l’extrême droite de la barre de menus de l’application qui apparaît en haut de l’écran (à gauche de l’horloge de la barre de menus) et augmente à gauche à mesure que les éléments sont ajoutés à celui-ci.
  • Menu Dock : menu de chaque application dans le dock qui s’affiche lorsque l’utilisateur clique avec le bouton droit ou clique sur l’icône de l’application, ou lorsque l’utilisateur clique sur l’icône et conserve le bouton de la souris vers le bas.
  • Bouton contextuel et listes déroulantes : un bouton contextuel affiche un élément sélectionné et présente une liste d’options à sélectionner lorsqu’il clique sur l’utilisateur. Une liste déroulante est un type de bouton contextuel généralement utilisé pour sélectionner des commandes spécifiques au contexte de la tâche actuelle. Les deux peuvent apparaître n’importe où dans une fenêtre.

An example menu

Dans cet article, nous allons aborder les principes fondamentaux de l’utilisation des barres de menus, menus et éléments de menu Cocoa dans une application Xamarin.Mac. Il est fortement suggéré que vous travaillez tout d’abord dans l’article Hello, Mac , en particulier les sections Introduction to Xcode and Interface Builder et Outlets and Actions , car elle couvre les concepts et techniques clés que nous utiliserons dans cet article.

Vous pouvez également examiner les classes /méthodes C# exposantes dansObjective-Cla section du document interne Xamarin.Mac, ainsi que les RegisterExport attributs utilisés pour connecter vos classes C# à des objets et des Objective-C éléments d’interface utilisateur.

Barre de menus de l’application

Contrairement aux applications s’exécutant sur le système d’exploitation Windows où chaque fenêtre peut avoir sa propre barre de menus attachée, chaque application s’exécutant sur macOS a une barre de menus unique qui s’exécute le long de l’écran utilisé pour chaque fenêtre de cette application :

A menu bar

Les éléments de cette barre de menus sont activés ou désactivés en fonction du contexte ou de l’état actuel de l’application et de son interface utilisateur à un moment donné. Par exemple : si l’utilisateur sélectionne un champ de texte, les éléments du menu Édition sont activés, tels que Copier et Couper.

Selon Apple et par défaut, toutes les applications macOS ont un ensemble standard de menus et d’éléments de menu qui apparaissent dans la barre de menus de l’application :

  • Menu Apple : ce menu permet d’accéder aux éléments à l’échelle du système disponibles pour l’utilisateur à tout moment, quelle que soit l’application en cours d’exécution. Ces éléments ne peuvent pas être modifiés par le développeur.
  • Menu De l’application : ce menu affiche le nom de l’application en gras et aide l’utilisateur à identifier l’application en cours d’exécution. Il contient des éléments qui s’appliquent à l’application dans son ensemble et qui ne sont pas un document ou un processus donné, tels que la fermeture de l’application.
  • Menu Fichier : éléments utilisés pour créer, ouvrir ou enregistrer des documents avec utilisant votre application. Si votre application n’est pas basée sur un document, ce menu peut être renommé ou supprimé.
  • Menu Modifier : contient des commandes telles que Couper, Copier et Coller qui sont utilisées pour modifier ou modifier des éléments dans l’interface utilisateur de l’application.
  • Menu Format : si l’application fonctionne avec du texte, ce menu contient des commandes pour ajuster la mise en forme de ce texte.
  • Menu Affichage : contient des commandes qui affectent l’affichage du contenu (affiché) dans l’interface utilisateur de l’application.
  • Menus spécifiques à l’application : il s’agit de menus spécifiques à votre application (par exemple, un menu signets pour un navigateur web). Ils doivent apparaître entre les menus Affichage et Fenêtre dans la barre.
  • Menu Fenêtre : contient des commandes permettant d’utiliser des fenêtres dans votre application, ainsi qu’une liste de fenêtres ouvertes actuelles.
  • Menu d’aide : si votre application fournit de l’aide à l’écran, le menu Aide doit être le menu le plus à droite de la barre.

Pour plus d’informations sur la barre de menus de l’application et les menus standard et les éléments de menu, consultez les instructions relatives à l’interface humaine d’Apple.

Barre de menus de l’application par défaut

Chaque fois que vous créez un projet Xamarin.Mac, vous obtenez automatiquement une barre de menus d’application standard et par défaut qui contient les éléments typiques qu’une application macOS aurait normalement (comme indiqué dans la section ci-dessus). La barre de menus par défaut de votre application est définie dans le fichier Main.storyboard (ainsi que le reste de l’interface utilisateur de votre application) sous le projet dans le Panneau Solution :

Select the main storyboard

Double-cliquez sur le fichier Main.storyboard pour l’ouvrir pour la modifier dans le Générateur d’interface de Xcode et vous serez présenté avec l’interface de l’éditeur de menu :

Editing the UI in Xcode, showing the Main dot storyboard.

À partir de là, nous pouvons cliquer sur des éléments tels que l’élément de menu Ouvrir dans le menu Fichier et modifier ou ajuster ses propriétés dans l’inspecteur d’attributs :

Editing a menu's attributes

Nous allons nous familiariser avec l’ajout, la modification et la suppression de menus et d’éléments plus loin dans cet article. Pour l’instant, nous voulons simplement voir quels menus et éléments de menu sont disponibles par défaut et comment ils ont été automatiquement exposés au code via un ensemble de points de vente et d’actions prédéfinis (pour plus d’informations, consultez notre documentation Sur les points de vente et actions ).

Par exemple, si nous cliquez sur l’inspecteur d’Connecter ion pour l’élément de menu Ouvrir, nous pouvons voir qu’il est automatiquement câblé à l’action openDocument: :

Viewing the attached action

Si vous sélectionnez le premier répondeur dans la hiérarchie d’interface et faites défiler vers le bas dans l’inspecteur Connecter ion, et vous verrez la définition de l’action openDocument: à laquelle l’élément de menu Ouvrir est attaché (ainsi que plusieurs autres actions par défaut pour l’application qui sont et ne sont pas automatiquement câblées aux contrôles) :

Viewing all attached actions

Pourquoi est-ce important ? Dans la section suivante, vous verrez comment ces actions définies automatiquement fonctionnent avec d’autres éléments d’interface utilisateur Cocoa pour activer et désactiver automatiquement les éléments de menu, ainsi que fournir des fonctionnalités intégrées pour les éléments.

Plus tard, nous allons utiliser ces actions intégrées pour activer et désactiver des éléments à partir du code et fournir nos propres fonctionnalités lorsqu’elles sont sélectionnées.

Fonctionnalités de menu intégrées

Si vous avez exécuté une application Xamarin.Mac nouvellement créée avant d’ajouter des éléments ou du code d’interface utilisateur, vous remarquerez que certains éléments sont automatiquement câblés et activés pour vous (avec une fonctionnalité complète intégrée), par exemple l’élément quitter l’application :

An enabled menu item

Bien que d’autres éléments de menu, tels que Couper, Copier et Coller , ne soient pas :

Disabled menu items

Arrêtons l’application et double-cliquez sur le fichier Main.storyboard dans le Panneau Solution pour l’ouvrir pour modification dans le Générateur d’interface de Xcode. Ensuite, faites glisser un affichage texte de la bibliothèque vers le contrôleur d’affichage de la fenêtre dans l’Éditeur d’interface :

Selecting a Text View from the Library

Dans l’Éditeur de contraintes, nous allons épingler l’affichage texte aux bords de la fenêtre et le définir là où elle augmente et se réduit avec la fenêtre en cliquant sur les quatre faisceaux d’I rouges en haut de l’éditeur et en cliquant sur le bouton Ajouter 4 contraintes :

Editing the contraints

Enregistrez vos modifications dans la conception de l’interface utilisateur et revenez au Visual Studio pour Mac pour synchroniser les modifications avec votre projet Xamarin.Mac. À présent, démarrez l’application, tapez du texte dans l’affichage texte, sélectionnez-le, puis ouvrez le menu Modifier :

The menu items are automatically enabled/disabled

Notez que les éléments Couper, Copier et Coller sont automatiquement activés et entièrement fonctionnels, sans écrire une seule ligne de code.

Comment cela se fait-il ? N’oubliez pas les actions prédéfinies intégrées qui sont connectées aux éléments de menu par défaut (comme indiqué ci-dessus), la plupart des éléments de l’interface utilisateur Cocoa qui font partie de macOS ont intégré des crochets à des actions spécifiques (par exemple copy:). Par conséquent, lorsqu’elles sont ajoutées à une fenêtre, active et sélectionnée, l’élément de menu ou les éléments correspondants attachés à cette action sont automatiquement activés. Si l’utilisateur sélectionne cet élément de menu, la fonctionnalité intégrée à l’élément d’interface utilisateur est appelée et exécutée, sans intervention du développeur.

Activation et désactivation des menus et des éléments

Par défaut, chaque fois qu’un événement utilisateur se produit, NSMenu active et désactive automatiquement chaque menu et élément de menu visibles en fonction du contexte de l’application. Il existe trois façons d’activer/désactiver un élément :

  • Activation automatique du menu : un élément de menu est activé si NSMenu un objet approprié répond à l’action à laquelle l’élément est connecté. Par exemple, la vue de texte ci-dessus qui avait un hook intégré à l’action copy: .
  • Actions personnalisées et validateMenuItem : pour tous les éléments de menu liés à une action personnalisée de contrôleur de fenêtre ou d’affichage, vous pouvez ajouter l’action validateMenuItem: et activer ou désactiver manuellement les éléments de menu.
  • Menu manuel activé : vous définissez manuellement la Enabled propriété de chaque NSMenuItem élément pour activer ou désactiver chaque élément dans un menu individuellement.

Pour choisir un système, définissez la AutoEnablesItems propriété d’un NSMenu. true est automatique (comportement par défaut) et false est manuel.

Important

Si vous choisissez d’utiliser le menu manuel activé, aucun des éléments de menu, même ceux contrôlés par les classes AppKit comme NSTextView, sont mis à jour automatiquement. Vous serez responsable de l’activation et de la désactivation de tous les éléments par le code.

Utilisation de validateMenuItem

Comme indiqué ci-dessus, pour tout élément de menu lié à une action personnalisée du contrôleur de fenêtre ou d’affichage, vous pouvez ajouter l’action validateMenuItem: et activer ou désactiver manuellement les éléments de menu.

Dans l’exemple suivant, la Tag propriété sera utilisée pour déterminer le type d’élément de menu qui sera activé/désactivé par l’action en fonction de l’état validateMenuItem: du texte sélectionné dans un NSTextView. La Tag propriété a été définie dans Le Générateur d’interface pour chaque élément de menu :

Setting the Tag property

Et le code suivant ajouté au contrôleur de vue :

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

Lorsque ce code est exécuté et qu’aucun texte n’est sélectionné dans le NSTextViewcode, les deux éléments de menu encapsulés sont désactivés (même s’ils sont câblés aux actions sur le contrôleur d’affichage) :

Showing disabled items

Si une section de texte est sélectionnée et que le menu est rouvert, les deux éléments de menu encapsulés sont disponibles :

Showing enabled items

Activation et réponse aux éléments de menu dans le code

Comme nous l’avons vu ci-dessus, simplement en ajoutant des éléments d’interface utilisateur Cocoa spécifiques à notre conception d’interface utilisateur (par exemple, un champ de texte), plusieurs éléments de menu par défaut sont activés et fonctionnent automatiquement, sans avoir à écrire de code. Examinons ensuite l’ajout de notre propre code C# à notre projet Xamarin.Mac pour activer un élément de menu et fournir des fonctionnalités lorsque l’utilisateur le sélectionne.

Par exemple, supposons que l’utilisateur puisse utiliser l’élément Ouvrir dans le menu Fichier pour sélectionner un dossier. Étant donné que nous voulons qu’il s’agit d’une fonction à l’échelle de l’application et non limitée à un élément de fenêtre ou d’interface utilisateur donné, nous allons ajouter le code pour le gérer à notre délégué d’application.

Dans le panneau Solution, double-cliquez sur le AppDelegate.CS fichier pour l’ouvrir pour modification :

Selecting the app delegate

Ajoutez le code suivant à la méthode 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 ();
    }
}

Nous allons exécuter l’application maintenant et ouvrir le menu Fichier :

The File menu

Notez que l’élément de menu Ouvrir est désormais activé. Si nous le sélectionnons, la boîte de dialogue d’ouverture s’affiche :

An open dialog

Si nous cliquez sur le bouton Ouvrir , notre message d’alerte s’affiche :

An example dialog message

La ligne clé ici était [Export ("openDocument:")], il indique NSMenu que notre AppDelegate a une méthode void OpenDialog (NSObject sender) qui répond à l’action openDocument: . Si vous vous souvenez de ce qui précède, l’élément de menu Ouvrir est automatiquement câblé à cette action par défaut dans le Générateur d’interface :

Viewing the attached actions

Examinons ensuite la création de notre propre menu, des éléments de menu et des actions et répondons-y dans le code.

Utilisation du menu récent ouvert

Par défaut, le menu Fichier contient un élément Open Recent qui effectue le suivi des derniers fichiers ouverts par l’utilisateur avec votre application. Si vous créez une NSDocument application Xamarin.Mac basée, ce menu sera géré automatiquement pour vous. Pour tout autre type d’application Xamarin.Mac, vous serez chargé de gérer et de répondre manuellement à cet élément de menu.

Pour gérer manuellement le menu Ouvrir récent , vous devez d’abord l’informer qu’un nouveau fichier a été ouvert ou enregistré à l’aide des éléments suivants :

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

Même si votre application n’utilise NSDocumentspas , vous utilisez toujours le NSDocumentControllermenu Ouvrir récent en envoyant un NSUrl emplacement du fichier à la NoteNewRecentDocumentURL méthode du SharedDocumentControllerfichier .

Ensuite, vous devez remplacer la OpenFile méthode du délégué d’application pour ouvrir un fichier que l’utilisateur sélectionne dans le menu Ouvrir récent . Par exemple :

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

Retourne true si le fichier peut être ouvert, sinon false un avertissement intégré s’affiche à l’utilisateur que le fichier n’a pas pu être ouvert.

Étant donné que le nom de fichier et le chemin d’accès retournés à partir du menu Ouvrir récent peuvent inclure un espace, nous devons échapper correctement à ce caractère avant de créer une NSUrl erreur ou nous obtiendrons une erreur. Nous procédons ainsi avec le code suivant :

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

Enfin, nous créons un NSUrl point qui pointe vers le fichier et utilisons une méthode d’assistance dans le délégué de l’application pour ouvrir une nouvelle fenêtre et charger le fichier dans celui-ci :

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

Pour rassembler tous les éléments, examinons un exemple d’implémentation dans un fichier 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 fonction des exigences de votre application, vous ne souhaiterez peut-être pas que l’utilisateur ouvre le même fichier dans plusieurs fenêtres en même temps. Dans notre exemple d’application, si l’utilisateur choisit un fichier déjà ouvert (à partir des éléments de menu Ouvrir récent ou Open.. ), la fenêtre qui contient le fichier est mise en avant.

Pour ce faire, nous avons utilisé le code suivant dans notre méthode d’assistance :

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

Nous avons conçu notre ViewController classe pour contenir le chemin d’accès au fichier dans sa Path propriété. Ensuite, nous parcourons toutes les fenêtres actuellement ouvertes dans l’application. Si le fichier est déjà ouvert dans l’une des fenêtres, il est placé devant toutes les autres fenêtres à l’aide de :

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

Si aucune correspondance n’est trouvée, une nouvelle fenêtre est ouverte avec le fichier chargé et le fichier est noté dans le menu Ouvrir récent :

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

Utilisation des actions de fenêtre personnalisées

Tout comme les actions intégrées de First Responder qui sont pré-câblées aux éléments de menu standard, vous pouvez créer de nouvelles actions personnalisées et les connecter à des éléments de menu dans le Générateur d’interface.

Tout d’abord, définissez une action personnalisée sur l’un des contrôleurs de fenêtre de votre application. Par exemple :

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

Ensuite, double-cliquez sur le fichier storyboard de l’application dans le Panneau Solution pour l’ouvrir pour modification dans le Générateur d’interface de Xcode. Sélectionnez le premier répondeur sous la scène de l’application, puis basculez vers l’inspecteur d’attributs :

The Attributes Inspector

Cliquez sur le + bouton en bas de l’inspecteur d’attributs pour ajouter une nouvelle action personnalisée :

Adding a new action

Donnez-lui le même nom que l’action personnalisée que vous avez créée sur votre contrôleur de fenêtre :

Editing the action name

Cliquez sur le contrôle et faites glisser d’un élément de menu vers le premier répondeur sous la scène d’application. Dans la liste contextuelle, sélectionnez la nouvelle action que vous venez de créer (defineKeyword: dans cet exemple) :

Attaching an action

Enregistrez les modifications dans le storyboard et revenez à Visual Studio pour Mac pour synchroniser les modifications. Si vous exécutez l’application, l’élément de menu auquel vous avez connecté l’action personnalisée sera automatiquement activé/désactivé (en fonction de la fenêtre avec l’action ouverte) et la sélection de l’élément de menu déclenche l’action :

Testing the new action

Ajout, modification et suppression de menus

Comme nous l’avons vu dans les sections précédentes, une application Xamarin.Mac est fournie avec un nombre prédéfini de menus par défaut et d’éléments de menu auxquels des contrôles d’interface utilisateur spécifiques activent et répondent automatiquement. Nous avons également vu comment ajouter du code à notre application qui permettra également de répondre à ces éléments par défaut.

Dans cette section, nous allons examiner la suppression des éléments de menu dont nous n’avons pas besoin, réorganiser les menus et ajouter de nouveaux menus, éléments de menu et actions.

Double-cliquez sur le fichier Main.storyboard dans le Panneau Solution pour l’ouvrir pour modification :

Double-clicking the storyboard file to edit the UI in Xcode.

Pour notre application Xamarin.Mac spécifique, nous n’allons pas utiliser le menu Affichage par défaut afin que nous allons le supprimer. Dans la hiérarchie d’interface, sélectionnez l’élément de menu Affichage qui fait partie de la barre de menus principale :

Selecting the View menu item

Appuyez sur Supprimer ou rétrospace pour supprimer le menu. Ensuite, nous n’allons pas utiliser tous les éléments dans le menu Format et nous voulons déplacer les éléments que nous allons utiliser sous les sous-menus. Dans la hiérarchie d’interface, sélectionnez les éléments de menu suivants :

Highlighting multiple items

Faites glisser les éléments sous le menu parent à partir du sous-menu où ils sont actuellement :

Dragging menu items to the parent menu

Votre menu doit maintenant ressembler à ceci :

The items in the new location

Ensuite, faites glisser le sous-menu Texte à partir du menu Format et placez-le dans la barre de menus principale entre les menus Format et Fenêtre :

The Text menu

Revenons sous le menu Format et supprimez l’élément de sous-menu Police . Ensuite, sélectionnez le menu Format et renommez-le « Police » :

The Font menu

Ensuite, nous allons créer un menu personnalisé d’expressions prédéfinies qui seront automatiquement ajoutées au texte dans l’affichage texte lorsqu’elles sont sélectionnées. Dans la zone de recherche située en bas de l’inspecteur de bibliothèque, tapez « menu ». Cela facilite la recherche et l’utilisation de tous les éléments de l’interface utilisateur du menu :

The Library Inspector

À présent, procédons comme suit pour créer notre menu :

  1. Faites glisser un élément de menu de l’inspecteur de bibliothèque vers la barre de menus entre les menus Texte et Fenêtre :

    Selecting a new menu item in the Library

  2. Renommez l’élément « Phrases » :

    Setting the menu name

  3. Ensuite, faites glisser un menu à partir de l’inspecteur de bibliothèque :

    Selecting a menu from the Library

  4. Déposez puis menu dans le nouvel élément de menu que nous venons de créer et remplacez son nom par « Phrases » :

    Editing the menu name

  5. Nous allons maintenant renommer les trois éléments de menu par défaut « Adresse », « Date » et « Salutation » :

    The Phrases menu

  6. Ajoutons un quatrième élément de menu en faisant glisser un élément de menu à partir de l’inspecteur de bibliothèque et en l’appelant « Signature » :

    Editing the menu item name

  7. Enregistrez les modifications apportées à la barre de menus.

Nous allons maintenant créer un ensemble d’actions personnalisées afin que nos nouveaux éléments de menu soient exposés au code C#. Dans Xcode, nous allons basculer vers la vue Assistant :

Creating the required actions

Procédons comme suit :

  1. Faites glisser le contrôle de l’élément de menu Adresse vers le fichier AppDelegate.h .

  2. Basculez le type d’Connecter ion en Action :

    Selecting the action type

  3. Entrez un nom de « phraseAddress » et appuyez sur le bouton Connecter pour créer l’action :

    Configuring the action by entering a name.

  4. Répétez les étapes ci-dessus pour les éléments de menu Date, Salutation et Signature :

    The completed actions

  5. Enregistrez les modifications apportées à la barre de menus.

Ensuite, nous devons créer une sortie pour notre affichage texte afin de pouvoir ajuster son contenu à partir du code. Sélectionnez le fichier ViewController.h dans l’ÉditeurAssistant et créez une sortie appelée documentText:

Creating an outlet

Revenez à Visual Studio pour Mac pour synchroniser les modifications de Xcode. Modifiez ensuite le fichier ViewController.cs et faites-le ressembler à ce qui suit :

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

Cela expose le texte de notre affichage texte en dehors de la ViewController classe et informe le délégué de l’application lorsque la fenêtre gagne ou perd le focus. Modifiez maintenant le fichier AppDelegate.cs et faites-le ressembler à ce qui suit :

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

Ici, nous avons effectué la AppDelegate classe partielle afin que nous puissions utiliser les actions et les sorties que nous avons définies dans Le Générateur d’interface. Nous exposons également une textEditor fenêtre pour suivre la fenêtre actuellement en cours de focus.

Les méthodes suivantes permettent de gérer nos éléments de menu et de menu personnalisés :

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

À présent, si nous exécutons notre application, tous les éléments du menu Phrase seront actifs et ajouteront l’expression donner à l’affichage texte lorsqu’ils sont sélectionnés :

An example of the app running

Maintenant que nous avons les principes de base de l’utilisation de la barre de menus de l’application vers le bas, examinons la création d’un menu contextuel personnalisé.

Création de menus à partir du code

Outre la création de menus et d’éléments de menu avec le Générateur d’interface de Xcode, il peut arriver qu’une application Xamarin.Mac crée, modifie ou supprime un menu, un sous-menu ou un élément de menu du code.

Dans l’exemple suivant, une classe est créée pour contenir les informations sur les éléments de menu et les sous-menus qui seront créés dynamiquement à la volée :

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

Ajout de menus et d’éléments

Avec cette classe définie, la routine suivante analyse une collection d’objets et génère de manière récursive de LanguageFormatCommandnouveaux menus et éléments de menu en les ajoutant au bas du menu existant (créé dans Interface Builder) qui a été passé :

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

Pour tout LanguageFormatCommand objet qui a une propriété vide Title , cette routine crée un élément de menu Séparateur (une ligne grise mince) entre les sections de menu :

menuItem = NSMenuItem.SeparatorItem;

Si un titre est fourni, un nouvel élément de menu avec ce titre est créé :

menuItem = new NSMenuItem (command.Title);

Si l’objet LanguageFormatCommand contient des objets enfants LanguageFormatCommand , un sous-menu est créé et la AssembleMenu méthode est récursivement appelée pour générer ce menu :

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

Pour tout nouvel élément de menu qui n’a pas de sous-menus, le code est ajouté pour gérer l’élément de menu sélectionné par l’utilisateur :

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

Test de la création du menu

Avec tout le code ci-dessus en place, si la collection d’objets LanguageFormatCommand suivante a été créée :

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

Et cette collection passée à la AssembleMenu fonction (avec le menu Format défini comme base), les menus dynamiques et les éléments de menu suivants sont créés :

The new menu items in the running app

Suppression de menus et d’éléments

Si vous devez supprimer un menu ou un élément de menu de l’interface utilisateur de l’application, vous pouvez utiliser simplement la RemoveItemAt méthode de la NSMenu classe en lui donnant l’index de base zéro de l’élément à supprimer.

Par exemple, pour supprimer les menus et les éléments de menu créés par la routine ci-dessus, vous pouvez utiliser le code suivant :

public void UnpopulateFormattingMenu(NSMenu menu) {

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

Dans le cas du code ci-dessus, les quatre premiers éléments de menu sont créés dans le Générateur d’interface de Xcode et absents disponibles dans l’application, de sorte qu’ils ne sont pas supprimés dynamiquement.

Menus contextuels

Les menus contextuels s’affichent lorsque l’utilisateur clique avec le bouton droit ou clique sur un élément dans une fenêtre. Par défaut, plusieurs éléments de l’interface utilisateur intégrés à macOS ont déjà des menus contextuels qui leur sont attachés (par exemple, l’affichage texte). Toutefois, il peut arriver que nous souhaitions créer nos propres menus contextuels personnalisés pour un élément d’interface utilisateur que nous avons ajouté à une fenêtre.

Nous allons modifier notre fichier Main.storyboard dans Xcode et ajouter une fenêtre Fenêtre à notre conception, définir sa classe sur « NSPanel » dans l’inspecteur d’identité, ajouter un nouvel élément Assistant au menu Fenêtre et l’attacher à la nouvelle fenêtre à l’aide d’un Show Segue :

Setting the segue type in the Main dot storyboard file.

Procédons comme suit :

  1. Faites glisser une étiquette de l’inspecteur de bibliothèque vers la fenêtre panneau et définissez son texte sur « Propriété » :

    Editing the label's value

  2. Ensuite, faites glisser un menu de l’inspecteur de bibliothèque sur le contrôleur de vue dans la hiérarchie d’affichage et renommez les trois éléments de menu par défaut Document, Texte et Police :

    The required menu items

  3. Maintenant, faites glisser le contrôle à partir de l’étiquette de propriété dans le menu :

    Dragging to create a segue

  4. Dans la boîte de dialogue contextuelle, sélectionnez Menu :

    Setting the segue type by selecting menu from Outlets in the Label context menu.

  5. À partir de l’inspecteur d’identité, définissez la classe du contrôleur d’affichage sur « PanelViewController » :

    Setting the segue class

  6. Revenez à Visual Studio pour Mac pour synchroniser, puis revenez au Générateur d’interface.

  7. Basculez vers l’Éditeur Assistant et sélectionnez le fichier PanelViewController.h.

  8. Créez une action pour l’élément de menu Document appelé propertyDocument:

    Configuring the action named propertyDocument.

  9. Répétez la création d’actions pour les éléments de menu restants :

    Repeating actions for the remaining menu items.

  10. Enfin, créez un point de sortie pour l’étiquette de propriété appelée propertyLabel:

    Configuring the outlet

  11. Enregistrez vos modifications et revenez à Visual Studio pour Mac à synchroniser avec Xcode.

Modifiez le fichier PanelViewController.cs et ajoutez le code suivant :

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

Maintenant, si nous exécutons l’application et cliquez avec le bouton droit sur l’étiquette de propriété dans le panneau, nous verrons notre menu contextuel personnalisé. Si nous sélectionnons et élément dans le menu, la valeur de l’étiquette change :

The contextual menu running

Examinons ensuite la création de menus de barre d’état.

Menus de la barre d’état

Les menus de barre d’état affichent une collection d’éléments de menu d’état qui fournissent une interaction avec ou des commentaires à l’utilisateur, tels qu’un menu ou une image reflétant l’état d’une application. Le menu de la barre d’état d’une application est activé et actif même si l’application s’exécute en arrière-plan. La barre d’état à l’échelle du système réside à droite de la barre de menus de l’application et est la seule barre d’état actuellement disponible dans macOS.

Nous allons modifier notre fichier AppDelegate.cs et faire en sorte que la DidFinishLaunching méthode ressemble à ce qui suit :

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; nous donne accès à la barre d’état à l’échelle du système. var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable); crée un élément de barre d’état. À partir de là, nous créons un menu et un certain nombre d’éléments de menu et joignons le menu à l’élément de barre d’état que nous venons de créer.

Si nous exécutons l’application, le nouvel élément de barre d’état s’affiche. La sélection d’un élément dans le menu modifie le texte dans l’affichage texte :

The status bar menu running

Ensuite, examinons la création d’éléments de menu d’ancrage personnalisés.

Menus d’ancrage personnalisés

Le menu d’ancrage s’affiche pour votre application Mac lorsque l’utilisateur clique avec le bouton droit ou contrôle sur l’icône de l’application dans le dock :

A custom dock menu

Nous allons créer un menu d’ancrage personnalisé pour notre application en procédant comme suit :

  1. Dans Visual Studio pour Mac, cliquez avec le bouton droit sur le projet de l’application, puis sélectionnez Ajouter>un nouveau fichier... Dans la boîte de dialogue nouveau fichier, sélectionnez Xamarin.Mac>Empty Interface Definition, utilisez « DockMenu » pour le nom, puis cliquez sur le bouton Nouveau pour créer le fichier DockMenu.xib :

    Adding an empty interface definition

  2. Dans le Panneau Solution, double-cliquez sur le fichier DockMenu.xib pour l’ouvrir pour modification dans Xcode. Créez un menuavec les éléments suivants : Adresse, Date, Message d’accueil et Signature

    Laying out the UI

  3. Ensuite, connectons nos nouveaux éléments de menu à nos actions existantes que nous avons créées pour notre menu personnalisé dans la section Ajout, Modification et Suppression de menus ci-dessus. Basculez vers l’inspecteur d’Connecter ion et sélectionnez le premier répondeur dans la hiérarchie d’interface. Faites défiler vers le bas et recherchez l’action phraseAddress: . Faites glisser une ligne du cercle sur cette action vers l’élément de menu Adresse :

    Dragging a line to the Address menu item.

  4. Répétez pour tous les autres éléments de menu qui les attachent à leurs actions correspondantes :

    Repeating for other menu items attaching them to their corresponding actions.

  5. Ensuite, sélectionnez l’application dans la hiérarchie d’interface. Dans l’inspecteur de Connecter ion, faites glisser une ligne du cercle sur la sortie vers le dockMenu menu que nous venons de créer :

    Dragging the wire up the outlet

  6. Enregistrez vos modifications et revenez à Visual Studio pour Mac pour vous synchroniser avec Xcode.

  7. Double-cliquez sur le fichier Info.plist pour l’ouvrir pour modification :

    Editing the Info.plist file

  8. Cliquez sur l’onglet Source en bas de l’écran :

    Selecting the Source view

  9. Cliquez sur Ajouter une nouvelle entrée, cliquez sur le bouton plus vert, définissez le nom de la propriété sur « AppleDockMenu » et la valeur sur « DockMenu » (nom de notre nouveau fichier .xib sans l’extension) :

    Adding the DockMenu item

Maintenant, si nous exécutons notre application et cliquez avec le bouton droit sur son icône dans le Dock, nos nouveaux éléments de menu seront affichés :

An example of the dock menu running

Si nous sélectionnons l’un des éléments personnalisés dans le menu, le texte de notre affichage texte sera modifié.

Bouton contextuel et listes déroulantes

Un bouton contextuel affiche un élément sélectionné et présente une liste d’options à sélectionner lorsque l’utilisateur clique dessus. Une liste déroulante est un type de bouton contextuel généralement utilisé pour sélectionner des commandes spécifiques au contexte de la tâche actuelle. Les deux peuvent apparaître n’importe où dans une fenêtre.

Nous allons créer un bouton contextuel personnalisé pour notre application en procédant comme suit :

  1. Modifiez le fichier Main.storyboard dans Xcode et faites glisser un bouton contextuel à partir de l’inspecteur de bibliothèque dans la fenêtre Panneau que nous avons créée dans la section Menus contextuels :

    Adding a popup button

  2. Ajoutez un nouvel élément de menu et définissez les titres des éléments dans la fenêtre contextuelle sur : Adresse, Date, Message d’accueil et Signature

    Configuring the menu items

  3. Ensuite, connectons nos nouveaux éléments de menu aux actions existantes que nous avons créées pour notre menu personnalisé dans la section Ajout, Modification et Suppression de menus ci-dessus. Basculez vers l’inspecteur d’Connecter ion et sélectionnez le premier répondeur dans la hiérarchie d’interface. Faites défiler vers le bas et recherchez l’action phraseAddress: . Faites glisser une ligne du cercle sur cette action vers l’élément de menu Adresse :

    Dragging to wire up an action

  4. Répétez pour tous les autres éléments de menu qui les attachent à leurs actions correspondantes :

    All required actions

  5. Enregistrez vos modifications et revenez à Visual Studio pour Mac pour vous synchroniser avec Xcode.

Maintenant, si nous exécutons notre application et que nous sélectionnons un élément dans la fenêtre contextuelle, le texte de notre affichage texte change :

An example of the popup running

Vous pouvez créer et utiliser des listes déroulantes de la même façon que les boutons contextuels. Au lieu de vous attacher à une action existante, vous pouvez créer vos propres actions personnalisées comme nous l’avons fait pour notre menu contextuel dans la section Menus contextuels .

Résumé

Cet article a examiné en détail l’utilisation des menus et des éléments de menu dans une application Xamarin.Mac. Tout d’abord, nous avons examiné la barre de menus de l’application, puis nous avons examiné la création de menus contextuels, puis nous avons examiné les menus de barre d’état et les menus d’ancrage personnalisés. Enfin, nous avons couvert les menus contextuels et les listes déroulantes.