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 os conceitos básicos 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 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 aos controles personalizados

Como dito acima, pode haver momentos em que você precise 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 facilmente herdar e criar uma ferramenta personalizada que pode ser adicionada à interface do usuário do seu aplicativo por meio do código C# ou por meio do Construtor de NSControl 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 tem (como NSButton).

Se o controle personalizado da Interface do Usuário exibir apenas informações (como uma ferramenta gráfica e de gráficos personalizadas), convém herdar de em vez de NSViewNSControl.

Não importa qual 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 estará respondendo à entrada do usuário (cliques no botão esquerdo do mouse), herdaremos do NSControl. Dessa forma, nosso controle personalizado terá automaticamente todos os recursos padrão que um Controle de Interface do Usuário integrado tem e responderá como um controle padrão do macOS.

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 criar um novo). Adicione uma nova classe e chame-a NSFlipSwitch:

Adicionando uma nova classe

Em seguida, edite a NSFlipSwitch.cs classe e faça com que ela tenha a seguinte aparência:

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 observar sobre nossa classe personalizada é que estamos herdando e usando o comando Register para expor essa classe ao e ao Objective-C Construtor de NSControl Interface do Xcode:

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

Nas seções a seguir, daremos uma olhada no 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 On/Off do switch. Nós 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 = trueo .

Se nosso controle exigisse mais do que um único estado On/Off (por exemplo, um switch de vários estados 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 do switch entre On e Off:

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 gráfico principal para desenhar a interface do usuário do nosso controle personalizado em tempo de execução. Antes de podermos fazer isso, precisamos ativar 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 seja configurado corretamente. Por exemplo:

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

Em seguida, precisamos substituir o DrawRect método e adicionar as rotinas 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 muda, 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 criado, você deve usar Métodos de SubstituiçãoouReconhecedores de Gestos, mas não ambos ao mesmo tempo, pois eles podem entrar em conflito um com o outro.

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 do interruptor 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 do switch 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 torne-o parecido 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 Gesture Recognizer 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 alteração 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 tenha a seguinte aparência:

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

Em segundo lugar, como nosso controle personalizado herda do , ele tem automaticamente uma Ação que pode ser atribuída no Construtor de NSControlInterfaces do Xcode. Para chamar essa ação quando o estado é 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 de 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 código C# ou no Construtor de Interfaces do Xcode.

Para adicionar o controle usando o Interface Builder, primeiro faça uma compilação limpa do projeto Xamarin.Mac e, em seguida, clique duas vezes no Main.storyboard arquivo para abri-lo no Interface Builder para edição:

Editando o storyboard no Xcode

Em seguida, arraste um 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 View

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

Configurando um novo Outlet

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

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 Interface Builder 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 um com o outro.

Resumo

Este artigo deu uma olhada detalhada na criação de um controle de interface de 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 Interface do Xcode.