Compartilhar via


Criando controles personalizados no Xamarin.Mac

Ao trabalhar com C# e .NET em um aplicativo Xamarin.Mac, você tem acesso aos mesmos controles de usuário que um desenvolvedor que trabalha no Objective-C, Swift e Xcode . Como o Xamarin.Mac se integra diretamente ao Xcode, você pode usar o Construtor de Interfaces do Xcode para criar e manter seus Controles de Usuário (ou, opcionalmente, criá-los diretamente no código C#).

Embora o macOS forneça uma grande variedade de controles de usuário integrados, pode haver momentos em que você precise criar um controle personalizado para fornecer funcionalidades não fornecidas prontas para uso ou para corresponder a um tema de interface do usuário personalizado (como uma interface de jogo).

Exemplo de um controle de interface do usuário personalizado

Neste artigo, abordaremos as noções básicas da criação de um controle de interface do usuário personalizado reutilizável 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 aos controles personalizados

Conforme mencionado acima, pode haver momentos em que você precisa criar um controle de interface do usuário personalizado reutilizável para fornecer funcionalidade exclusiva para a interface do usuário do aplicativo Xamarin.Mac ou para criar um tema de interface do usuário personalizado (como uma interface de jogo).

Nessas situações, você pode herdar NSControl e criar facilmente uma ferramenta personalizada que pode ser adicionada à interface do usuário do aplicativo por meio do código C# ou por meio do Construtor de Interfaces do Xcode. Ao herdar do NSControl seu controle personalizado, terá automaticamente todos os recursos padrão que um controle de interface do usuário interno possui (como NSButton).

Se o controle de interface do usuário personalizado exibir apenas informações (como um gráfico personalizado e uma ferramenta gráfica), talvez você queira herdar de em vez de NSView NSControl.

Independentemente da classe base usada, as etapas básicas para criar um controle personalizado são as mesmas.

Neste artigo, criará um componente Flip Switch personalizado que fornece um tema de interface do usuário exclusivo e um exemplo de criação de um controle de interface do usuário personalizado totalmente funcional.

Criando o controle personalizado

Como o controle personalizado que estamos criando responderá à entrada do usuário (cliques com o botão esquerdo do mouse), vamos herdar do NSControl. Dessa forma, nosso controle personalizado terá automaticamente todos os recursos padrão que um controle de interface do usuário integrado possui e responderá como um controle macOS padrão.

No Visual Studio para Mac, abra o projeto Xamarin.Mac para o qual você deseja criar um Controle de Interface do Usuário Personalizado (ou crie um novo). Adicione uma nova classe e chame-a NSFlipSwitchde :

Adicionando uma nova classe

Em seguida, edite a NSFlipSwitch.cs classe e faça com que ela fique parecida com o seguinte:

using Foundation;
using System;
using System.CodeDom.Compiler;
using AppKit;
using CoreGraphics;

namespace MacCustomControl
{
    [Register("NSFlipSwitch")]
    public class NSFlipSwitch : NSControl
    {
        #region Private Variables
        private bool _value = false;
        #endregion

        #region Computed Properties
        public bool Value {
            get { return _value; }
            set {
                // Save value and force a redraw
                _value = value;
                NeedsDisplay = true;
            }
        }
        #endregion

        #region Constructors
        public NSFlipSwitch ()
        {
            // Init
            Initialize();
        }

        public NSFlipSwitch (IntPtr handle) : base (handle)
        {
            // Init
            Initialize();
        }

        [Export ("initWithFrame:")]
        public NSFlipSwitch (CGRect frameRect) : base(frameRect) {
            // Init
            Initialize();
        }

        private void Initialize() {
            this.WantsLayer = true;
            this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
        }
        #endregion

        #region Draw Methods
        public override void DrawRect (CGRect dirtyRect)
        {
            base.DrawRect (dirtyRect);

            // Use Core Graphic routines to draw our UI
            ...

        }
        #endregion

        #region Private Methods
        private void FlipSwitchState() {
            // Update state
            Value = !Value;
        }
        #endregion

    }
}

A primeira coisa a notar sobre nossa classe personalizada é que estamos herdando NSControl e usando o comando Register para expor essa classe e o Objective-C Construtor de Interface do Xcode:

[Register("NSFlipSwitch")]
public class NSFlipSwitch : NSControl

Nas seções a seguir, examinaremos o restante do código acima em detalhes.

Rastreando o estado do controle

Como nosso controle personalizado é um switch, precisamos de uma maneira de rastrear o estado ligado/desligado do switch. Lidamos com isso com o seguinte código em NSFlipSwitch:

private bool _value = false;
...

public bool Value {
    get { return _value; }
    set {
        // Save value and force a redraw
        _value = value;
        NeedsDisplay = true;
    }
}

Quando o estado do switch muda, precisamos de uma maneira de atualizar a interface do usuário. Fazemos isso forçando o controle a redesenhar sua interface do usuário com NeedsDisplay = true.

Se nosso controle exigisse mais do que um único estado On/Off (por exemplo, um switch multiestado com 3 posições), poderíamos ter usado um Enum para rastrear o estado. Para o nosso exemplo, um simples bool serve.

Também adicionamos um método auxiliar para trocar o estado da opção entre Ligado e Desligado:

private void FlipSwitchState() {
    // Update state
    Value = !Value;
}

Posteriormente, expandiremos essa classe auxiliar para informar ao chamador quando o estado dos switches for alterado.

Desenhando a interface do controle

Vamos usar rotinas de desenho do Core Graphic para desenhar a interface do usuário do nosso controle personalizado em tempo de execução. Antes de podermos fazer isso, precisamos ativar as camadas para nosso controle. Fazemos isso com o seguinte método privado:

private void Initialize() {
    this.WantsLayer = true;
    this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
}

Esse método é chamado de cada um dos construtores do controle para garantir que o controle esteja configurado corretamente. Por exemplo:

public NSFlipSwitch (IntPtr handle) : base (handle)
{
    // Init
    Initialize();
}

Em seguida, precisamos substituir o DrawRect método e adicionar as rotinas do Core Graphic para desenhar o controle:

public override void DrawRect (CGRect dirtyRect)
{
    base.DrawRect (dirtyRect);

    // Use Core Graphic routines to draw our UI
    ...

}

Ajustaremos a representação visual do controle quando seu estado for alterado (como passar de Ativado para Desativado). Sempre que o estado for alterado, podemos usar o NeedsDisplay = true comando para forçar o controle a redesenhar para o novo estado.

Respondendo à entrada do usuário

Há duas maneiras básicas de adicionar a entrada do usuário ao nosso controle personalizado: Substituir rotinas de manipulação do mouse ou reconhecedores de gestos. O método que usamos será baseado na funcionalidade exigida pelo nosso controle.

Importante

Para qualquer controle personalizado que você criar, você deve usar Métodos de Substituição ou Reconhecedores de Gestos, mas não ambos ao mesmo tempo, pois eles podem entrar em conflito entre si.

Manipulando a entrada do usuário com métodos de substituição

Os objetos que herdam de (ou NSView) têm vários métodos de substituição para manipular a entrada do NSControl mouse ou do teclado. Para nosso controle de exemplo, queremos inverter o estado da opção entre Ativado e Desativado quando o usuário clica no controle com o botão esquerdo do mouse. Podemos adicionar os seguintes métodos de substituição à NSFlipSwitch classe para lidar com isso:

#region Mouse Handling Methods
// --------------------------------------------------------------------------------
// Handle mouse with Override Methods.
// NOTE: Use either this method or Gesture Recognizers, NOT both!
// --------------------------------------------------------------------------------
public override void MouseDown (NSEvent theEvent)
{
    base.MouseDown (theEvent);

    FlipSwitchState ();
}

public override void MouseDragged (NSEvent theEvent)
{
    base.MouseDragged (theEvent);
}

public override void MouseUp (NSEvent theEvent)
{
    base.MouseUp (theEvent);
}

public override void MouseMoved (NSEvent theEvent)
{
    base.MouseMoved (theEvent);
}
## endregion

No código acima, chamamos o FlipSwitchState método (definido acima) para inverter o estado On/Off da chave no MouseDown método. Isso também forçará o controle a ser redesenhado para refletir o estado atual.

Manipulando a entrada do usuário com reconhecedores de gestos

Opcionalmente, você pode usar Reconhecedores de Gestos para manipular o usuário que interage com o controle. Remova as substituições adicionadas acima, edite o Initialize método e faça com que ele se pareça com o seguinte:

private void Initialize() {
    this.WantsLayer = true;
    this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;

    // --------------------------------------------------------------------------------
    // Handle mouse with Gesture Recognizers.
    // NOTE: Use either this method or the Override Methods, NOT both!
    // --------------------------------------------------------------------------------
    var click = new NSClickGestureRecognizer (() => {
        FlipSwitchState();
    });
    AddGestureRecognizer (click);
}

Aqui, estamos criando um novo NSClickGestureRecognizer e chamando nosso FlipSwitchState método para alterar o estado do switch quando o usuário clica nele com o botão esquerdo do mouse. O AddGestureRecognizer (click) método adiciona o Reconhecimento de Gestos ao controle.

Novamente, o método que usamos depende do que estamos tentando realizar com nosso controle personalizado. Se precisarmos de acesso de baixo nível à interação do usuário, use os métodos de substituição. Se precisarmos de funcionalidades predefinidas, como cliques do mouse, use Reconhecedores de Gestos.

Respondendo a eventos de mudança de estado

Quando o usuário altera o estado do nosso controle personalizado, precisamos de uma maneira de responder à alteração de estado no código (como fazer algo ao clicar em um botão personalizado).

Para fornecer essa funcionalidade, edite a NSFlipSwitch classe e adicione o seguinte código:

#region Events
public event EventHandler ValueChanged;

internal void RaiseValueChanged() {
    if (this.ValueChanged != null)
        this.ValueChanged (this, EventArgs.Empty);

    // Perform any action bound to the control from Interface Builder
    // via an Action.
    if (this.Action !=null)
        NSApplication.SharedApplication.SendAction (this.Action, this.Target, this);
}
## endregion

Em seguida, edite o FlipSwitchState método e faça com que ele fique parecido com o seguinte:

private void FlipSwitchState() {
    // Update state
    Value = !Value;
    RaiseValueChanged ();
}

Primeiro, fornecemos um ValueChanged evento ao qual podemos adicionar um manipulador no código C# para que possamos executar uma ação quando o usuário alterar o estado da opção.

Em segundo lugar, como nosso controle personalizado herda de NSControl, ele tem automaticamente uma ação que pode ser atribuída no Construtor de Interfaces do Xcode. Para chamar essa ação quando o estado for alterado, usamos o seguinte código:

if (this.Action !=null)
    NSApplication.SharedApplication.SendAction (this.Action, this.Target, this);

Primeiro, verificamos se uma ação foi atribuída ao controle. Em seguida, chamamos a Ação se ela tiver sido definida.

Usando o controle personalizado

Com nosso controle personalizado totalmente definido, podemos adicioná-lo à interface do usuário do aplicativo Xamarin.Mac usando o código C# ou no Construtor de Interfaces do Xcode.

Para adicionar o controle usando o Construtor de Interfaces, primeiro faça um build limpo do projeto Xamarin.Mac e, em seguida, clique duas vezes no Main.storyboard arquivo para abri-lo no Construtor de Interfaces para edição:

Editando o storyboard no Xcode

Em seguida, arraste a Custom View para o design da interface do usuário:

Selecionando um Modo de Exibição Personalizado na Biblioteca

Com o Modo de Exibição Personalizado ainda selecionado, alterne para o Inspetor de Identidade e altere a Classe do modo de exibição paraNSFlipSwitch:

Definindo a classe do Modo de Exibição

Alterne para o Editor Assistente e crie uma saída para o controle personalizado (certificando-se de vinculá-lo ao ViewController.h arquivo e não ao .m arquivo):

Configurando uma nova tomada

Salve suas alterações, retorne ao Visual Studio para Mac e permita que as alterações sejam sincronizadas. Edite o ViewController.cs arquivo e faça com que o ViewDidLoad método se pareça com o seguinte:

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

    // Do any additional setup after loading the view.
    OptionTwo.ValueChanged += (sender, e) => {
        // Display the state of the option switch
        Console.WriteLine("Option Two: {0}", OptionTwo.Value);
    };
}

Aqui, respondemos ao ValueChanged evento que definimos acima na NSFlipSwitch classe e escrevemos o valor atual quando o usuário clica no controle.

Opcionalmente, poderíamos retornar ao Construtor de Interfaces e definir uma Ação no controle:

Configurando uma nova ação

Novamente, edite o ViewController.cs arquivo e adicione o seguinte método:

partial void OptionTwoFlipped (Foundation.NSObject sender) {
    // Display the state of the option switch
    Console.WriteLine("Option Two: {0}", OptionTwo.Value);
}

Importante

Você deve usar o Evento ou definir uma Ação no Construtor de Interfaces, mas não deve usar os dois métodos ao mesmo tempo ou eles podem entrar em conflito entre si.

Resumo

Este artigo examinou detalhadamente a criação de um controle de interface do usuário personalizado reutilizável em um aplicativo Xamarin.Mac. Vimos como desenhar a interface do usuário de controles personalizados, as duas principais maneiras de responder à entrada do mouse e do usuário e como expor o novo controle a ações no Construtor de Interfaces do Xcode.