Dialoghi in Xamarin.Mac

Quando si lavora con C# e .NET in un'applicazione Xamarin.Mac, è possibile accedere alle stesse finestre di dialogo e windows modali che uno sviluppatore lavora in Objective-C e Xcode . Poiché Xamarin.Mac si integra direttamente con Xcode, è possibile usare Interface Builder di Xcode per creare e gestire Windows modale (o, facoltativamente, crearli direttamente nel codice C#).

Viene visualizzata una finestra di dialogo in risposta a un'azione dell'utente e in genere fornisce i modi in cui gli utenti possono completare l'azione. Una finestra di dialogo richiede una risposta dell'utente prima che possa essere chiusa.

Windows può essere usato in uno stato modeless (ad esempio un editor di testo che può avere più documenti aperti contemporaneamente) o modale (ad esempio una finestra di dialogo di esportazione che deve essere ignorata prima che l'applicazione possa continuare).

An open dialog box

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

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

Introduzione ai dialoghi

Viene visualizzata una finestra di dialogo in risposta a un'azione dell'utente (ad esempio il salvataggio di un file) e consente agli utenti di completare tale azione. Una finestra di dialogo richiede una risposta dell'utente prima che possa essere chiusa.

Secondo Apple, esistono tre modi per presentare un dialogo:

  • Modal documento: una finestra di dialogo Modale documento impedisce all'utente di eseguire qualsiasi altra operazione all'interno di un documento specificato fino a quando non viene chiusa.
  • Modal dell'app: una finestra di dialogo Modale dell'app impedisce all'utente di interagire con l'applicazione fino a quando non viene chiusa.
  • Un dialogo senza modalità consente agli utenti di modificare le impostazioni nella finestra di dialogo mentre interagisce ancora con la finestra del documento.

Qualsiasi standard NSWindow può essere usato come finestra di dialogo personalizzata visualizzandola in modo modally:

An example modal window

Finestre di dialogo modali documento

Un foglio è una finestra di dialogo modale collegata a una determinata finestra di documento, impedendo agli utenti di interagire con la finestra fino a quando non chiude la finestra di dialogo. Un foglio è collegato alla finestra da cui emerge e un solo foglio può essere aperto per una finestra in qualsiasi momento.

An example modal sheet

Preferenze di Windows

Una finestra Preferenze è una finestra di dialogo senza modalità che contiene le impostazioni dell'applicazione che l'utente cambia raramente. Le preferenze di Windows includono spesso una barra degli strumenti che consente all'utente di passare da un gruppo di impostazioni all'altro:

An example preference window

Apri finestra di dialogo

La finestra di dialogo Apri offre agli utenti un modo coerente per trovare e aprire un elemento in un'applicazione:

A open dialog box

macOS offre finestre di dialogo standard di installazione di stampa e pagina che l'applicazione può visualizzare in modo che gli utenti possano avere un'esperienza di stampa coerente in ogni applicazione usata.

È possibile visualizzare la finestra di dialogo Stampa come finestra di dialogo a virgola mobile libera:

A print dialog box

Oppure può essere visualizzato come foglio:

A print sheet

La finestra di dialogo Imposta pagina può essere visualizzata come finestra di dialogo mobile libera:

A page setup dialog

Oppure può essere visualizzato come foglio:

A page setup sheet

Salva finestre di dialogo

La finestra di dialogo Salva offre agli utenti un modo coerente per salvare un elemento in un'applicazione. La finestra di dialogo salva ha due stati: Minimo (noto anche come Compresso):

A save dialog

E lo stato espanso :

An expanded save dialog

La finestra di dialogo di salvataggio minimo può essere visualizzata anche come foglio:

A minimal save sheet

Come può la finestra di dialogo Di salvataggio espanso :

An expanded save sheet

Per altre informazioni, vedere la sezione Dialogs di Apple OS X Human Interface Guidelines

Aggiunta di una finestra modale a un progetto

A parte la finestra del documento principale, potrebbe essere necessario che un'applicazione Xamarin.Mac visualizzi altri tipi di finestre all'utente, ad esempio Preferenze o Pannelli di controllo.

Per aggiungere una nuova finestra, eseguire le operazioni seguenti:

  1. Nella Esplora soluzioni aprire il file per la Main.storyboard modifica in Interface Builder di Xcode.

  2. Trascinare un nuovo controller di visualizzazione nell'area di progettazione:

    Selecting a View Controller from the Library

  3. In Identity Inspector immettere CustomDialogController per Nome classe:

    Setting the class name to CustomDialogController.

  4. Tornare a Visual Studio per Mac, consentire la sincronizzazione con Xcode e creare il CustomDialogController.h file.

  5. Tornare a Xcode e progettare l'interfaccia:

    Designing the UI in Xcode

  6. Creare un oggetto Modal Segue dalla finestra principale dell'app al nuovo controller di visualizzazione trascinando il controllo dall'elemento dell'interfaccia utente che aprirà la finestra di dialogo alla finestra del dialogo. Assegnare l'identificatoreModalSegue:

    A modal segue

  7. Collegare eventuali azioni e punti vendita:

    Configuring an Action

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

Fare in modo che il CustomDialogController.cs file sia simile al seguente:

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

Questo codice espone alcune proprietà per impostare il titolo e la descrizione del dialogo e alcuni eventi per reagire alla finestra di dialogo annullata o accettata.

Modificare quindi il file, eseguire l'override ViewController.cs del PrepareForSegue metodo e renderlo simile al seguente:

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

Questo codice inizializza il codice seguente definito in Interface Builder di Xcode nella finestra di dialogo e configura il titolo e la descrizione. Gestisce anche la scelta effettuata dall'utente nella finestra di dialogo.

È possibile eseguire l'applicazione e visualizzare la finestra di dialogo personalizzata:

An example dialog

Per altre informazioni sull'uso di windows in un'applicazione Xamarin.Mac, vedere la documentazione relativa all'uso di Windows .

Creazione di un foglio personalizzato

Un foglio è una finestra di dialogo modale collegata a una determinata finestra di documento, impedendo agli utenti di interagire con la finestra fino a quando non chiude la finestra di dialogo. Un foglio è collegato alla finestra da cui emerge e un solo foglio può essere aperto per una finestra in qualsiasi momento.

Per creare un foglio personalizzato in Xamarin.Mac, eseguire le operazioni seguenti:

  1. Nella Esplora soluzioni aprire il file per la Main.storyboard modifica in Interface Builder di Xcode.

  2. Trascinare un nuovo controller di visualizzazione nell'area di progettazione:

    Selecting a View Controller from the Library

  3. Progettare l'interfaccia utente:

    The UI design

  4. Creare un foglio Segue dalla finestra principale al nuovo controller di visualizzazione:

    Selecting the Sheet segue type

  5. In Identity Inspector denominare la classeSheetViewController del controller di visualizzazione:

    Setting the class name to SheetViewController.

  6. Definire gli outlet e le azioni necessari:

    Defining the required Outlets and Actions

  7. Salvare le modifiche e tornare a Visual Studio per Mac da sincronizzare.

Modificare quindi il SheetViewController.cs file e renderlo simile al seguente:

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

Modificare quindi il ViewController.cs file, modificare il PrepareForSegue metodo e renderlo simile al seguente:

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

Se si esegue l'applicazione e si apre il foglio, verrà collegato alla finestra:

An example sheet

Creazione di una finestra di dialogo Preferenze

Prima di definire la visualizzazione preferenza in Interface Builder, è necessario aggiungere un tipo segue personalizzato per gestire la disattivazione delle preferenze. Aggiungere una nuova classe al progetto e chiamarla ReplaceViewSeque. Modificare la classe e renderla simile alla seguente:

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

    }

}

Con la procedura personalizzata creata, è possibile aggiungere una nuova finestra in Interface Builder di Xcode per gestire le preferenze.

Per aggiungere una nuova finestra, eseguire le operazioni seguenti:

  1. Nella Esplora soluzioni aprire il file per la Main.storyboard modifica in Interface Builder di Xcode.

  2. Trascinare un nuovo controller finestra nell'area di progettazione:

    Select a Window Controller from the Library

  3. Disporre la finestra accanto alla finestra di progettazione della barra dei menu:

    Adding the new Window

  4. Creare copie del controller di visualizzazione collegato in quanto saranno presenti schede nella visualizzazione delle preferenze:

    Adding the required View Controllers

  5. Trascinare un nuovo controller della barra degli strumenti dalla libreria:

    Select a Toolbar Controller from the Library

  6. E rilasciarlo nella finestra nell'area di progettazione:

    Adding a new Toolbar Controller

  7. Layout della struttura della barra degli strumenti:

    Layout the toolbar

  8. Ctrl- Fare clic e trascinare da ogni pulsante della barra degli strumenti alle visualizzazioni create in precedenza. Selezionare un tipo di segue personalizzato :

    Setting a Custom segue type.

  9. Selezionare il nuovo Segue e impostare Classe su ReplaceViewSegue:

    Setting the segue class

  10. Nell'area di progettazione della barra dei menu selezionare Preferenze dal menu Applicazione, fare clic e trascinare nella finestra Preferenze per creare una finestra Mostra segue:

    Setting the segue type by dragging Preferences to the Preferences Window.

  11. Salvare le modifiche e tornare a Visual Studio per Mac da sincronizzare.

Se si esegue il codice e si seleziona Preferenze dalmenu Applicazione, verrà visualizzata la finestra:

An example preferences window displaying the word Profile.

Per altre informazioni sull'uso di Windows e barre degli strumenti, vedere la documentazione di Windows e Barre degli strumenti .

Salvataggio e caricamento delle preferenze

In un'app macOS tipica, quando l'utente apporta modifiche a una delle preferenze utente dell'app, tali modifiche vengono salvate automaticamente. Il modo più semplice per gestirlo in un'app Xamarin.Mac consiste nel creare una singola classe per gestire tutte le preferenze dell'utente e condividerla a livello di sistema.

Aggiungere prima di tutto una nuova AppPreferences classe al progetto ed ereditare da NSObject. Le preferenze saranno progettate per l'uso del data binding e della codifica chiave-valore che renderanno molto più semplice il processo di creazione e gestione delle preferenze. Poiché le preferenze sono costituite da una piccola quantità di tipi di dati semplici, usare l'predefinito per NSUserDefaults archiviare e recuperare i valori.

Modificare il AppPreferences.cs file e renderlo simile al seguente:

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

Questa classe contiene alcune routine helper, ad SaveIntesempio , , LoadIntSaveColor, LoadColore così via, per semplificare l'usoNSUserDefaults. Inoltre, poiché NSUserDefaults non dispone di un modo predefinito per gestire NSColors, i NSColorToHexString metodi e NSColorFromHexString vengono usati per convertire i colori in stringhe esadecimali basate sul Web (#RRGGBBAA dove AA è la trasparenza alfa) che possono essere facilmente archiviate e recuperate.

AppDelegate.cs Nel file creare un'istanza dell'oggetto AppPreferences che verrà usato a livello di app:

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

Preferenze di collegamento alle visualizzazioni preferenza

Connettere quindi la classe Preferenza agli elementi dell'interfaccia utente nella finestra preferenza e nelle visualizzazioni create in precedenza. In Interface Builder selezionare un controller di visualizzazione preferenza e passare a Identity Inspector, creare una classe personalizzata per il controller:

The Identity Inspector

Tornare a Visual Studio per Mac per sincronizzare le modifiche e aprire la classe appena creata per la modifica. Fare in modo che la classe sia simile alla seguente:

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

Si noti che questa classe ha eseguito due operazioni: Prima di tutto, è disponibile una proprietà helper App per semplificare l'accesso ad AppDelegate . In secondo luogo, la Preferences proprietà espone la classe appPreferences globale per il data binding con tutti i controlli dell'interfaccia utente posizionati in questa visualizzazione.

Fare quindi doppio clic sul file Storyboard per riaprirlo in Interface Builder (e vedere le modifiche appena apportate in precedenza). Trascinare tutti i controlli dell'interfaccia utente necessari per compilare l'interfaccia delle preferenze nella visualizzazione. Per ogni controllo, passare a Binding Inspector e associarsi alle singole proprietà della classe AppPreference :

The Binding Inspector

Ripetere i passaggi precedenti per tutti i pannelli (Controller di visualizzazione) e le proprietà delle preferenze necessari.

Applicazione di modifiche alle preferenze a tutte le finestre aperte

Come indicato in precedenza, in una tipica app macOS, quando l'utente apporta modifiche a una delle preferenze utente dell'app, tali modifiche vengono salvate automaticamente e applicate a qualsiasi finestra che l'utente potrebbe avere aperto nell'applicazione.

Un'attenta pianificazione e progettazione delle preferenze e delle finestre della tua app consentirà di eseguire questo processo in modo uniforme e trasparente all'utente finale, con una quantità minima di lavoro di codifica.

Per qualsiasi finestra che utilizza preferenze dell'app, aggiungere la proprietà helper seguente al controller visualizzazione contenuto per semplificare l'accesso ad AppDelegate :

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

Aggiungere quindi una classe per configurare il contenuto o il comportamento in base alle preferenze dell'utente:

public void ConfigureEditor() {

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

}

È necessario chiamare il metodo di configurazione quando la finestra viene aperta per la prima volta per assicurarsi che sia conforme alle preferenze dell'utente:

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

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

Modificare quindi il AppDelegate.cs file e aggiungere il metodo seguente per applicare le modifiche alle preferenze a tutte le finestre aperte:

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

}

Aggiungere quindi una PreferenceWindowDelegate classe al progetto e renderla simile alla seguente:

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

In questo modo tutte le modifiche alle preferenze verranno inviate a tutte le finestre aperte alla chiusura della finestra di preferenza.

Modificare infine il controller finestra delle preferenze e aggiungere il delegato creato in precedenza:

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

Con tutte queste modifiche apportate, se l'utente modifica le preferenze dell'app e chiude la finestra delle preferenze, le modifiche verranno applicate a tutte le finestre aperte:

An example Preferences Window, displayed with several other open windows.

Finestra di dialogo Apri

La finestra di dialogo Apri offre agli utenti un modo coerente per trovare e aprire un elemento in un'applicazione. Per visualizzare una finestra di dialogo aperta in un'applicazione Xamarin.Mac, usare il codice seguente:

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;

    }
}

Nel codice precedente si apre una nuova finestra del documento per visualizzare il contenuto del file. È necessario sostituire questo codice con la funzionalità richiesta dall'applicazione.

Quando si utilizza un oggetto NSOpenPanel, sono disponibili le proprietà seguenti:

  • CanChooseFiles : se true l'utente può selezionare i file.
  • CanChooseDirectories : se true l'utente può selezionare le directory.
  • AllowsMultipleSelection : se true l'utente può selezionare più file alla volta.
  • ResolveAliases : se true si seleziona e alias, lo risolve nel percorso del file originale.
  • AllowedFileTypes : matrice di stringhe di tipi di file che l'utente può selezionare come estensione o UTI. Il valore predefinito è null, che consente l'apertura di qualsiasi file.

Il RunModal () metodo visualizza la finestra di dialogo Apri e consente all'utente di selezionare file o directory (come specificato dalle proprietà) e restituisce 1 se l'utente fa clic sul pulsante Apri .

La finestra di dialogo Apri restituisce i file o le directory selezionati dell'utente come matrice di URL nella URL proprietà .

Se si esegue il programma e si seleziona la voce Apri dalmenu File , viene visualizzato quanto segue:

An open dialog box

Finestre di dialogo Imposta stampa e pagina

macOS offre finestre di dialogo standard di installazione di stampa e pagina che l'applicazione può visualizzare in modo che gli utenti possano avere un'esperienza di stampa coerente in ogni applicazione usata.

Il codice seguente mostrerà la finestra di dialogo di stampa 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 ();
        }
    }
}

Se si imposta la ShowPrintAsSheet proprietà su false, eseguire l'applicazione e visualizzare la finestra di dialogo di stampa, verrà visualizzato quanto segue:

A print dialog box

Se si imposta la ShowPrintAsSheet proprietà su true, eseguire l'applicazione e visualizzare la finestra di dialogo di stampa, verrà visualizzato quanto segue:

A print sheet

Il codice seguente visualizzerà la finestra di dialogo Layout pagina:

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

Se si imposta la ShowPrintAsSheet proprietà su false, eseguire l'applicazione e visualizzare la finestra di dialogo layout di stampa, verrà visualizzato quanto segue:

A page setup dialog

Se si imposta la ShowPrintAsSheet proprietà su true, eseguire l'applicazione e visualizzare la finestra di dialogo layout di stampa, verrà visualizzato quanto segue:

A page setup sheet

Per altre informazioni sull'uso delle finestre di dialogo Di installazione di stampa e pagina, vedere la documentazione NSPrintPanel e NSPageLayout di Apple.

Finestra di dialogo Salva

La finestra di dialogo Salva offre agli utenti un modo coerente per salvare un elemento in un'applicazione.

Il codice seguente mostrerà la finestra di dialogo di salvataggio 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 proprietà è una matrice di stringhe di tipi di file che l'utente può selezionare per salvare il file con nome. Il tipo di file può essere specificato come estensione o UTI. Il valore predefinito è null, che consente l'uso di qualsiasi tipo di file.

Se si imposta la ShowSaveAsSheet proprietà su false, eseguire l'applicazione e selezionare Salva con nome dal menu File , verrà visualizzato quanto segue:

A save dialog box

L'utente può espandere la finestra di dialogo:

An expanded save dialog box

Se si imposta la ShowSaveAsSheet proprietà su true, eseguire l'applicazione e selezionare Salva con nome dal menu File , verrà visualizzato quanto segue:

A save sheet

L'utente può espandere la finestra di dialogo:

An expanded save sheet

Per altre informazioni sull'uso della finestra di dialogo salva, vedere la documentazione di NSSavePanel di Apple.

Riepilogo

Questo articolo ha esaminato in dettaglio l'uso di finestre modali, fogli e finestre di dialogo di sistema standard in un'applicazione Xamarin.Mac. Sono stati illustrati i diversi tipi e usi di finestre modali, fogli e finestre di dialogo, come creare e gestire finestre e fogli modali nel generatore di interfacce di Xcode e come usare finestre modali, fogli e dialoghi nel codice C#.