Compartilhar via


Caixas de diálogo no Xamarin.Mac

Ao trabalhar com C# e .NET em um aplicativo Xamarin.Mac, você tem acesso às mesmas Caixas de Diálogo e Janelas Modais que um desenvolvedor que trabalha e Objective-C o Xcode tem. Como o Xamarin.Mac se integra diretamente ao Xcode, você pode usar o Construtor de Interfaces do Xcode para criar e manter suas Janelas Modais (ou, opcionalmente, criá-las diretamente no código C#).

Uma caixa de diálogo é exibida em resposta a uma ação do usuário e normalmente fornece maneiras pelas quais os usuários podem concluir a ação. Uma caixa de diálogo requer uma resposta do usuário antes de ser fechada.

O Windows pode ser usado em um estado sem janela restrita (como um editor de texto que pode ter vários documentos abertos ao mesmo tempo) ou Modal (como uma caixa de diálogo Exportar que deve ser descartada antes que o aplicativo possa continuar).

Uma caixa de diálogo aberta

Neste artigo, abordaremos as noções básicas de como trabalhar com Caixas de Diálogo e Janelas Modais em um aplicativo Xamarin.Mac. É altamente recomendável que você trabalhe primeiro no artigo Olá, Mac , especificamente nas seções Introdução ao Xcode e ao Construtor de Interfaces e Saídas e Ações , pois ele aborda os principais conceitos e técnicas que usaremos neste artigo.

Talvez você queira dar uma olhada na seção Expondo classes/métodos C# para Objective-Cdo documento Internos do Xamarin.Mac também, ele explica os Register comandos e Export usados para conectar suas classes C# a Objective-C objetos e elementos de interface do usuário.

Introdução às caixas de diálogo

Uma caixa de diálogo é exibida em resposta a uma ação do usuário (como salvar um arquivo) e fornece uma maneira de os usuários concluírem essa ação. Uma caixa de diálogo requer uma resposta do usuário antes de ser fechada.

De acordo com a Apple, existem três maneiras de apresentar um diálogo:

  • Modal do documento - Uma caixa de diálogo Modal do documento impede que o usuário faça qualquer outra coisa em um determinado documento até que ele seja descartado.
  • Modal do aplicativo - Uma caixa de diálogo Modal do aplicativo impede que o usuário interaja com o aplicativo até que ele seja descartado.
  • Sem janela restrita Uma caixa de diálogo sem janela restrita permite que os usuários alterem as configurações na caixa de diálogo enquanto ainda interagem com a janela do documento.

Qualquer padrão NSWindow pode ser usado como uma caixa de diálogo personalizada, exibindo-o modalmente:

Um exemplo de janela modal

Folhas de diálogo modais do documento

Uma planilha é uma caixa de diálogo modal anexada a uma determinada janela de documento, impedindo que os usuários interajam com a janela até que ignorem a caixa de diálogo. Uma planilha é anexada à janela da qual ela emerge e somente uma planilha pode ser aberta para uma janela a qualquer momento.

Um exemplo de folha modal

Preferências Windows

Uma janela de preferências é uma caixa de diálogo sem janela restrita que contém as configurações do aplicativo que o usuário altera com pouca frequência. Preferências As janelas geralmente incluem uma barra de ferramentas que permite ao usuário alternar entre diferentes grupos de configurações:

Um exemplo de janela de preferências

Abrir caixa de diálogo

A caixa de diálogo Abrir oferece aos usuários uma maneira consistente de localizar e abrir um item em um aplicativo:

Uma caixa de diálogo aberta

O macOS fornece caixas de diálogo padrão de impressão e configuração de página que seu aplicativo pode exibir para que os usuários possam ter uma experiência de impressão consistente em todos os aplicativos que usam.

A caixa de diálogo Imprimir pode ser exibida como uma caixa de diálogo flutuante livre:

Uma caixa de diálogo de impressão

Ou pode ser exibido como uma planilha:

Uma folha de impressão

A caixa de diálogo Configurar página pode ser exibida como uma caixa de diálogo flutuante livre:

Uma caixa de diálogo de configuração de página

Ou pode ser exibido como uma planilha:

Uma planilha de configuração de página

Salvar caixas de diálogo

A caixa de diálogo Salvar oferece aos usuários uma maneira consistente de salvar um item em um aplicativo. A caixa de diálogo Salvar tem dois estados: Mínimo (também conhecido como Collapsed):

Uma caixa de diálogo de salvamento

E o estado expandido :

Uma caixa de diálogo de salvamento expandida

A caixa de diálogo Salvar mínimo também pode ser exibida como uma planilha:

Uma planilha de salvamento mínimo

Assim como a caixa de diálogo Salvar expandida :

Uma planilha de salvamento expandida

Para obter mais informações, consulte a seção Diálogos das Diretrizes de Interface Humana do OS X da Apple

Adicionando uma janela modal a um projeto

Além da janela principal do documento, um aplicativo Xamarin.Mac pode precisar exibir outros tipos de janelas para o usuário, como Preferências ou Painéis de Inspetor.

Para adicionar uma nova janela, faça o seguinte:

  1. No Gerenciador de Soluções, abra o Main.storyboard arquivo para edição no Construtor de Interfaces do Xcode.

  2. Arraste um novo Controlador de Exibição para a Superfície de Design:

    Selecionando um controlador de exibição na biblioteca

  3. No Inspetor de identidade, insira CustomDialogController para o Nome da classe:

    Definindo o nome da classe como CustomDialogController.

  4. Volte para o Visual Studio para Mac, permita que ele seja sincronizado com o Xcode e crie o CustomDialogController.h arquivo.

  5. Retorne ao Xcode e projete sua interface:

    Projetando a interface do usuário no Xcode

  6. Crie um Modal Segue da Janela Principal do seu aplicativo para o novo Controlador de Exibição arrastando com a tecla Control pressionada do elemento de interface do usuário que abrirá a caixa de diálogo para a janela da caixa de diálogo. Atribua o identificadorModalSegue:

    A modal segue

  7. Conecte todas as ações e tomadas:

    Configurando uma ação

  8. Salve suas alterações e retorne ao Visual Studio para Mac para sincronizar com o Xcode.

Faça com que o CustomDialogController.cs arquivo tenha a seguinte aparência:

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

Esse código expõe algumas propriedades para definir o título e a descrição da caixa de diálogo e alguns eventos para reagir à caixa de diálogo que está sendo cancelada ou aceita.

Em seguida, edite o ViewController.cs arquivo, substitua o PrepareForSegue método e faça com que ele se pareça com o seguinte:

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

Esse código inicializa o segue que definimos no Construtor de Interfaces do Xcode para nossa caixa de diálogo e configura o título e a descrição. Ele também lida com a escolha que o usuário faz na caixa de diálogo.

Podemos executar nosso aplicativo e exibir a caixa de diálogo personalizada:

Um exemplo de caixa de diálogo

Para obter mais informações sobre como usar o Windows em um aplicativo Xamarin.Mac, consulte nossa documentação Trabalhando com o Windows .

Criando uma planilha personalizada

Uma planilha é uma caixa de diálogo modal anexada a uma determinada janela de documento, impedindo que os usuários interajam com a janela até que ignorem a caixa de diálogo. Uma planilha é anexada à janela da qual ela emerge e somente uma planilha pode ser aberta para uma janela a qualquer momento.

Para criar uma planilha personalizada no Xamarin.Mac, vamos fazer o seguinte:

  1. No Gerenciador de Soluções, abra o Main.storyboard arquivo para edição no Construtor de Interfaces do Xcode.

  2. Arraste um novo Controlador de Exibição para a Superfície de Design:

    Selecionando um controlador de exibição na biblioteca

  3. Projete sua interface de usuário:

    O design da interface do usuário

  4. Crie um Sheet Segue da janela principal para o novo View Controller:

    Selecionando o tipo de segue de planilha

  5. No Inspetor de identidade, nomeie a classe SheetViewController do controlador de exibição :

    Definindo o nome da classe como SheetViewController.

  6. Defina quaisquer saídas e ações necessárias:

    Definindo as saídas e ações necessárias

  7. Salve suas alterações e retorne ao Visual Studio para Mac para sincronizar.

Em seguida, edite o SheetViewController.cs arquivo e faça com que ele fique parecido com o seguinte:

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

Em seguida, edite o ViewController.cs arquivo, edite o PrepareForSegue método e faça com que ele fique parecido com o seguinte:

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 executarmos nosso aplicativo e abrirmos a planilha, ela será anexada à janela:

Uma folha de exemplo

Criando uma caixa de diálogo de preferências

Antes de definirmos o Modo de Exibição de Preferências no Construtor de Interfaces, precisaremos adicionar um tipo de segue personalizado para lidar com a troca das preferências. Adicione uma nova classe ao seu projeto e chame-a ReplaceViewSequede . Edite a classe e faça com que ela fique parecida com a seguinte:

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

    }

}

Com o segue personalizado criado, podemos adicionar uma nova janela no Construtor de Interfaces do Xcode para lidar com nossas preferências.

Para adicionar uma nova janela, faça o seguinte:

  1. No Gerenciador de Soluções, abra o Main.storyboard arquivo para edição no Construtor de Interfaces do Xcode.

  2. Arraste um novo Controlador de Janela para a Superfície de Design:

    Selecione um controlador de janela na biblioteca

  3. Organize a janela perto do designer da barra de menus:

    Adicionando a nova janela

  4. Crie cópias do Controlador de Exibição anexado, pois haverá guias em seu modo de exibição de preferência:

    Adicionando os controladores de exibição necessários

  5. Arraste um novo Controlador da Barra de Ferramentas da Biblioteca:

    Selecione um controlador da barra de ferramentas na biblioteca

  6. E solte-o na Janela na Superfície de Design:

    Adicionando um novo controlador da barra de ferramentas

  7. Faça o layout do design da sua barra de ferramentas:

    Layout da barra de ferramentas

  8. Clique com a tecla Control pressionada e arraste de cada botão da barra de ferramentas para as exibições criadas acima. Selecione um tipo de segue personalizado :

    Definindo um tipo de segue personalizado.

  9. Selecione o novo Segue e defina a Classe como ReplaceViewSegue:

    Definindo a classe segue

  10. No Designer da Barra de Menus na Superfície de Design, no Menu do Aplicativo, selecione Preferências..., clique com a tecla Control pressionada e arraste para a Janela de Preferências para criar um Show segue:

    Definir o tipo de segue arrastando Preferências para a janela Preferências.

  11. Salve suas alterações e retorne ao Visual Studio para Mac para sincronizar.

Se executarmos o código e selecionarmos as Preferências... no Menu do Aplicativo, a janela será exibida:

Um exemplo de janela de preferências exibindo a palavra Perfil.

Para obter mais informações sobre como trabalhar com o Windows e as Barras de Ferramentas, consulte nossa documentação do Windows e das Barras de Ferramentas.

Salvando e carregando preferências

Em um aplicativo macOS típico, quando o usuário faz alterações em qualquer uma das preferências do usuário do aplicativo, essas alterações são salvas automaticamente. A maneira mais fácil de lidar com isso em um aplicativo Xamarin.Mac é criar uma única classe para gerenciar todas as preferências do usuário e compartilhá-la em todo o sistema.

Primeiro, adicione uma nova AppPreferences classe ao projeto e herde de NSObject. As preferências serão projetadas para usar a vinculação de dados e a codificação de valor-chave, o que tornará o processo de criação e manutenção dos formulários de preferência muito mais simples. Como as Preferências consistirão em uma pequena quantidade de tipos de dados simples, use o interno NSUserDefaults para armazenar e recuperar valores.

Edite o AppPreferences.cs arquivo e faça com que ele fique parecido com o seguinte:

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

Essa classe contém algumas rotinas auxiliares, como SaveInt, LoadInt, SaveColor, LoadColor, etc. para facilitar o trabalho NSUserDefaults . Além disso, como NSUserDefaults não possui uma maneira integrada de lidar com NSColors, os NSColorToHexString métodos and NSColorFromHexString são usados para converter cores em strings hexadecimais baseadas na web (#RRGGBBAA onde AA está a transparência alfa) que podem ser facilmente armazenadas e recuperadas.

No arquivo, crie uma instância do objeto AppPreferences que será usada em todo o AppDelegate.cs aplicativo:

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

        ...

Preferências de fiação para vistas de preferência

Em seguida, conecte a classe Preference aos elementos da interface do usuário na janela de preferências e nas exibições criadas acima. No Construtor de Interfaces, selecione um Controlador de Exibição de Preferências e alterne para o Inspetor de Identidade, crie uma classe personalizada para o controlador:

O Inspetor de Identidade

Volte para o Visual Studio para Mac para sincronizar suas alterações e abrir a classe recém-criada para edição. Faça com que a classe se pareça com a seguinte:

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

Observe que essa classe fez duas coisas aqui: Primeiro, há uma propriedade auxiliar App para facilitar o acesso ao AppDelegate . Em segundo lugar, a Preferences propriedade expõe a classe AppPreferences global para associação de dados com todos os controles de interface do usuário colocados nesse modo de exibição.

Em seguida, clique duas vezes no arquivo Storyboard para reabri-lo no Construtor de Interfaces (e veja as alterações feitas acima). Arraste todos os controles de interface do usuário necessários para criar a interface de preferências para a Exibição. Para cada controle, alterne para o Inspetor de Associação e vincule às propriedades individuais da classe AppPreference :

O inspetor de encadernação

Repita as etapas acima para todos os painéis (Controladores de vista) e Propriedades de preferência necessárias.

Aplicando alterações de preferência a todas as janelas abertas

Conforme mencionado acima, em um aplicativo macOS típico, quando o usuário faz alterações em qualquer uma das Preferências do Usuário do Aplicativo, essas alterações são salvas automaticamente e aplicadas a qualquer janela que o usuário possa ter aberto no aplicativo.

O planejamento e o design cuidadosos das preferências e janelas do seu aplicativo permitirão que esse processo aconteça de forma suave e transparente para o usuário final, com uma quantidade mínima de trabalho de codificação.

Para qualquer Window que consumirá Preferências do Aplicativo, adicione a seguinte propriedade auxiliar ao seu Controlador de Exibição de Conteúdo para facilitar o acesso ao nosso AppDelegate :

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

Em seguida, adicione uma classe para configurar o conteúdo ou o comportamento com base nas preferências do usuário:

public void ConfigureEditor() {

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

}

Você precisa chamar o método de configuração quando a janela é aberta pela primeira vez para garantir que ela esteja em conformidade com as preferências do usuário:

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

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

Em seguida, edite o AppDelegate.cs arquivo e adicione o seguinte método para aplicar quaisquer alterações de preferência a todas as janelas abertas:

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

}

Em seguida, adicione uma PreferenceWindowDelegate classe ao projeto e faça com que ela tenha a seguinte aparência:

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

Isso fará com que todas as alterações de preferência sejam enviadas para todas as janelas abertas quando a janela de preferências for fechada.

Por fim, edite o Controlador de Janela de Preferências e adicione o delegado criado acima:

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

Com todas essas alterações em vigor, se o usuário editar as Preferências do aplicativo e fechar a janela de preferências, as alterações serão aplicadas a todas as janelas abertas:

Um exemplo de Janela de Preferências, exibida com várias outras janelas abertas.

A caixa de diálogo Abrir

A caixa de diálogo Abrir oferece aos usuários uma maneira consistente de localizar e abrir um item em um aplicativo. Para exibir uma caixa de diálogo Abrir em um aplicativo Xamarin.Mac, use o seguinte código:

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;

    }
}

No código acima, estamos abrindo uma nova janela de documento para exibir o conteúdo do arquivo. Você precisará substituir esse código pela funcionalidade exigida pelo seu aplicativo.

As seguintes propriedades estão disponíveis ao trabalhar com um NSOpenPanel:

  • CanChooseFiles - Se true o usuário puder selecionar arquivos.
  • CanChooseDirectories - Se true o usuário puder selecionar diretórios.
  • AllowsMultipleSelection - Se true o usuário puder selecionar mais de um arquivo por vez.
  • ResolveAliases - Se true selecionar e alias, resolve-o para o caminho do arquivo original.
  • AllowedFileTypes - É uma matriz de cadeia de caracteres de tipos de arquivo que o usuário pode selecionar como uma extensão ou UTI. O valor padrão é null, que permite que qualquer arquivo seja aberto.

O RunModal () método exibe a caixa de diálogo Abrir e permite que o usuário selecione arquivos ou diretórios (conforme especificado pelas propriedades) e retorna 1 se o usuário clicar no botão Abrir .

A caixa de diálogo Abrir retorna os arquivos ou diretórios selecionados do usuário como uma matriz de URLs na URL propriedade.

Se executarmos o programa e selecionarmos o item Abrir... no menu Arquivo, o seguinte será exibido:

Uma caixa de diálogo aberta

As caixas de diálogo Imprimir e Configurar página

O macOS fornece caixas de diálogo padrão de impressão e configuração de página que seu aplicativo pode exibir para que os usuários possam ter uma experiência de impressão consistente em todos os aplicativos que usam.

O código a seguir mostrará a caixa de diálogo de impressão padrão:

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 definirmos a ShowPrintAsSheet propriedade como false, executar o aplicativo e exibir a caixa de diálogo de impressão, o seguinte será exibido:

Uma caixa de diálogo de impressão

Se definir a ShowPrintAsSheet propriedade como true, execute o aplicativo e exiba a caixa de diálogo de impressão, o seguinte será exibido:

Uma folha de impressão

O código a seguir exibirá a caixa de diálogo Layout de página:

[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 definirmos a ShowPrintAsSheet propriedade como false, executar o aplicativo e exibir a caixa de diálogo de layout de impressão, o seguinte será exibido:

Uma caixa de diálogo de configuração de página

Se definir a ShowPrintAsSheet propriedade como true, execute o aplicativo e exiba a caixa de diálogo de layout de impressão, o seguinte será exibido:

Uma planilha de configuração de página

Para obter mais informações sobre como trabalhar com as caixas de diálogo de impressão e configuração de página, consulte a documentação NSPrintPanel e NSPageLayout da Apple.

A caixa de diálogo Salvar

A caixa de diálogo Salvar oferece aos usuários uma maneira consistente de salvar um item em um aplicativo.

O código a seguir mostrará a caixa de diálogo Salvar padrão:

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

}

A AllowedFileTypes propriedade é uma matriz de cadeia de caracteres de tipos de arquivo que o usuário pode selecionar para salvar o arquivo. O tipo de arquivo pode ser especificado como uma extensão ou UTI. O valor padrão é null, que permite que qualquer tipo de arquivo seja usado.

Se definirmos a ShowSaveAsSheet propriedade como false, execute o aplicativo e selecione Salvar como... no menu Arquivo, o seguinte será exibido:

Uma caixa de diálogo de salvamento

O usuário pode expandir a caixa de diálogo:

Uma caixa de diálogo de salvamento expandida

Se definirmos a ShowSaveAsSheet propriedade como true, execute o aplicativo e selecione Salvar como... no menu Arquivo, o seguinte será exibido:

Uma planilha de salvamento

O usuário pode expandir a caixa de diálogo:

Uma planilha de salvamento expandida

Para obter mais informações sobre como trabalhar com a caixa de diálogo Salvar, consulte a documentação NSSavePanel da Apple.

Resumo

Este artigo examinou detalhadamente o trabalho com janelas modais, planilhas e caixas de diálogo do sistema padrão em um aplicativo Xamarin.Mac. Vimos os diferentes tipos e usos de Janelas Modais , Planilhas e Caixas de Diálogo, como criar e manter Janelas Modais e Planilhas no Construtor de Interfaces do Xcode e como trabalhar com Janelas Modais