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 quantidade de controles de usuário internos, pode haver momentos em que você precisa 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 do 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 sugerido que você trabalhe primeiro no artigo Olá, Mac , especificamente nas seções Introdução ao Xcode e Construtor de Interfaces e Saídas e Ações , pois 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# do Objective-C documento Interno do Xamarin.Mac , 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 indicado 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 do jogo).
Nessas situações, você pode herdar NSControl
facilmente e criar 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
controle personalizado, você terá automaticamente todos os recursos padrão que um Controle de Interface do Usuário interno tem (como NSButton
).
Se o controle de Interface do Usuário personalizado apenas exibir informações (como um gráfico personalizado e uma ferramenta gráfica), talvez você queira herdar de NSView
em vez de NSControl
.
Não importa qual classe base seja usada, as etapas básicas para criar um controle personalizado são as mesmas.
Neste artigo, criará um componente personalizado do Flip Switch que fornece um tema exclusivo da interface do usuário 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 no botão esquerdo do mouse), herdaremos de NSControl
. Dessa forma, nosso controle personalizado terá automaticamente todos os recursos padrão que um Controle de Interface do Usuário interno tem e responderá como um controle macOS padrão.
Em 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 NSFlipSwitch
de :
Em seguida, edite a NSFlipSwitch.cs
classe e faça com que ela se pareça 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 observar sobre nossa classe personalizada em que estamos herdando NSControl
e usando o comando Register para expor essa classe e Objective-C o Construtor de Interfaces do Xcode:
[Register("NSFlipSwitch")]
public class NSFlipSwitch : NSControl
Nas seções a seguir, vamos dar uma olhada no restante do código acima em detalhes.
Acompanhando o estado do controle
Como nosso Controle Personalizado é um comutador, precisamos de uma maneira de controlar o estado Ativado/Desativado do comutador. 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 comutador é alterado, 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 que um único estado Ativado/Desativado (por exemplo, um comutador de vários estados com três posições), poderíamos ter usado uma Enumeração para acompanhar o estado. Para nosso exemplo, um bool simples fará.
Também adicionamos um método auxiliar para trocar o estado da opção entre Ativado e Desativado:
private void FlipSwitchState() {
// Update state
Value = !Value;
}
Posteriormente, expandiremos essa classe auxiliar para informar o chamador quando o estado de alternância for alterado.
Desenhando a interface do controle
Vamos usar as rotinas de desenho gráfico principal para desenhar a Interface do Usuário do nosso controle personalizado em runtime. Antes de fazermos 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 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
...
}
Vamos ajustar a representação visual para o controle quando o estado dele for alterado (como ir 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 Tratamento de Mouse ou Reconhecedores de Gestos. Qual método 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 em que eles podem entrar em conflito uns com os outros.
Manipulando a entrada do usuário com métodos de substituição
Objetos que herdam de (ou NSView
) têm vários métodos de substituição para lidar com a entrada de NSControl
mouse ou teclado. Para nosso controle de exemplo, queremos inverter o estado da opção entre Ativado e Desativado quando o usuário clicar 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 Ativado/Desativado da opção 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 lidar com 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 comutador 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, qual método 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 funcionalidade predefinida, 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 quando clica 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 se pareça 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.
Segundo, 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, marcar para ver se uma Ação foi atribuída ao controle. Em seguida, chamaremos 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 limpo build do projeto Xamarin.Mac e clique duas vezes no Main.storyboard
arquivo para abri-lo no Construtor de Interfaces para edição:
Em seguida, arraste um Custom View
para o design da Interface do Usuário:
Com a Exibição Personalizada ainda selecionada, alterne para o Inspetor de Identidade e altere a Classe do modo de exibição para NSFlipSwitch
:
Alterne para o Editor Assistente e crie uma Tomada para o controle personalizado (certificando-se de associá-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 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 gravamos o Valor atual quando o usuário clica no controle.
Opcionalmente, podemos retornar ao Construtor de Interfaces 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 ambos os métodos ao mesmo tempo ou eles podem entrar em conflito uns com os outros.
Resumo
Este artigo deu uma olhada detalhada na 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 main 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.