Creazione di controlli personalizzati in Xamarin.Mac
Quando si lavora con C# e .NET in un'applicazione Xamarin.Mac, è possibile accedere agli stessi controlli utente che uno sviluppatore che lavora in Objective-C, Swift e Xcode . Poiché Xamarin.Mac si integra direttamente con Xcode, è possibile usare Interface Builder di Xcode per creare e gestire i controlli utente (o facoltativamente crearli direttamente nel codice C#).
Anche se macOS offre un'ampia gamma di controlli utente predefiniti, potrebbe essere necessario creare un controllo personalizzato per fornire funzionalità non fornite predefinite o per trovare una corrispondenza con un tema dell'interfaccia utente personalizzato, ad esempio un'interfaccia del gioco.
In questo articolo verranno illustrate le nozioni di base per la creazione di un controllo interfaccia utente personalizzato riutilizzabile in un'applicazione Xamarin.Mac. È consigliabile usare prima di tutto l'articolo Hello, Mac , in particolare le sezioni Introduzione a Xcode e Interface Builder e Outlet e Actions , in quanto illustra i concetti e le tecniche chiave che verranno usati in questo articolo.
È possibile esaminare anche la sezione Esposizione di classi/metodi C# al Objective-Cdocumento Internals di Xamarin.Mac, che illustra anche i Register
comandi e Export
usati per collegare le classi C# agli oggetti e agli Objective-C elementi dell'interfaccia utente.
Introduzione ai controlli personalizzati
Come indicato in precedenza, potrebbero verificarsi momenti in cui è necessario creare un controllo interfaccia utente personalizzato riutilizzabile per fornire funzionalità univoche per l'interfaccia utente dell'app Xamarin.Mac o per creare un tema dell'interfaccia utente personalizzato (ad esempio un'interfaccia di gioco).
In queste situazioni, puoi facilmente ereditare da NSControl
e creare uno strumento personalizzato che può essere aggiunto all'interfaccia utente dell'app tramite codice C# o tramite Interface Builder di Xcode. Ereditando dal NSControl
controllo personalizzato, tutte le funzionalità standard di un controllo interfaccia utente predefinito ( ad esempio NSButton
).
Se il controllo interfaccia utente personalizzato visualizza solo informazioni ,ad esempio un grafico personalizzato e uno strumento grafico, è possibile ereditare da anziché NSControl
da NSView
.
Indipendentemente dalla classe di base usata, i passaggi di base per la creazione di un controllo personalizzato sono gli stessi.
In questo articolo verrà creato un componente flip switch personalizzato che fornisce un tema univoco dell'interfaccia utente e un esempio di creazione di un controllo interfaccia utente personalizzato completamente funzionante.
Compilazione del controllo personalizzato
Poiché il controllo personalizzato che stiamo creando risponderà all'input dell'utente (clic con il pulsante sinistro del mouse), erediteremo da NSControl
. In questo modo, il controllo personalizzato avrà automaticamente tutte le funzionalità standard che un controllo interfaccia utente predefinito ha e risponde come un controllo macOS standard.
In Visual Studio per Mac aprire il progetto Xamarin.Mac per cui si vuole creare un controllo interfaccia utente personalizzato o crearne uno nuovo. Aggiungere una nuova classe e chiamarla NSFlipSwitch
:
Modificare quindi la NSFlipSwitch.cs
classe e renderla simile alla seguente:
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
}
}
La prima cosa da notare sulla classe personalizzata in cui si eredita NSControl
e si usa il comando Register per esporre questa classe a Objective-C e Interface Builder di Xcode:
[Register("NSFlipSwitch")]
public class NSFlipSwitch : NSControl
Nelle sezioni seguenti verranno esaminate in dettaglio il resto del codice precedente.
Rilevamento dello stato del controllo
Poiché il controllo personalizzato è un'opzione, è necessario un modo per tenere traccia dello stato On/Off dell'interruttore. La gestione viene gestita con il codice seguente in NSFlipSwitch
:
private bool _value = false;
...
public bool Value {
get { return _value; }
set {
// Save value and force a redraw
_value = value;
NeedsDisplay = true;
}
}
Quando lo stato dell'opzione cambia, è necessario un modo per aggiornare l'interfaccia utente. A tale scopo, forzando il controllo a ridisegnare l'interfaccia utente con NeedsDisplay = true
.
Se il controllo richiedeva più che un singolo stato On/Off (ad esempio un commutatore a più stati con 3 posizioni), avremmo potuto usare un Enum per tenere traccia dello stato. Per questo esempio, un semplice bool farà.
È stato aggiunto anche un metodo helper per scambiare lo stato del commutatore tra Attivato e Disattivato:
private void FlipSwitchState() {
// Update state
Value = !Value;
}
Successivamente, si espanderà questa classe helper per informare il chiamante quando lo stato switch è cambiato.
Disegno dell'interfaccia del controllo
Useremo le routine di disegno Core Graphic per disegnare l'interfaccia utente del controllo personalizzato in fase di esecuzione. Prima di poter eseguire questa operazione, è necessario attivare i livelli per il controllo. Questa operazione viene eseguita con il metodo privato seguente:
private void Initialize() {
this.WantsLayer = true;
this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
}
Questo metodo viene chiamato da ognuno dei costruttori del controllo per assicurarsi che il controllo sia configurato correttamente. Ad esempio:
public NSFlipSwitch (IntPtr handle) : base (handle)
{
// Init
Initialize();
}
Successivamente, è necessario eseguire l'override del DrawRect
metodo e aggiungere le routine Core Graphic per disegnare il controllo:
public override void DrawRect (CGRect dirtyRect)
{
base.DrawRect (dirtyRect);
// Use Core Graphic routines to draw our UI
...
}
La rappresentazione visiva per il controllo verrà modificata quando lo stato cambia, ad esempio passando da Attivato a Disattivato. Ogni volta che lo stato cambia, è possibile usare il NeedsDisplay = true
comando per forzare il ridisegno del controllo per il nuovo stato.
Risposta all'input dell'utente
Esistono due modi di base per aggiungere l'input dell'utente al controllo personalizzato: Eseguire l'override delle routine di gestione del mouse o dei riconoscitori di movimento. Quale metodo viene usato, sarà basato sulle funzionalità richieste dal controllo.
Importante
Per qualsiasi controllo personalizzato creato, è consigliabile usare metodi di override o riconoscitori movimenti, ma non entrambi contemporaneamente possono entrare in conflitto tra loro.
Gestione dell'input utente con metodi di override
Gli oggetti che ereditano da NSControl
(o NSView
) hanno diversi metodi di override per la gestione dell'input del mouse o della tastiera. Per il controllo di esempio, si vuole capovolgere lo stato del commutatore tra Attivato e Disattivato quando l'utente fa clic sul controllo con il pulsante sinistro del mouse. È possibile aggiungere i metodi di override seguenti alla NSFlipSwitch
classe per gestirli:
#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
Nel codice precedente viene chiamato il FlipSwitchState
metodo (definito in precedenza) per capovolgere lo stato On/Off dell'opzione nel MouseDown
metodo . In questo modo il controllo verrà inoltre ridisegnato per riflettere lo stato corrente.
Gestione dell'input dell'utente con riconoscimento movimento
Facoltativamente, è possibile usare i riconoscitori movimenti per gestire l'interazione dell'utente con il controllo. Rimuovere le sostituzioni aggiunte in precedenza, modificare il Initialize
metodo e renderlo simile al seguente:
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);
}
In questo caso viene creato un nuovo NSClickGestureRecognizer
metodo e viene chiamato il FlipSwitchState
metodo per modificare lo stato dell'interruttore quando l'utente fa clic su di esso con il pulsante sinistro del mouse. Il AddGestureRecognizer (click)
metodo aggiunge il riconoscimento movimento al controllo .
Anche in questo caso, il metodo usato dipende da ciò che si sta tentando di eseguire con il controllo personalizzato. Se è necessario un accesso di basso livello all'interazione dell'utente, usare i metodi di override. Se sono necessarie funzionalità predefinite, ad esempio i clic del mouse, usare Riconoscimento movimenti.
Risposta agli eventi di modifica dello stato
Quando l'utente modifica lo stato del controllo personalizzato, è necessario un modo per rispondere alla modifica dello stato nel codice, ad esempio quando si fa clic su un pulsante personalizzato.
Per fornire questa funzionalità, modificare la NSFlipSwitch
classe e aggiungere il codice seguente:
#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
Modificare quindi il FlipSwitchState
metodo e renderlo simile al seguente:
private void FlipSwitchState() {
// Update state
Value = !Value;
RaiseValueChanged ();
}
In primo luogo, viene fornito un ValueChanged
evento a cui è possibile aggiungere un gestore nel codice C# in modo da poter eseguire un'azione quando l'utente modifica lo stato dell'opzione.
In secondo luogo, poiché il controllo personalizzato eredita da NSControl
, ha automaticamente un'azione che può essere assegnata in Interface Builder di Xcode. Per chiamare questa azione quando lo stato cambia, viene usato il codice seguente:
if (this.Action !=null)
NSApplication.SharedApplication.SendAction (this.Action, this.Target, this);
Prima di tutto, verificare se al controllo è stata assegnata un'azione. Successivamente, chiamiamo l'azione se è stata definita.
Uso del controllo personalizzato
Con il controllo personalizzato completamente definito, è possibile aggiungerlo all'interfaccia utente dell'app Xamarin.Mac usando il codice C# o in Interface Builder di Xcode.
Per aggiungere il controllo usando Interface Builder, eseguire prima una compilazione pulita del progetto Xamarin.Mac, quindi fare doppio clic sul Main.storyboard
file per aprirlo in Interface Builder per la modifica:
Trascinare quindi un oggetto Custom View
nella progettazione dell'interfaccia utente:
Con la visualizzazione personalizzata ancora selezionata, passare a Identity Inspector e modificare la classe della visualizzazione in NSFlipSwitch
:
Passare all'Editor assistente e creare un outlet per il controllo personalizzato (assicurandosi di associarlo nel file e non nel .m
ViewController.h
file):
Salvare le modifiche, tornare a Visual Studio per Mac e consentire la sincronizzazione delle modifiche. Modificare il ViewController.cs
file e fare in modo che il ViewDidLoad
metodo sia simile al seguente:
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);
};
}
In questo caso, rispondiamo all'evento ValueChanged
definito in precedenza nella NSFlipSwitch
classe e scriviamo il valore corrente quando l'utente fa clic sul controllo.
Facoltativamente, è possibile tornare a Interface Builder e definire un'azione sul controllo :
Anche in questo caso, modificare il ViewController.cs
file e aggiungere il metodo seguente:
partial void OptionTwoFlipped (Foundation.NSObject sender) {
// Display the state of the option switch
Console.WriteLine("Option Two: {0}", OptionTwo.Value);
}
Importante
È consigliabile usare l'evento o definire un'azione in Interface Builder, ma non è consigliabile usare entrambi i metodi contemporaneamente oppure possono entrare in conflitto tra loro.
Riepilogo
Questo articolo ha esaminato in dettaglio la creazione di un controllo interfaccia utente personalizzato riutilizzabile in un'applicazione Xamarin.Mac. È stato illustrato come disegnare l'interfaccia utente dei controlli personalizzati, i due modi principali per rispondere all'input del mouse e dell'utente e come esporre il nuovo controllo alle azioni in Interface Builder di Xcode.