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 ist, können Sie den Schnittstellen-Generator von Xcode verwenden, um Ihre Benutzersteuerelemente zu erstellen und zu verwalten (oder sie optional direkt in 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.
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 im Objective-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 NSControl
wir 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 NSFlipSwitch
auf:
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 oder Gestenerkennungen 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 sauberen Build des Xamarin.Mac-Projekts aus, und doppelklicken Sie dann auf die Main.storyboard
Datei, um es im Schnittstellen-Generator zum Bearbeiten zu öffnen:
Ziehen Sie als Nächstes ein Custom View
Shape in das Design der Benutzeroberfläche:
Wenn die benutzerdefinierte Ansicht weiterhin ausgewählt ist, wechseln Sie zum Identitätsinspektor, und ändern Sie die Klasse der Ansicht in NSFlipSwitch
:
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):
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:
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 sie die benutzerdefinierte Steuerelement-UI zeichnen, die beiden wichtigsten Möglichkeiten zum Reagieren auf Maus- und Benutzereingaben und zum Verfügbarmachen des neuen Steuerelements für Aktionen im Benutzeroberflächen-Generator von Xcode.