Compartilhar via


Diálogos 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 trabalhando e Objective-CXcode faz. Como o Xamarin.Mac se integra diretamente ao Xcode, você pode usar o Construtor de Interfaces do Xcode para criar e manter seu Windows Modal (ou, opcionalmente, criá-los diretamente no código C#).

Uma caixa de diálogo aparece 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 Modeless (como um editor de texto que pode ter vários documentos abertos ao mesmo tempo) ou Modal (como uma caixa de diálogo Export que deve ser descartada antes que o aplicativo possa continuar).

Uma caixa de diálogo aberta

Neste artigo, abordaremos os conceitos básicos 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 Interface e Saídas e Ações, pois ele aborda os principais conceitos e técnicas que usaremos neste artigo.

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

Introdução às caixas de diálogo

Uma caixa de diálogo aparece em resposta a uma ação do usuário (como salvar um arquivo) e fornece uma maneira para 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 Dialog:

  • Modal de Documento - Uma caixa de diálogo Modal de Documento impede que o usuário faça qualquer outra coisa dentro de um determinado documento até que ele seja descartado.
  • Modal de aplicativo - Uma caixa de diálogo modal de aplicativo impede que o usuário interaja com o aplicativo até que ele seja descartado.
  • Sem moderação Uma caixa de diálogo sem moderação 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-a modalmente:

Um exemplo de janela modal

Folhas de diálogo modais de 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 descartem a caixa de diálogo. Uma folha é anexada à janela da qual ela emerge e apenas uma folha pode ser aberta para uma janela a qualquer momento.

Um exemplo de folha modal

Preferências do 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 O Windows geralmente inclui uma barra de ferramentas que permite ao usuário alternar entre diferentes grupos de configurações:

Uma janela de preferência de exemplo

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 folha 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 Recolhido):

Uma caixa de diálogo Salvar

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 folha de salvamento mínima

Assim como a caixa de diálogo Salvar expandida :

Uma folha 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 do 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 View Controller para a superfície de design:

    Selecionando um controlador de exibição na biblioteca

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

    Definindo o nome da classe como CustomDialogController.

  4. Volte para o Visual Studio para Mac, permita que ele sincronize 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 o controle do elemento da 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 quaisquer ações e saídas:

    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 tenha a seguinte aparência:

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 a sequência que definimos no Construtor de Interface do Xcode em 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:

Uma caixa de diálogo de exemplo

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 descartem a caixa de diálogo. Uma folha é anexada à janela da qual ela emerge e apenas uma folha 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 View Controller 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 sua janela principal para o novo View Controller:

    Selecionando o tipo de Planilha

  5. No Inspetor de Identidade, nomeie a classeSheetViewController do controlador de exibição:

    Definindo o nome da classe como SheetViewController.

  6. Defina as 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 torne-o 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 tenha a seguinte aparência:

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 a Visualização de Preferências no Construtor de Interfaces, precisaremos adicionar um tipo de opção personalizado para lidar com a alternância das preferências. Adicione uma nova classe ao seu projeto e chame-a ReplaceViewSequede . Edite a classe e faça com que ela tenha a seguinte aparência:

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 acompanhamento personalizado criado, podemos adicionar uma nova janela no Construtor de Interface 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 View Controller anexado, pois haverá guias em sua visualização de preferência:

    Adicionando os controladores de exibição necessários

  5. Arraste um novo controlador de barra de ferramentas da biblioteca:

    Selecione um controlador de barra de ferramentas na biblioteca

  6. E solte-o na janela na superfície de design:

    Adicionando um novo controlador de barra de ferramentas

  7. Layout do design da 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 os modos de exibição criados 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 até a Janela de Preferências para criar um Show segue:

    Defina o tipo de acompanhamento 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:

Uma janela de preferências de exemplo 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.

Preferências de salvamento e carregamento

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 do NSObject. As preferências serão projetadas para usar Vinculação de Dados e Codificação de Chave-Valor , 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 NSUserDefaults uma pequena quantidade de tipos de dados simples, use o interno para armazenar e recuperar valores.

Edite o AppPreferences.cs arquivo e faça com que ele tenha a seguinte aparência:

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

Esta classe contém algumas rotinas auxiliares, como SaveInt, LoadInt, SaveColor, LoadColor, etc. para facilitar o trabalho NSUserDefaults . Além disso, como NSUserDefaults não tem uma maneira interna de lidar com NSColors, os NSColorToHexString métodos e NSColorFromHexString são usados para converter cores em cadeias de caracteres 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á usado 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 modos de exibição de preferência

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

O Inspetor de Identidade

Alterne de volta 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 tenha a seguinte aparência:

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 vinculação de dados com quaisquer controles de interface do usuário colocados neste View.

Em seguida, clique duas vezes no arquivo Storyboard para reabri-lo no Interface Builder (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 Binding Inspector e vincule às propriedades individuais da classe AppPreference :

O Inspetor de Vinculação

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

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

Como dito 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 Janela que consumirá as Preferências do Aplicativo, adicione a seguinte propriedade auxiliar ao 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 de acordo 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 ele 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 quaisquer alterações de preferência sejam enviadas para todos os Windows abertos quando a janela de preferência for fechada.

Finalmente, edite o Controlador da 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 estiver selecionando 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 folha 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 como. 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 Salvar

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 folha de salvamento

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

Uma folha de salvamento expandida

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

Resumo

Este artigo deu uma olhada detalhada no trabalho com o Windows Modal, Planilhas e as caixas de diálogo padrão do sistema em um aplicativo Xamarin.Mac. Vimos os diferentes tipos e usos de Modal Windows, Sheets e Dialogs, como criar e manter Modal Windows e Sheets no Construtor de Interfaces do Xcode e como trabalhar com Modal Windows, Sheets e Dialogs em código C#.