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).
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 NSView
NSControl
.
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
:
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 = true
o .
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 NSControl
Interfaces 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:
Em seguida, arraste um Custom View
para o design da Interface do Usuário:
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
:
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):
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:
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.