Erstellen von benutzerdefinierten Steuerelementen in Xamarin.Mac

Wenn Sie mit C# und .NET in einer Xamarin.Mac-Anwendung arbeiten, haben Sie Zugriff auf die gleichen Benutzersteuerelemente, in Objective-Cdenen ein Entwickler arbeitet, Swift und Xcode . Da Xamarin.Mac direkt in Xcode integriert wird, können Sie den Benutzeroberflächen-Generator von Xcode verwenden, um Ihre Benutzersteuerelemente zu erstellen und zu Standard (oder optional direkt im C#-Code zu erstellen).

Während macOS eine Fülle von integrierten Benutzersteuerelementen bietet, kann es vorkommen, dass Sie ein benutzerdefiniertes Steuerelement erstellen müssen, um Funktionen bereitzustellen, die nicht sofort verfügbar sind oder um einem benutzerdefinierten Benutzeroberflächendesign (z. B. einer Spieloberfläche) entsprechen.

Beispiel für ein benutzerdefiniertes UI-Steuerelement

In diesem Artikel befassen wir uns mit den Grundlagen der Erstellung eines wiederverwendbaren benutzerdefinierten Benutzeroberflächensteuerelements in einer Xamarin.Mac-Anwendung. Es wird dringend empfohlen, dass Sie zuerst den Artikel "Hello, Mac " durcharbeiten, insbesondere die Abschnitte "Einführung in Xcode" und "Interface Builder " und "Outlets" und "Actions ", da es sich um wichtige Konzepte und Techniken handelt, die wir in diesem Artikel verwenden werden.

Möglicherweise möchten Sie sich auch die Verfügbarmachen von C#-Klassen /-Methoden imObjective-CAbschnitt des Xamarin.Mac Internals-Dokuments ansehen, und es werden die befehle Export erläutert, die Register zum Verketten Ihrer C#-Klassen mit Objective-C Objekten und UI-Elementen verwendet werden.

Einführung in benutzerdefinierte Steuerelemente

Wie oben erwähnt, kann es vorkommen, dass Sie ein wiederverwendbares, benutzerdefiniertes Benutzeroberflächensteuerelement erstellen müssen, um eindeutige Funktionen für die Benutzeroberfläche Ihrer Xamarin.Mac-App bereitzustellen oder ein benutzerdefiniertes UI-Design (z. B. eine Spieloberfläche) zu erstellen.

In diesen Situationen können Sie ganz einfach ein NSControl benutzerdefiniertes Tool erben und erstellen, das entweder über C#-Code oder über den Schnittstellen-Generator von Xcode zu Ihrer App hinzugefügt werden kann. Durch das Erben von NSControl Ihrem benutzerdefinierten Steuerelement verfügen automatisch alle Standardfeatures, über die ein integriertes Benutzeroberflächensteuerelement verfügt (z NSButton. B. ).

Wenn Ihr benutzerdefiniertes Benutzeroberflächen-Steuerelement nur Informationen anzeigt (z. B. ein benutzerdefiniertes Diagramm- und Grafiktool), sollten Sie anstelle NSView von NSControl.

Unabhängig davon, welche Basisklasse verwendet wird, sind die grundlegenden Schritte zum Erstellen eines benutzerdefinierten Steuerelements identisch.

Erstellen Sie in diesem Artikel eine benutzerdefinierte Flip Switch-Komponente, die ein eindeutiges Benutzeroberflächendesign und ein Beispiel für die Erstellung eines voll funktionsfähigen benutzerdefinierten Benutzeroberflächensteuerelements bereitstellt.

Erstellen des benutzerdefinierten Steuerelements

Da das benutzerdefinierte Steuerelement, das NSControlwir erstellen, auf Benutzereingaben reagiert (linke Maustastenklicks), werden wir von erben. Auf diese Weise verfügt unser benutzerdefiniertes Steuerelement automatisch über alle Standardfeatures, über die ein integriertes Benutzeroberflächensteuerelement verfügt und wie ein standardmäßiges macOS-Steuerelement reagiert.

Öffnen Sie in Visual Studio für Mac das Xamarin.Mac-Projekt, für das Sie ein benutzerdefiniertes Benutzeroberflächensteuerelement erstellen möchten (oder erstellen Sie ein neues). Fügen Sie einen neuen Kurs hinzu, und rufen Sie ihn NSFlipSwitchauf:

Hinzufügen einer neuen Klasse

Bearbeiten Sie als Nächstes den NSFlipSwitch.cs Kurs, und stellen Sie sicher, dass sie wie folgt aussieht:

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

    }
}

Beachten Sie zunächst unsere benutzerdefinierte Klasse, dass wir von NSControl der Klasse erben und den Befehl Register verwenden, um diese Klasse Objective-C dem Schnittstellen-Generator von Xcode zur Verfügung zu stellen:

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

In den folgenden Abschnitten sehen wir uns den restlichen Code im Detail an.

Nachverfolgen des Zustands des Steuerelements

Da unser benutzerdefiniertes Steuerelement ein Schalter ist, benötigen wir eine Möglichkeit, den Ein/Aus-Zustand des Schalters nachzuverfolgen. Wir behandeln dies mit dem folgenden Code in NSFlipSwitch:

private bool _value = false;
...

public bool Value {
    get { return _value; }
    set {
        // Save value and force a redraw
        _value = value;
        NeedsDisplay = true;
    }
}

Wenn sich der Status der Option ändert, benötigen wir eine Möglichkeit, die Benutzeroberfläche zu aktualisieren. Dazu erzwingen wir, dass das Steuerelement die Benutzeroberfläche neu gezeichnet hat NeedsDisplay = true.

Wenn unser Steuerelement mehr benötigt, dass ein einzelner Ein/Aus-Zustand (z. B. ein Mehrzustandsschalter mit 3 Positionen) erforderlich ist, könnten wir eine Enumeration zum Nachverfolgen des Zustands verwendet haben. Für unser Beispiel wird ein einfacher Bool ausgeführt.

Außerdem wurde eine Hilfsmethode hinzugefügt, um den Zustand des Schalters zwischen Ein und Aus zu tauschen:

private void FlipSwitchState() {
    // Update state
    Value = !Value;
}

Später erweitern wir diese Hilfsklasse, um den Anrufer darüber zu informieren, wenn sich der Zustand der Schalter geändert hat.

Zeichnen der Schnittstelle des Steuerelements

Wir werden Core Graphic-Zeichnungsroutinen verwenden, um die Benutzeroberfläche unseres benutzerdefinierten Steuerelements zur Laufzeit zu zeichnen. Bevor wir dies tun können, müssen wir Ebenen für unser Steuerelement aktivieren. Dies geschieht mit der folgenden privaten Methode:

private void Initialize() {
    this.WantsLayer = true;
    this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
}

Diese Methode wird von jedem der Konstruktoren des Steuerelements aufgerufen, um sicherzustellen, dass das Steuerelement ordnungsgemäß konfiguriert ist. Zum Beispiel:

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

Als Nächstes müssen wir die DrawRect Methode außer Kraft setzen und die Core Graphic-Routinen hinzufügen, um das Steuerelement zu zeichnen:

public override void DrawRect (CGRect dirtyRect)
{
    base.DrawRect (dirtyRect);

    // Use Core Graphic routines to draw our UI
    ...

}

Wir passen die visuelle Darstellung des Steuerelements an, wenn sich der Zustand ändert (z. B. von "Ein" zu "Aus"). Jedes Mal, wenn sich der Zustand ändert, können wir den NeedsDisplay = true Befehl verwenden, um zu erzwingen, dass das Steuerelement für den neuen Zustand neu gezeichnet wird.

Reagieren auf Benutzereingaben

Es gibt zwei grundlegende Möglichkeiten, wie wir Benutzereingaben zu unserem benutzerdefinierten Steuerelement hinzufügen können: Außerkraftsetzen von Mausbehandlungsroutinen oder Gestenerkennungen. Welche Methode wir verwenden, basiert auf der Funktionalität, die von unserer Kontrolle benötigt wird.

Wichtig

Für jedes benutzerdefinierte Steuerelement, das Sie erstellen, sollten Sie entweder Methodenüberschreiben oderGestenerkennungen verwenden, aber nicht beide gleichzeitig, da sie miteinander in Konflikt stehen können.

Behandeln von Benutzereingaben mit Außerkraftsetzungsmethoden

Objekte, die von NSControl (oder NSView) erben, weisen mehrere Überschreibungsmethoden für die Behandlung von Maus- oder Tastatureingaben auf. Für unser Beispielsteuerelement möchten wir den Zustand des Schalters zwischen Ein und Aus kippen, wenn der Benutzer mit der linken Maustaste auf das Steuerelement klickt. Wir können der NSFlipSwitch Klasse die folgenden Überschreibungsmethoden hinzufügen, um dies zu behandeln:

#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

Im obigen Code rufen wir die FlipSwitchState Methode (oben definiert) auf, um den Ein/Aus-Zustand des Schalters in der MouseDown Methode zu kippen. Dadurch wird auch erzwungen, dass das Steuerelement neu gezeichnet wird, um den aktuellen Zustand widerzuspiegeln.

Behandeln von Benutzereingaben mit Gestikerkennungen

Optional können Sie Gestenerkennungen verwenden, um den Benutzer zu verarbeiten, der mit dem Steuerelement interagiert. Entfernen Sie die oben hinzugefügten Außerkraftsetzungen, bearbeiten Sie die Initialize Methode, und stellen Sie sicher, dass sie wie folgt aussieht:

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);
}

Hier erstellen wir eine neue NSClickGestureRecognizer Methode und rufen unsere FlipSwitchState Methode auf, um den Zustand des Schalters zu ändern, wenn der Benutzer mit der linken Maustaste darauf klickt. Die AddGestureRecognizer (click) Methode fügt dem Steuerelement die Gestikerkennung hinzu.

Welche Methode wir verwenden, hängt wiederum davon ab, was wir mit unserem benutzerdefinierten Steuerelement erreichen möchten. Wenn der Zugriff auf die Benutzerinteraktion auf niedriger Ebene erforderlich ist, verwenden Sie die Außerkraftsetzungsmethoden.If we need low level access the to user interaction, use the Override Methods. Wenn wir vordefinierte Funktionen benötigen, z. B. Mausklicks, verwenden Sie Gestenerkennungen.

Reagieren auf Zustandsänderungsereignisse

Wenn der Benutzer den Status unseres benutzerdefinierten Steuerelements ändert, benötigen wir eine Möglichkeit, auf die Zustandsänderung im Code zu reagieren (z. B. eine Aktion, wenn auf eine benutzerdefinierte Schaltfläche geklickt wird).

Um diese Funktionalität bereitzustellen, bearbeiten Sie die NSFlipSwitch Klasse, und fügen Sie den folgenden Code hinzu:

#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

Bearbeiten Sie als Nächstes die FlipSwitchState Methode, und lassen Sie sie wie folgt aussehen:

private void FlipSwitchState() {
    // Update state
    Value = !Value;
    RaiseValueChanged ();
}

Zunächst stellen wir ein ValueChanged Ereignis bereit, dem wir in C#-Code einen Handler hinzufügen können, damit wir eine Aktion ausführen können, wenn der Benutzer den Status der Option ändert.

Zweitens, da unser benutzerdefiniertes Steuerelement erbt NSControl, verfügt es automatisch über eine Aktion , die im Schnittstellen-Generator von Xcode zugewiesen werden kann. Um diese Aktion aufzurufen, wenn sich der Zustand ändert, verwenden wir den folgenden Code:

if (this.Action !=null)
    NSApplication.SharedApplication.SendAction (this.Action, this.Target, this);

Zunächst überprüfen wir, ob dem Steuerelement eine Aktion zugewiesen wurde. Als Nächstes rufen wir die Aktion auf, wenn sie definiert wurde.

Verwenden des benutzerdefinierten Steuerelements

Mit dem vollständig definierten benutzerdefinierten Steuerelement können wir es entweder mithilfe von C#-Code oder im Benutzeroberflächen-Generator von Xcode zu unserer Xamarin.Mac-App hinzufügen.

Um das Steuerelement mithilfe des Schnittstellen-Generators hinzuzufügen, führen Sie zuerst einen sauber Build des Xamarin.Mac-Projekts aus, und doppelklicken Sie dann auf die Main.storyboard Datei, um es im Schnittstellen-Generator zum Bearbeiten zu öffnen:

Bearbeiten des Storyboards in Xcode

Ziehen Sie als Nächstes ein Custom View Shape in das Design der Benutzeroberfläche:

Auswählen einer benutzerdefinierten Ansicht aus der Bibliothek

Wenn die benutzerdefinierte Ansicht weiterhin ausgewählt ist, wechseln Sie zum Identitätsinspektor, und ändern Sie die Klasse der Ansicht in NSFlipSwitch:

Festlegen der Ansichtsklasse

Wechseln Sie zum Assistenten-Editor , und erstellen Sie eine Steckdose für das benutzerdefinierte Steuerelement (stellen Sie sicher, dass sie in der ViewController.h Datei und nicht in der .m Datei gebunden werden):

Konfigurieren eines neuen Steckdose

Speichern Sie Ihre Änderungen, kehren Sie zu Visual Studio für Mac zurück, und lassen Sie die Änderungen zu synchronisieren. Bearbeiten Sie die ViewController.cs Datei, und stellen Sie sicher, dass die ViewDidLoad Methode wie folgt aussieht:

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);
    };
}

Hier antworten wir auf das Ereignis, das ValueChanged wir oben für die NSFlipSwitch Klasse definiert haben, und schreiben den aktuellen Wert aus, wenn der Benutzer auf das Steuerelement klickt.

Optional können wir zum Schnittstellen-Generator zurückkehren und eine Aktion für das Steuerelement definieren:

Konfigurieren einer neuen Aktion

Bearbeiten Sie die ViewController.cs Datei erneut, und fügen Sie die folgende Methode hinzu:

partial void OptionTwoFlipped (Foundation.NSObject sender) {
    // Display the state of the option switch
    Console.WriteLine("Option Two: {0}", OptionTwo.Value);
}

Wichtig

Sie sollten entweder das Ereignis verwenden oder eine Aktion im Schnittstellen-Generator definieren, sie sollten jedoch nicht beide Methoden gleichzeitig verwenden oder miteinander in Konflikt stehen.

Zusammenfassung

Dieser Artikel hat einen detaillierten Blick auf das Erstellen eines wiederverwendbaren benutzerdefinierten Benutzeroberflächensteuerelements in einer Xamarin.Mac-Anwendung erstellt. Wir haben gesehen, wie die benutzerdefinierte Steuerelement-UI, die beiden Standard Möglichkeiten zum Reagieren auf Maus- und Benutzereingaben und zum Verfügbarmachen des neuen Steuerelements für Aktionen im Benutzeroberflächen-Generator von Xcode.