Condividi tramite


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.

Esempio di un controllo dell'interfaccia utente personalizzato

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# alObjective-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é NSControlda 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:

Aggiunta di una nuova classe

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 metodidi override oriconoscitori 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:

Modifica dello storyboard in Xcode

Trascinare quindi un oggetto Custom View nella progettazione dell'interfaccia utente:

Selezione di una visualizzazione personalizzata dalla libreria

Con la visualizzazione personalizzata ancora selezionata, passare a Identity Inspector e modificare la classe della visualizzazione in NSFlipSwitch:

Impostazione della classe View

Passare all'Editor assistente e creare un outlet per il controllo personalizzato (assicurandosi di associarlo nel file e non nel .mViewController.h file):

Configurazione di un nuovo outlet

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 :

Configurazione di una nuova azione

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.