Boîtes de dialogue dans Xamarin.Mac

Lorsque vous utilisez C# et .NET dans une application Xamarin.Mac, vous avez accès aux mêmes boîtes de dialogue et fenêtres modales que celles d’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 fenêtres modales (ou éventuellement les créer directement dans du code C#).

Une boîte de dialogue s’affiche en réponse à une action utilisateur et fournit généralement des façons dont les utilisateurs peuvent effectuer l’action. Une boîte de dialogue nécessite une réponse de l’utilisateur avant qu’elle puisse être fermée.

Windows peut être utilisé dans un état sans mode (par exemple, un éditeur de texte qui peut avoir plusieurs documents ouverts à la fois) ou modal (par exemple, une boîte de dialogue Exporter qui doit être ignorée avant que l’application puisse continuer).

Boîte de dialogue ouverte

Dans cet article, nous allons aborder les principes de base de l’utilisation des dialogues et des fenêtres modales dans une application Xamarin.Mac. Il est fortement suggéré de commencer par l’article Hello, Mac , en particulier les sections Introduction to Xcode et Interface Builderet Outlets and Actions , car il couvre les concepts et techniques clés que nous allons utiliser dans cet article.

Vous pouvez également consulter la section Exposing C# classes/méthodes to Objective-C du document Xamarin.Mac Internals . Il explique les Register commandes et Export utilisées pour connecter vos classes C# à des objets et des éléments d’interface Objective-C utilisateur.

Présentation des boîtes de dialogue

Une boîte de dialogue s’affiche en réponse à une action de l’utilisateur (par exemple, l’enregistrement d’un fichier) et permet aux utilisateurs d’effectuer cette action. Une boîte de dialogue nécessite une réponse de l’utilisateur avant qu’elle puisse être fermée.

Selon Apple, il existe trois façons de présenter un dialogue :

  • Document modal : une boîte de dialogue modale de document empêche l’utilisateur d’effectuer autre chose dans un document donné jusqu’à ce qu’il soit ignoré.
  • Application Modal : une boîte de dialogue modale d’application empêche l’utilisateur d’interagir avec l’application jusqu’à ce qu’elle soit ignorée.
  • Sans mode Une boîte de dialogue sans mode permet aux utilisateurs de modifier les paramètres de la boîte de dialogue tout en interagissant avec la fenêtre de document.

N’importe quelle norme NSWindow peut être utilisée comme boîte de dialogue personnalisée en l’affichant de façon modale :

Exemple de fenêtre modale

Feuilles de dialogue modales de document

Une feuille est une boîte de dialogue modale attachée à une fenêtre de document donnée, empêchant les utilisateurs d’interagir avec la fenêtre jusqu’à ce qu’ils ignorent la boîte de dialogue. Une feuille est attachée à la fenêtre d’où elle sort et une seule feuille peut être ouverte pour une fenêtre à tout moment.

Exemple de feuille modale

Préférences Windows

Une fenêtre Préférences est une boîte de dialogue sans mode qui contient les paramètres de l’application que l’utilisateur modifie rarement. Préférences Windows inclut souvent une barre d’outils qui permet à l’utilisateur de basculer entre différents groupes de paramètres :

Exemple de fenêtre de préférence

Ouvrir la boîte de dialogue

La boîte de dialogue Ouvrir offre aux utilisateurs un moyen cohérent de rechercher et d’ouvrir un élément dans une application :

Boîte de dialogue ouverte

macOS fournit des boîtes de dialogue d’impression et de mise en page standard que votre application peut afficher afin que les utilisateurs puissent avoir une expérience d’impression cohérente dans chaque application qu’ils utilisent.

La boîte de dialogue Imprimer peut être affichée sous la forme d’une boîte de dialogue flottante libre :

Boîte de dialogue Imprimer

Vous pouvez également l’afficher sous la forme d’une feuille :

Feuille d’impression

La boîte de dialogue Mise en page peut être affichée sous la forme d’une boîte de dialogue flottante libre :

Boîte de dialogue de configuration de page

Vous pouvez également l’afficher sous la forme d’une feuille :

Feuille de configuration de page

Enregistrer les boîtes de dialogue

La boîte de dialogue Enregistrer offre aux utilisateurs un moyen cohérent d’enregistrer un élément dans une application. La boîte de dialogue Enregistrer a deux états : Minimal (également appelé Réduit) :

Boîte de dialogue Enregistrer

Et l’état Développé :

Boîte de dialogue d’enregistrement développée

La boîte de dialogue Enregistrement minimal peut également être affichée sous la forme d’une feuille :

Une feuille d’enregistrement minimale

Comme peut le faire la boîte de dialogue Enregistrer développée :

Feuille d’enregistrement développée

Pour plus d’informations, consultez la section Boîtes de dialogue des Instructions relatives à l’interface humaine OS X d’Apple

Ajout d’une fenêtre modale à un projet

En plus de la fenêtre de document main, une application Xamarin.Mac peut avoir besoin d’afficher d’autres types de fenêtres pour l’utilisateur, tels que préférences ou panneaux d’inspecteur.

Pour ajouter une nouvelle fenêtre, procédez comme suit :

  1. Dans le Explorateur de solutions, ouvrez le Main.storyboard fichier à modifier dans le Générateur d’interface de Xcode.

  2. Faites glisser un nouveau contrôleur d’affichage vers l’aire de conception :

    Sélection d’un contrôleur d’affichage dans la bibliothèque

  3. Dans l’inspecteur d’identité, entrez CustomDialogController comme nom de classe :

    Définition du nom de la classe sur CustomDialogController.

  4. Revenez à Visual Studio pour Mac, permettez-lui de se synchroniser avec Xcode et de créer le CustomDialogController.h fichier.

  5. Revenez à Xcode et concevez votre interface :

    Conception de l’interface utilisateur dans Xcode

  6. Créez une séquence modale à partir de la fenêtre principale de votre application vers le nouveau contrôleur d’affichage en faisant glisser le contrôle de l’élément d’interface utilisateur qui ouvrira la boîte de dialogue vers la fenêtre de la boîte de dialogue. Affectez l’identificateurModalSegue :

    Une ségue modale

  7. Wire-up toutes les actions et les points de vente :

    Configuration d’une action

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

Faites en sorte que le CustomDialogController.cs fichier ressemble à ce qui suit :

using System;
using Foundation;
using AppKit;

namespace MacDialog
{
    public partial class CustomDialogController : NSViewController
    {
        #region Private Variables
        private string _dialogTitle = "Title";
        private string _dialogDescription = "Description";
        private NSViewController _presentor;
        #endregion

        #region Computed Properties
        public string DialogTitle {
            get { return _dialogTitle; }
            set { _dialogTitle = value; }
        }

        public string DialogDescription {
            get { return _dialogDescription; }
            set { _dialogDescription = value; }
        }

        public NSViewController Presentor {
            get { return _presentor; }
            set { _presentor = value; }
        }
        #endregion

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

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

            // Set initial title and description
            Title.StringValue = DialogTitle;
            Description.StringValue = DialogDescription;
        }
        #endregion

        #region Private Methods
        private void CloseDialog() {
            Presentor.DismissViewController (this);
        }
        #endregion

        #region Custom Actions
        partial void AcceptDialog (Foundation.NSObject sender) {
            RaiseDialogAccepted();
            CloseDialog();
        }

        partial void CancelDialog (Foundation.NSObject sender) {
            RaiseDialogCanceled();
            CloseDialog();
        }
        #endregion

        #region Events
        public EventHandler DialogAccepted;

        internal void RaiseDialogAccepted() {
            if (this.DialogAccepted != null)
                this.DialogAccepted (this, EventArgs.Empty);
        }

        public EventHandler DialogCanceled;

        internal void RaiseDialogCanceled() {
            if (this.DialogCanceled != null)
                this.DialogCanceled (this, EventArgs.Empty);
        }
        #endregion
    }
}

Ce code expose quelques propriétés pour définir le titre et la description de la boîte de dialogue et quelques événements pour réagir à l’annulation ou à l’acceptation de la boîte de dialogue.

Ensuite, modifiez le ViewController.cs fichier, remplacez la PrepareForSegue méthode et faites-la ressembler à ce qui suit :

public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
    base.PrepareForSegue (segue, sender);

    // Take action based on the segue name
    switch (segue.Identifier) {
    case "ModalSegue":
        var dialog = segue.DestinationController as CustomDialogController;
        dialog.DialogTitle = "MacDialog";
        dialog.DialogDescription = "This is a sample dialog.";
        dialog.DialogAccepted += (s, e) => {
            Console.WriteLine ("Dialog accepted");
            DismissViewController (dialog);
        };
        dialog.Presentor = this;
        break;
    }
}

Ce code initialise le segue que nous avons défini dans le Générateur d’interface de Xcode dans notre boîte de dialogue et configure le titre et la description. Il gère également le choix que l’utilisateur effectue dans la boîte de dialogue.

Nous pouvons exécuter notre application et afficher la boîte de dialogue personnalisée :

Exemple de boîte de dialogue

Pour plus d’informations sur l’utilisation de Windows dans une application Xamarin.Mac, consultez notre documentation Utilisation de Windows .

Création d’une feuille personnalisée

Une feuille est une boîte de dialogue modale attachée à une fenêtre de document donnée, empêchant les utilisateurs d’interagir avec la fenêtre jusqu’à ce qu’ils ignorent la boîte de dialogue. Une feuille est attachée à la fenêtre d’où elle sort et une seule feuille peut être ouverte pour une fenêtre à tout moment.

Pour créer une feuille personnalisée dans Xamarin.Mac, procédons comme suit :

  1. Dans le Explorateur de solutions, ouvrez le Main.storyboard fichier à modifier dans le Générateur d’interface de Xcode.

  2. Faites glisser un nouveau contrôleur d’affichage vers l’aire de conception :

    Sélection d’un contrôleur d’affichage dans la bibliothèque

  3. Concevez votre interface utilisateur :

    Conception de l’interface utilisateur

  4. Créez un segue de feuille à partir de votre fenêtre principale vers le nouveau contrôleur d’affichage :

    Sélection du type de segue Feuille

  5. Dans l’inspecteur d’identité, nommez la classeSheetViewController du contrôleur d’affichage :

    Définition du nom de la classe sur SheetViewController.

  6. Définissez les points de vente et actions nécessaires :

    Définition des points de vente et des actions requis

  7. Enregistrez vos modifications et revenez à Visual Studio pour Mac à synchroniser.

Ensuite, modifiez le SheetViewController.cs fichier et faites-le ressembler à ce qui suit :

using System;
using Foundation;
using AppKit;

namespace MacDialog
{
    public partial class SheetViewController : NSViewController
    {
        #region Private Variables
        private string _userName = "";
        private string _password = "";
        private NSViewController _presentor;
        #endregion

        #region Computed Properties
        public string UserName {
            get { return _userName; }
            set { _userName = value; }
        }

        public string Password {
            get { return _password;}
            set { _password = value;}
        }

        public NSViewController Presentor {
            get { return _presentor; }
            set { _presentor = value; }
        }
        #endregion

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

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

            // Set initial values
            NameField.StringValue = UserName;
            PasswordField.StringValue = Password;

            // Wireup events
            NameField.Changed += (sender, e) => {
                UserName = NameField.StringValue;
            };
            PasswordField.Changed += (sender, e) => {
                Password = PasswordField.StringValue;
            };
        }
        #endregion

        #region Private Methods
        private void CloseSheet() {
            Presentor.DismissViewController (this);
        }
        #endregion

        #region Custom Actions
        partial void AcceptSheet (Foundation.NSObject sender) {
            RaiseSheetAccepted();
            CloseSheet();
        }

        partial void CancelSheet (Foundation.NSObject sender) {
            RaiseSheetCanceled();
            CloseSheet();
        }
        #endregion

        #region Events
        public EventHandler SheetAccepted;

        internal void RaiseSheetAccepted() {
            if (this.SheetAccepted != null)
                this.SheetAccepted (this, EventArgs.Empty);
        }

        public EventHandler SheetCanceled;

        internal void RaiseSheetCanceled() {
            if (this.SheetCanceled != null)
                this.SheetCanceled (this, EventArgs.Empty);
        }
        #endregion
    }
}

Ensuite, modifiez le ViewController.cs fichier, modifiez la PrepareForSegue méthode et faites-la ressembler à ce qui suit :

public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
    base.PrepareForSegue (segue, sender);

    // Take action based on the segue name
    switch (segue.Identifier) {
    case "ModalSegue":
        var dialog = segue.DestinationController as CustomDialogController;
        dialog.DialogTitle = "MacDialog";
        dialog.DialogDescription = "This is a sample dialog.";
        dialog.DialogAccepted += (s, e) => {
            Console.WriteLine ("Dialog accepted");
            DismissViewController (dialog);
        };
        dialog.Presentor = this;
        break;
    case "SheetSegue":
        var sheet = segue.DestinationController as SheetViewController;
        sheet.SheetAccepted += (s, e) => {
            Console.WriteLine ("User Name: {0} Password: {1}", sheet.UserName, sheet.Password);
        };
        sheet.Presentor = this;
        break;
    }
}

Si nous exécutons notre application et ouvrez la feuille, elle sera attachée à la fenêtre :

Exemple de feuille

Création d’une boîte de dialogue Préférences

Avant de disposer de l’affichage des préférences dans le Générateur d’interface, nous devons ajouter un type de segue personnalisé pour gérer le basculement des préférences. Ajoutez une nouvelle classe à votre projet et appelez-la ReplaceViewSeque. Modifiez la classe et faites-la ressembler à ce qui suit :

using System;
using AppKit;
using Foundation;

namespace MacWindows
{
    [Register("ReplaceViewSeque")]
    public class ReplaceViewSeque : NSStoryboardSegue
    {
        #region Constructors
        public ReplaceViewSeque() {

        }

        public ReplaceViewSeque (string identifier, NSObject sourceController, NSObject destinationController) : base(identifier,sourceController,destinationController) {

        }

        public ReplaceViewSeque (IntPtr handle) : base(handle) {
        }

        public ReplaceViewSeque (NSObjectFlag x) : base(x) {
        }
        #endregion

        #region Override Methods
        public override void Perform ()
        {
            // Cast the source and destination controllers
            var source = SourceController as NSViewController;
            var destination = DestinationController as NSViewController;

            // Is there a source?
            if (source == null) {
                // No, get the current key window
                var window = NSApplication.SharedApplication.KeyWindow;

                // Swap the controllers
                window.ContentViewController = destination;

                // Release memory
                window.ContentViewController?.RemoveFromParentViewController ();
            } else {
                // Swap the controllers
                source.View.Window.ContentViewController = destination;

                // Release memory
                source.RemoveFromParentViewController ();
            }
        
        }
        #endregion

    }

}

Une fois le segue personnalisé créé, nous pouvons ajouter une nouvelle fenêtre dans le Générateur d’interface de Xcode pour gérer nos préférences.

Pour ajouter une nouvelle fenêtre, procédez comme suit :

  1. Dans le Explorateur de solutions, ouvrez le Main.storyboard fichier à modifier dans le Générateur d’interface de Xcode.

  2. Faites glisser un nouveau contrôleur de fenêtre dans l’aire de conception :

    Sélectionner un contrôleur de fenêtre dans la bibliothèque

  3. Organisez la fenêtre près du concepteur de barre de menus :

    Ajout de la nouvelle fenêtre

  4. Créez des copies du contrôleur d’affichage attaché, car il y aura des onglets dans votre vue de préférence :

    Ajout des contrôleurs d’affichage requis

  5. Faites glisser un nouveau contrôleur de barre d’outils à partir de la bibliothèque :

    Sélectionner un contrôleur de barre d’outils dans la bibliothèque

  6. Et déposez-le sur la fenêtre dans l’aire de conception :

    Ajout d’un nouveau contrôleur de barre d’outils

  7. Disposition de la conception de votre barre d’outils :

    Disposition de la barre d’outils

  8. Control-Click et faites glisser de chaque bouton de barre d’outils vers les vues que vous avez créées ci-dessus. Sélectionnez un type de segue personnalisé :

    Définition d’un type de segue personnalisé.

  9. Sélectionnez le nouveau Segue et définissez la classe sur ReplaceViewSegue:

    Définition de la classe segue

  10. Dans la barre de menus Designer de l’aire de conception, dans le menu Application, sélectionnez Préférences..., cliquez sur la fenêtre Préférences et faites glisser jusqu’à la fenêtre Préférences pour créer une fenêtre Afficher :

    Définition du type segue en faisant glisser Préférences vers la fenêtre Préférences.

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

Si nous exécutons le code et que nous sélectionnons préférences... dans le menu Application, la fenêtre s’affiche :

Exemple de fenêtre de préférences affichant le mot Profil.

Pour plus d’informations sur l’utilisation de Windows et de barres d’outils, consultez notre documentation windows et barres d’outils .

Préférences d’enregistrement et de chargement

Dans une application macOS classique, lorsque l’utilisateur apporte des modifications à l’une des préférences utilisateur de l’application, ces modifications sont enregistrées automatiquement. Le moyen le plus simple de gérer cela dans une application Xamarin.Mac consiste à créer une seule classe pour gérer toutes les préférences de l’utilisateur et les partager à l’échelle du système.

Tout d’abord, ajoutez une nouvelle AppPreferences classe au projet et héritez de NSObject. Les préférences seront conçues pour utiliser la liaison de données et le codage Key-Value , ce qui simplifiera considérablement le processus de création et de maintenance des formulaires de préférence. Étant donné que les préférences se composent d’une petite quantité de types de données simples, utilisez le intégré NSUserDefaults pour stocker et récupérer des valeurs.

Modifiez le AppPreferences.cs fichier et faites-le ressembler à ce qui suit :

using System;
using Foundation;
using AppKit;

namespace SourceWriter
{
    [Register("AppPreferences")]
    public class AppPreferences : NSObject
    {
        #region Computed Properties
        [Export("DefaultLanguage")]
        public int DefaultLanguage {
            get { 
                var value = LoadInt ("DefaultLanguage", 0);
                return value; 
            }
            set {
                WillChangeValue ("DefaultLanguage");
                SaveInt ("DefaultLanguage", value, true);
                DidChangeValue ("DefaultLanguage");
            }
        }

        [Export("SmartLinks")]
        public bool SmartLinks {
            get { return LoadBool ("SmartLinks", true); }
            set {
                WillChangeValue ("SmartLinks");
                SaveBool ("SmartLinks", value, true);
                DidChangeValue ("SmartLinks");
            }
        }

        // Define any other required user preferences in the same fashion
        ...

        [Export("EditorBackgroundColor")]
        public NSColor EditorBackgroundColor {
            get { return LoadColor("EditorBackgroundColor", NSColor.White); }
            set {
                WillChangeValue ("EditorBackgroundColor");
                SaveColor ("EditorBackgroundColor", value, true);
                DidChangeValue ("EditorBackgroundColor");
            }
        }
        #endregion

        #region Constructors
        public AppPreferences ()
        {
        }
        #endregion

        #region Public Methods
        public int LoadInt(string key, int defaultValue) {
            // Attempt to read int
            var number = NSUserDefaults.StandardUserDefaults.IntForKey(key);

            // Take action based on value
            if (number == null) {
                return defaultValue;
            } else {
                return (int)number;
            }
        }
            
        public void SaveInt(string key, int value, bool sync) {
            NSUserDefaults.StandardUserDefaults.SetInt(value, key);
            if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
        }

        public bool LoadBool(string key, bool defaultValue) {
            // Attempt to read int
            var value = NSUserDefaults.StandardUserDefaults.BoolForKey(key);

            // Take action based on value
            if (value == null) {
                return defaultValue;
            } else {
                return value;
            }
        }

        public void SaveBool(string key, bool value, bool sync) {
            NSUserDefaults.StandardUserDefaults.SetBool(value, key);
            if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
        }

        public string NSColorToHexString(NSColor color, bool withAlpha) {
            //Break color into pieces
            nfloat red=0, green=0, blue=0, alpha=0;
            color.GetRgba (out red, out green, out blue, out alpha);

            // Adjust to byte
            alpha *= 255;
            red *= 255;
            green *= 255;
            blue *= 255;

            //With the alpha value?
            if (withAlpha) {
                return String.Format ("#{0:X2}{1:X2}{2:X2}{3:X2}", (int)alpha, (int)red, (int)green, (int)blue);
            } else {
                return String.Format ("#{0:X2}{1:X2}{2:X2}", (int)red, (int)green, (int)blue);
            }
        }

        public NSColor NSColorFromHexString (string hexValue)
        {
            var colorString = hexValue.Replace ("#", "");
            float red, green, blue, alpha;

            // Convert color based on length
            switch (colorString.Length) {
            case 3 : // #RGB
                red = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(0, 1)), 16) / 255f;
                green = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(1, 1)), 16) / 255f;
                blue = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(2, 1)), 16) / 255f;
                return NSColor.FromRgba(red, green, blue, 1.0f);
            case 6 : // #RRGGBB
                red = Convert.ToInt32(colorString.Substring(0, 2), 16) / 255f;
                green = Convert.ToInt32(colorString.Substring(2, 2), 16) / 255f;
                blue = Convert.ToInt32(colorString.Substring(4, 2), 16) / 255f;
                return NSColor.FromRgba(red, green, blue, 1.0f);
            case 8 : // #AARRGGBB
                alpha = Convert.ToInt32(colorString.Substring(0, 2), 16) / 255f;
                red = Convert.ToInt32(colorString.Substring(2, 2), 16) / 255f;
                green = Convert.ToInt32(colorString.Substring(4, 2), 16) / 255f;
                blue = Convert.ToInt32(colorString.Substring(6, 2), 16) / 255f;
                return NSColor.FromRgba(red, green, blue, alpha);
            default :
                throw new ArgumentOutOfRangeException(string.Format("Invalid color value '{0}'. It should be a hex value of the form #RBG, #RRGGBB or #AARRGGBB", hexValue));
            }
        }

        public NSColor LoadColor(string key, NSColor defaultValue) {

            // Attempt to read color
            var hex = NSUserDefaults.StandardUserDefaults.StringForKey(key);

            // Take action based on value
            if (hex == null) {
                return defaultValue;
            } else {
                return NSColorFromHexString (hex);
            }
        }

        public void SaveColor(string key, NSColor color, bool sync) {
            // Save to default
            NSUserDefaults.StandardUserDefaults.SetString(NSColorToHexString(color,true), key);
            if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
        }
        #endregion
    }
}

Cette classe contient quelques routines d’assistance telles que SaveInt, , SaveColorLoadInt, LoadColor, etc. pour faciliter l’utilisationNSUserDefaults. En outre, étant donné NSUserDefaults que n’a pas de méthode intégrée pour gérer NSColors, les NSColorToHexString méthodes et NSColorFromHexString sont utilisées pour convertir des couleurs en chaînes hexadécimale basées sur le web (#RRGGBBAAAA est la transparence alpha) qui peuvent être facilement stockées et récupérées.

Dans le AppDelegate.cs fichier, créez un instance de l’objet AppPreferences qui sera utilisé à l’échelle de l’application :

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

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

        public AppPreferences Preferences { get; set; } = new AppPreferences();
        #endregion

        #region Constructors
        public AppDelegate ()
        {
        }
        #endregion
        
        ...

Câblage des préférences vers les vues de préférence

Ensuite, connectez la classe Préférence aux éléments de l’interface utilisateur dans la fenêtre Préférences et vues créées ci-dessus. Dans Le Générateur d’interface, sélectionnez un contrôleur d’affichage des préférences et basculez vers l’inspecteur d’identité, créez une classe personnalisée pour le contrôleur :

Inspecteur d’identité

Revenez à Visual Studio pour Mac pour synchroniser vos modifications et ouvrir la classe nouvellement créée pour modification. Faites en sorte que la classe ressemble à ce qui suit :

using System;
using Foundation;
using AppKit;

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

        #region Computed Properties
        [Export("Preferences")]
        public AppPreferences Preferences {
            get { return App.Preferences; }
        }
        #endregion

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

Notez que cette classe a fait deux choses ici : tout d’abord, il existe une propriété d’assistance App pour faciliter l’accès à AppDelegate . Deuxièmement, la Preferences propriété expose la classe AppPreferences globale pour la liaison de données avec tous les contrôles d’interface utilisateur placés sur cette vue.

Ensuite, double-cliquez sur le fichier Storyboard pour le rouvrir dans le Générateur d’interface (et voir les modifications qui viennent d’être apportées ci-dessus). Faites glisser tous les contrôles d’interface utilisateur nécessaires pour générer l’interface des préférences dans l’affichage. Pour chaque contrôle, basculez vers l’inspecteur de liaison et liez aux propriétés individuelles de la classe AppPreference :

Inspecteur de liaison

Répétez les étapes ci-dessus pour tous les panneaux (Afficher les contrôleurs) et les propriétés de préférence requises.

Application de modifications de préférence à toutes les fenêtres ouvertes

Comme indiqué ci-dessus, dans une application macOS classique, lorsque l’utilisateur apporte des modifications à l’une des préférences utilisateur de l’application, ces modifications sont enregistrées automatiquement et appliquées à toutes les fenêtres que l’utilisateur peut avoir ouvertes dans l’application.

Une planification et une conception minutieuses des préférences et des fenêtres de votre application permettront à ce processus de se dérouler de manière fluide et transparente pour l’utilisateur final, avec un minimum de travail de codage.

Pour toute fenêtre qui consommera les préférences d’application, ajoutez la propriété d’assistance suivante à son contrôleur d’affichage de contenu pour faciliter l’accès à notre AppDelegate :

#region Application Access
public static AppDelegate App {
    get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion

Ensuite, ajoutez une classe pour configurer le contenu ou le comportement en fonction des préférences de l’utilisateur :

public void ConfigureEditor() {

    // General Preferences
    TextEditor.AutomaticLinkDetectionEnabled = App.Preferences.SmartLinks;
    TextEditor.AutomaticQuoteSubstitutionEnabled = App.Preferences.SmartQuotes;
    ...

}

Vous devez appeler la méthode de configuration lorsque la fenêtre est ouverte pour la première fois pour vous assurer qu’elle est conforme aux préférences de l’utilisateur :

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

    // Configure editor from user preferences
    ConfigureEditor ();
    ...
}

Ensuite, modifiez le AppDelegate.cs fichier et ajoutez la méthode suivante pour appliquer les modifications de préférence à toutes les fenêtres ouvertes :

public void UpdateWindowPreferences() {

    // Process all open windows
    for(int n=0; n<NSApplication.SharedApplication.Windows.Length; ++n) {
        var content = NSApplication.SharedApplication.Windows[n].ContentViewController as ViewController;
        if (content != null ) {
            // Reformat all text
            content.ConfigureEditor ();
        }
    }

}

Ensuite, ajoutez une PreferenceWindowDelegate classe au projet et faites en sorte qu’elle ressemble à ce qui suit :

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

namespace SourceWriter
{
    public class PreferenceWindowDelegate : NSWindowDelegate
    {
        #region Application Access
        public static AppDelegate App {
            get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
        }
        #endregion

        #region Computed Properties
        public NSWindow Window { get; set;}
        #endregion

        #region constructors
        public PreferenceWindowDelegate (NSWindow window)
        {
            // Initialize
            this.Window = window;

        }
        #endregion

        #region Override Methods
        public override bool WindowShouldClose (Foundation.NSObject sender)
        {
            
            // Apply any changes to open windows
            App.UpdateWindowPreferences();

            return true;
        }
        #endregion
    }
}

Cela entraîne l’envoi de toutes les modifications de préférence à tous les windows ouverts lorsque la fenêtre de préférence se ferme.

Enfin, modifiez le contrôleur de fenêtre de préférence et ajoutez le délégué créé ci-dessus :

using System;
using Foundation;
using AppKit;

namespace SourceWriter
{
    public partial class PreferenceWindowController : NSWindowController
    {
        #region Constructors
        public PreferenceWindowController (IntPtr handle) : base (handle)
        {
        }
        #endregion

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

            // Initialize
            Window.Delegate = new PreferenceWindowDelegate(Window);
            Toolbar.SelectedItemIdentifier = "General";
        }
        #endregion
    }
}

Une fois toutes ces modifications en place, si l’utilisateur modifie les préférences de l’application et ferme la fenêtre Préférences, les modifications sont appliquées à tous les windows ouverts :

Exemple de fenêtre Préférences, affichée avec plusieurs autres fenêtres ouvertes.

Boîte de dialogue Ouvrir

La boîte de dialogue Ouvrir offre aux utilisateurs un moyen cohérent de rechercher et d’ouvrir un élément dans une application. Pour afficher une boîte de dialogue Ouvrir dans une application Xamarin.Mac, utilisez le code suivant :

var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = true;
dlg.CanChooseDirectories = false;
dlg.AllowedFileTypes = new string[] { "txt", "html", "md", "css" };

if (dlg.RunModal () == 1) {
    // Nab the first file
    var url = dlg.Urls [0];

    if (url != null) {
        var path = url.Path;

        // Create a new window to hold the text
        var newWindowController = new MainWindowController ();
        newWindowController.Window.MakeKeyAndOrderFront (this);

        // Load the text into the window
        var window = newWindowController.Window as MainWindow;
        window.Text = File.ReadAllText(path);
        window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
        window.RepresentedUrl = url;

    }
}

Dans le code ci-dessus, nous ouvrons une nouvelle fenêtre de document pour afficher le contenu du fichier. Vous devez remplacer ce code par des fonctionnalités requises par votre application.

Les propriétés suivantes sont disponibles lors de l’utilisation d’un NSOpenPanel:

  • CanChooseFiles : si true l’utilisateur peut sélectionner des fichiers.
  • CanChooseDirectories : si true l’utilisateur peut sélectionner des répertoires.
  • PermetmultipleSelection : si true l’utilisateur peut sélectionner plusieurs fichiers à la fois.
  • ResolveAliases : en true cas de sélection et d’alias, le résout en chemin d’accès du fichier d’origine.
  • AllowedFileTypes : tableau de chaînes de types de fichiers que l’utilisateur peut sélectionner en tant qu’extension ou UTI. La valeur par défaut est null, ce qui permet d’ouvrir n’importe quel fichier.

La RunModal () méthode affiche la boîte de dialogue Ouvrir et permet à l’utilisateur de sélectionner des fichiers ou des répertoires (comme spécifié par les propriétés) et retourne 1 si l’utilisateur clique sur le bouton Ouvrir .

La boîte de dialogue Ouvrir retourne les fichiers ou répertoires sélectionnés de l’utilisateur sous la forme d’un tableau d’URL dans la URL propriété .

Si nous exécutons le programme et que nous sélectionnons l’élément Ouvrir... dans le menu Fichier , les éléments suivants s’affichent :

Boîte de dialogue ouverte

Boîtes de dialogue Imprimer et mise en page

macOS fournit des boîtes de dialogue d’impression et de mise en page standard que votre application peut afficher afin que les utilisateurs puissent avoir une expérience d’impression cohérente dans chaque application qu’ils utilisent.

Le code suivant affiche la boîte de dialogue Imprimer standard :

public bool ShowPrintAsSheet { get; set;} = true;
...

[Export ("showPrinter:")]
void ShowDocument (NSObject sender) {
    var dlg = new NSPrintPanel();

    // Display the print dialog as dialog box
    if (ShowPrintAsSheet) {
        dlg.BeginSheet(new NSPrintInfo(),this,this,null,new IntPtr());
    } else {
        if (dlg.RunModalWithPrintInfo(new NSPrintInfo()) == 1) {
            var alert = new NSAlert () {
                AlertStyle = NSAlertStyle.Critical,
                InformativeText = "We need to print the document here...",
                MessageText = "Print Document",
            };
            alert.RunModal ();
        }
    }
}

Si nous définissons la ShowPrintAsSheet propriété sur false, exécutez l’application et affichez la boîte de dialogue d’impression, les éléments suivants s’affichent :

Boîte de dialogue Imprimer

Si définissez la ShowPrintAsSheet propriété sur true, exécutez l’application et affichez la boîte de dialogue d’impression, les éléments suivants s’affichent :

Feuille d’impression

Le code suivant affiche la boîte de dialogue Mise en page :

[Export ("showLayout:")]
void ShowLayout (NSObject sender) {
    var dlg = new NSPageLayout();

    // Display the print dialog as dialog box
    if (ShowPrintAsSheet) {
        dlg.BeginSheet (new NSPrintInfo (), this);
    } else {
        if (dlg.RunModal () == 1) {
            var alert = new NSAlert () {
                AlertStyle = NSAlertStyle.Critical,
                InformativeText = "We need to print the document here...",
                MessageText = "Print Document",
            };
            alert.RunModal ();
        }
    }
}

Si nous définissons la propriété sur ShowPrintAsSheetfalse, exécutez l’application et affichez la boîte de dialogue de disposition d’impression, les éléments suivants s’affichent :

Boîte de dialogue de configuration de page

Si définissez la ShowPrintAsSheet propriété sur true, exécutez l’application et affichez la boîte de dialogue de disposition d’impression, les éléments suivants s’affichent :

Feuille de configuration de page

Pour plus d’informations sur l’utilisation des boîtes de dialogue d’impression et de configuration de page, consultez la documentation NSPrintPanel et NSPageLayout d’Apple.

Boîte de dialogue Enregistrer

La boîte de dialogue Enregistrer offre aux utilisateurs un moyen cohérent d’enregistrer un élément dans une application.

Le code suivant affiche la boîte de dialogue d’enregistrement standard :

public bool ShowSaveAsSheet { get; set;} = true;
...

[Export("saveDocumentAs:")]
void ShowSaveAs (NSObject sender)
{
    var dlg = new NSSavePanel ();
    dlg.Title = "Save Text File";
    dlg.AllowedFileTypes = new string[] { "txt", "html", "md", "css" };

    if (ShowSaveAsSheet) {
        dlg.BeginSheet(mainWindowController.Window,(result) => {
            var alert = new NSAlert () {
                AlertStyle = NSAlertStyle.Critical,
                InformativeText = "We need to save the document here...",
                MessageText = "Save Document",
            };
            alert.RunModal ();
        });
    } else {
        if (dlg.RunModal () == 1) {
            var alert = new NSAlert () {
                AlertStyle = NSAlertStyle.Critical,
                InformativeText = "We need to save the document here...",
                MessageText = "Save Document",
            };
            alert.RunModal ();
        }
    }

}

La AllowedFileTypes propriété est un tableau de chaînes de types de fichiers que l’utilisateur peut sélectionner pour enregistrer le fichier. Le type de fichier peut être spécifié sous forme d’extension ou d’UTI. La valeur par défaut est null, ce qui permet d’utiliser n’importe quel type de fichier.

Si nous définissons la ShowSaveAsSheet propriété falsesur , exécutez l’application et sélectionnez Enregistrer sous... dans le menu Fichier , les éléments suivants s’affichent :

Boîte de dialogue Enregistrer

L’utilisateur peut développer la boîte de dialogue :

Boîte de dialogue d’enregistrement développée

Si nous définissons la ShowSaveAsSheet propriété truesur , exécutez l’application et sélectionnez Enregistrer sous... dans le menu Fichier , les éléments suivants s’affichent :

Feuille d’enregistrement

L’utilisateur peut développer la boîte de dialogue :

Feuille d’enregistrement développée

Pour plus d’informations sur l’utilisation de la boîte de dialogue Enregistrer, consultez la documentation NSSavePanel d’Apple .

Résumé

Cet article a examiné en détail l’utilisation des fenêtres modales, des feuilles et des boîtes de dialogue système standard dans une application Xamarin.Mac. Nous avons vu les différents types et utilisations des fenêtres modales, des feuilles et des boîtes de dialogue, comment créer et gérer des fenêtres modales et des feuilles dans le Générateur d’interface de Xcode et comment utiliser les fenêtres modales, les feuilles et les dialogues dans le code C#.