Erstellen von benutzerdefinierten Steuerelementen in xamarin. MacCreating Custom Controls in Xamarin.Mac

Wenn Sie mit c# und .net in einer xamarin. Mac-Anwendung arbeiten, haben Sie Zugriff auf dieselben Benutzer Steuerelemente, die ein Entwickler in " Ziel-C", " SWIFT " und " Xcode " verwendet.When working with C# and .NET in a Xamarin.Mac application, you have access to the same User Controls that a developer working in Objective-C, Swift and Xcode does. Da xamarin. Mac direkt in Xcode integriert ist, können Sie die Interface Builder von Xcode verwenden, um die Benutzer Steuerelemente zu erstellen und zu verwalten (oder Sie optional direkt in c#-Code zu erstellen).Because Xamarin.Mac integrates directly with Xcode, you can use Xcode's Interface Builder to create and maintain your User Controls (or optionally create them directly in C# code).

MacOS bietet zwar eine Vielzahl integrierter Benutzer Steuerelemente, es kann jedoch vorkommen, dass Sie ein benutzerdefiniertes Steuerelement erstellen müssen, um Funktionen bereitzustellen, die nicht standardmäßig bereitgestellt werden, oder um ein benutzerdefiniertes UI-Design (z. b. eine spielschnittstelle) abzugleichen.While macOS provides a wealth of built-in User Controls, there might be times that you need to create a custom control to provide functionality not provided out-of-the-box or to match a custom UI theme (such as a game interface).

Beispiel für ein benutzerdefiniertes UI-SteuerelementExample of a custom UI control

In diesem Artikel werden die Grundlagen der Erstellung eines wiederverwendbaren benutzerdefinierten Benutzeroberflächen Steuer Elements in einer xamarin. Mac-Anwendung behandelt.In this article, we'll cover the basics of creating a reusable Custom User Interface Control in a Xamarin.Mac application. Es wird dringend empfohlen, dass Sie zunächst den Artikel Hello, Mac , insbesondere die Einführung in Xcode und die Abschnitte zu Interface Builder und Outlets und Aktionen , durcharbeiten, da er wichtige Konzepte und Techniken behandelt, die wir in diesem Artikel verwenden werden.It is highly suggested that you work through the Hello, Mac article first, specifically the Introduction to Xcode and Interface Builder and Outlets and Actions sections, as it covers key concepts and techniques that we'll be using in this article.

Lesen Sie ggf. den Abschnitt verfügbar machen von c#-Klassen/-Methoden zu "Ziel-c " im Dokument " xamarin. Mac ". darin werden die Register -und-Befehle erläutert, die Export zum Verknüpfen ihrer c#-Klassen mit Ziel-c-Objekten und Benutzeroberflächen Elementen verwendet werden.You may want to take a look at the Exposing C# classes / methods to Objective-C section of the Xamarin.Mac Internals document as well, it explains the Register and Export commands used to wire-up your C# classes to Objective-C objects and UI Elements.

Einführung in benutzerdefinierte SteuerelementeIntroduction to Custom Controls

Wie bereits erwähnt, kann es vorkommen, dass Sie ein wiederverwendbares benutzerdefiniertes Steuerelement für die Benutzeroberfläche erstellen müssen, um eine eindeutige Funktionalität für die Benutzeroberfläche Ihrer xamarin. Mac-app bereitzustellen oder um ein benutzerdefiniertes UI-Design (z. b. eine spielschnittstelle)As stated above, there might be times when you need to create a reusable, Custom User Interface Control to provide unique functionality for your Xamarin.Mac app's UI or to create a custom UI theme (such as a game interface).

In diesen Situationen können Sie problemlos von Erben NSControl und ein benutzerdefiniertes Tool erstellen, das entweder über c#-Code oder über den Interface Builder von Xcode zur Benutzeroberfläche Ihrer app hinzugefügt werden kann.In these situations, you can easily inherit from NSControl and create a custom tool that can either be added to your app's UI via C# code or through Xcode's Interface Builder. Wenn NSControl Sie von einem benutzerdefinierten Steuerelement erben, verfügt automatisch über alle Standard Features, die ein integriertes Steuerelement für die Benutzeroberfläche hat (z NSButton . b.).By inheriting from NSControl your custom control will automatically have all of the standard features that a built-in User Interface Control has (such as NSButton).

Wenn Ihr benutzerdefiniertes Steuerelement für die Benutzeroberfläche lediglich Informationen anzeigt (z. b. ein benutzerdefiniertes Diagramm und Grafik Tool), können Sie von NSView anstelle von Erben NSControl .If your custom User Interface control just displays information (like a custom charting and graphic tool), you might want to inherit from NSView instead of NSControl.

Unabhängig davon, welche Basisklasse verwendet wird, sind die grundlegenden Schritte zum Erstellen eines benutzerdefinierten Steuer Elements identisch.No matter which base class is used, the basic steps for creating a custom control is the same.

In diesem Artikel erstellen Sie eine benutzerdefinierte Flip Switch-Komponente, die ein eindeutiges Design der Benutzeroberfläche und ein Beispiel für die Erstellung eines voll funktionsfähigen benutzerdefinierten Benutzeroberflächen-Steuer Elements bereitstellt.In this article will, create a custom Flip Switch component that provides a unique User Interface Theme and an example of building a fully functional Custom User Interface Control.

Aufbauen des benutzerdefinierten Steuer ElementsBuilding the Custom Control

Da das benutzerdefinierte Steuerelement, das wir erstellen, auf die Benutzereingabe (Mausklicks mit der linken Maustaste) antwortet, wird die Vererbung von übernommen NSControl .Since the custom control we are creating will be responding to user input (left mouse button clicks), we are going to inherit from NSControl. Auf diese Weise verfügt unser benutzerdefiniertes Steuerelement automatisch über alle Standard Features, die ein integriertes Steuerelement für die Benutzeroberfläche hat und wie ein macOS-Standard Steuerelement antwortet.In this way, our custom control will automatically have all of the standard features that a built-in User Interface Control has and respond like a standard macOS control.

Öffnen Sie in Visual Studio für Mac das xamarin. Mac-Projekt, für das Sie ein benutzerdefiniertes Steuerelement für die Benutzeroberfläche erstellen möchten (oder erstellen Sie ein neues).In Visual Studio for Mac, open the Xamarin.Mac project that you want to create a Custom User Interface Control for (or create a new one). Fügen Sie eine neue Klasse hinzu, und nennen Sie Sie NSFlipSwitch :Add a new class and call it NSFlipSwitch:

Hinzufügen einer neuen KlasseAdding a new class

Bearbeiten Sie als nächstes die NSFlipSwitch.cs -Klasse, und machen Sie Sie wie folgt:Next, edit the NSFlipSwitch.cs class and make it look like the following:

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

    }
}

Der erste Punkt, der über unsere benutzerdefinierte Klasse in zu beachten ist, die von uns geerbt wird NSControl und die den Register -Befehl verwendet, um diese Klasse für die Interface Builder von Ziel-C und Xcode verfügbar zu machen:The first thing to notice about our custom class in that we are inheriting from NSControl and using the Register command to expose this class to Objective-C and Xcode's Interface Builder:

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

In den folgenden Abschnitten wird der Rest des obigen Codes ausführlich erläutert.In the following sections, we'll take a look at the rest of the above code in detail.

Nachverfolgen des Zustands des Steuer ElementsTracking the Control's State

Da unser benutzerdefiniertes Steuerelement ein Switch ist, benötigen wir eine Möglichkeit, den ein-/Ausschalten des Schalters zu verfolgen.Since our Custom Control is a switch, we need a way to track the On/Off state of the switch. Dies wird mit dem folgenden Code in behandelt NSFlipSwitch :We handle that with the following 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 des Schalters ändert, benötigen wir eine Möglichkeit, die Benutzeroberfläche zu aktualisieren.When the state of the switch changes, we need a way to updated the UI. Dazu erzwingen Sie, dass das Steuerelement die Benutzeroberfläche mit neu zeichnet NeedsDisplay = true .We do that by forcing the control to redraw its UI with NeedsDisplay = true.

Wenn für das Steuerelement mehr als ein/aus-Status (z. b. ein Switch mit mehreren Zuständen mit drei Positionen) benötigt wird, könnte eine-Aufzählung verwendet werden , um den Status zu verfolgen.If our control required more that a single On/Off state (for example a multi-state switch with 3 positions), we could have used an Enum to track the state. In unserem Beispiel wird ein einfacher boolescher Code dargestellt.For our example, a simple bool will do.

Wir haben auch eine Hilfsmethode hinzugefügt, um den Status des Schalters zwischen on und Off auszutauschen:We also added a helper method to swap the state of the switch between On and Off:

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

Später wird diese Hilfsklasse erweitert, um den Aufrufer darüber zu informieren, wenn sich der Schalter Zustand geändert hat.Later, we'll expand this helper class to inform the caller when the switches state has changed.

Zeichnen der-Schnittstelle des Steuer ElementsDrawing the Control's Interface

Wir werden die Kern Grafik Zeichnungs Routinen verwenden, um die Benutzeroberfläche des benutzerdefinierten Steuer Elements zur Laufzeit zu zeichnen.We are going to use Core Graphic drawing routines to draw our custom control's User Interface at runtime. Bevor wir dies tun können, müssen wir die Ebenen für das Steuerelement aktivieren.Before we can do this, we need to turn on layers for our control. Dies geschieht mit der folgenden privaten Methode:We do this with the following private method:

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

Diese Methode wird von jedem der Konstruktoren des Steuer Elements aufgerufen, um sicherzustellen, dass das Steuerelement ordnungsgemäß konfiguriert ist.This method gets called from each of the control's constructors to ensure that the control is properly configured. Beispiel:For example:

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

Als nächstes müssen wir die DrawRect -Methode überschreiben und die Kern Grafik Routinen zum Zeichnen des Steuer Elements hinzufügen:Next, we need to override the DrawRect method and add the Core Graphic routines to draw the control:

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

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

}

Die visuelle Darstellung des Steuer Elements wird angepasst, wenn sich der Zustand ändert (z. b. durch ein-oder ausschalten).We'll be adjusting the visual representation for the control when its state changes (such as going from On to Off). Wenn sich der Status ändert, können wir mit dem NeedsDisplay = true Befehl erzwingen, dass das Steuerelement für den neuen Zustand neu gezeichnet wird.Any time the state changes, we can use the NeedsDisplay = true command to force the control to redraw for the new state.

Reagieren auf BenutzereingabenResponding to User Input

Es gibt zwei grundlegende Möglichkeiten, Benutzereingaben zum benutzerdefinierten Steuerelement hinzuzufügen: über Schreiben der Maus Behandlungs Routinen oder Gesten ErkennungsTools.There are two basic way that we can add user input to our custom control: Override Mouse Handling Routines or Gesture Recognizers. Welche Methode wir verwenden, basiert auf der Funktionalität, die von unserem Steuerelement benötigt wird.Which method we use, will be based on the functionality required by our control.

Wichtig

Für ein beliebiges benutzerdefiniertes Steuerelement, das Sie erstellen, sollten Sie entweder Überschreibungs Methoden oder Gesten ErkennungsTools verwenden, aber nicht beides gleichzeitig, da Sie miteinander in Konflikt stehen können.For any custom control you create, you should use either Override Methods or Gesture Recognizers, but not both at the same time as they can conflict with each other.

Behandeln von Benutzereingaben mit Überschreibungs MethodenHandling User Input with Override Methods

Objekte, die von NSControl (oder NSView ) erben, verfügen über mehrere Überschreibungs Methoden zur Behandlung von Maus-oder Tastatureingaben.Objects that inherit from NSControl (or NSView) have several override methods for handling mouse or keyboard input. In unserem Beispiel Steuerelement möchten wir den Zustand des Schalters zwischen ein -und ausschalten , wenn der Benutzer mit der linken Maustaste auf das Steuerelement klickt.For our example control, we want to flip the state of the switch between On and Off when the user clicks on the control with the left mouse button. Wir können der-Klasse die folgenden Überschreibungs Methoden hinzufügen NSFlipSwitch , um Folgendes zu behandeln:We can add the following override methods to the NSFlipSwitch class to handle this:

#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 wird die FlipSwitchState -Methode (oben definiert) aufgerufen, um den ein-/Ausschalten des Schalters in der- MouseDown Methode zu kippen.In the above code, we call the FlipSwitchState method (defined above) to flip the On/Off state of the switch in the MouseDown method. Dadurch wird auch erzwungen, dass das Steuerelement neu gezeichnet wird, um den aktuellen Zustand widerzuspiegeln.This will also force the control to be redrawn to reflect the current state.

Behandeln von Benutzereingaben mit Gesten Erkennungs ToolsHandling User Input with Gesture Recognizers

Optional können Sie Gesten Erkennungs Tools verwenden, um den Benutzer zu verarbeiten, der mit dem Steuerelement interagiert.Optionally, you can use Gesture Recognizers to handle the user interacting with the control. Entfernen Sie die oben hinzugefügten außer Kraft setzungen, bearbeiten Initialize Sie die-Methode, und führen Sie Sie wie folgt aus:Remove the overrides added above, edit the Initialize method and make it look like the following:

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 und rufen unsere FlipSwitchState Methode auf, um den Status des Schalters zu ändern, wenn der Benutzer mit der linken Maustaste darauf klickt.Here, we are creating a new NSClickGestureRecognizer and calling our FlipSwitchState method to change the switch's state when the user clicks on it with the left mouse button. Mit der-Methode wird dem- AddGestureRecognizer (click) Steuerelement die Gestenerkennung hinzugefügt.The AddGestureRecognizer (click) method adds the Gesture Recognizer to the control.

Die Methode, die wir verwenden, hängt wiederum davon ab, was wir mit unserem benutzerdefinierten Steuerelement erreichen möchten.Again, which method we use depends on what we are trying to accomplish with our custom control. Verwenden Sie die Überschreibungs Methoden, wenn der Zugriff auf die Benutzerinteraktion gering ist.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 Gesten Erkennungs Tools.If we need predefined functionality, such as mouse clicks, use Gesture Recognizers.

Reagieren auf Status Änderungs EreignisseResponding to State Change Events

Wenn der Benutzer den Zustand des benutzerdefinierten Steuer Elements ändert, benötigen wir eine Möglichkeit, auf die Statusänderung im Code zu reagieren (z. b. durch das durch Klicken auf eine benutzerdefinierte Schaltfläche).When the user changes the state of our custom control, we need a way to respond to the state change in code (such as doing something when clicks on a custom button).

Um diese Funktionalität bereitzustellen, bearbeiten NSFlipSwitch Sie die-Klasse, und fügen Sie den folgenden Code hinzu:To provide this functionality, edit the NSFlipSwitch class and add the following code:

#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 führen Sie Sie wie folgt aus:Next, edit the FlipSwitchState method and make it look like the following:

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

Zuerst wird ein Ereignis bereitgestellt ValueChanged , dem wir einen Handler in c#-Code hinzufügen können, damit wir eine Aktion ausführen können, wenn der Benutzer den Zustand des Schalters ändert.First, we provide a ValueChanged event that we can add a handler to in C# code so that we can perform an action when the user changes the state of the switch.

Zweitens: da das benutzerdefinierte Steuerelement von erbt NSControl , verfügt es automatisch über eine Aktion , die in der Interface Builder von Xcode zugewiesen werden kann.Second, because our custom control inherits from NSControl, it automatically has an Action that can be assigned in Xcode's Interface Builder. Um diese Aktion aufzurufen, wenn sich der Status ändert, verwenden wir den folgenden Code:To call this Action when the state changes, we use the following code:

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

Zuerst überprüfen wir, ob dem Steuerelement eine Aktion zugewiesen wurde.First, we check to see if an Action has been assigned to the control. Als nächstes wird die Aktion aufgerufen, wenn Sie definiert wurde.Next, we call the Action if it has been defined.

Verwenden des benutzerdefinierten Steuer ElementsUsing the Custom Control

Wenn das benutzerdefinierte Steuerelement vollständig definiert ist, können wir es entweder der Benutzeroberfläche der xamarin. Mac-App mithilfe von c#-Code oder in der Interface Builder von Xcode hinzufügen.With our custom control fully defined, we can either add it to our Xamarin.Mac app's UI using C# code or in Xcode's Interface Builder.

Um das Steuerelement mithilfe von Interface Builder hinzuzufügen, führen Sie zunächst eine saubere Erstellung des xamarin. Mac-Projekts aus, und doppelklicken Sie dann Main.storyboard auf die Datei, um Sie in Interface Builder zu öffnen:To add the control using Interface Builder, first do a clean build of the Xamarin.Mac project, then double-click the Main.storyboard file to open it in Interface Builder for edit:

Bearbeiten des Storyboards in XcodeEditing the storyboard in Xcode

Ziehen Sie als nächstes eine Custom View in den Benutzeroberflächen Entwurf:Next, drag a Custom View into the User Interface design:

Auswählen einer benutzerdefinierten Ansicht aus der BibliothekSelecting a Custom View from the Library

Wenn die benutzerdefinierte Ansicht noch ausgewählt ist, wechseln Sie zum Identitäts Inspektor , und ändern Sie die Klasse der Ansicht in NSFlipSwitch :With the Custom View still selected, switch to the Identity Inspector and change the view's Class to NSFlipSwitch:

Festlegen der Klasse der SichtSetting the View's class

Wechseln Sie zum Assistenten-Editor , und erstellen Sie ein Outlet für das benutzerdefinierte Steuerelement (stellen Sie sicher, dass es in der ViewController.h Datei und nicht in der Datei gebunden wird .m ):Switch to the Assistant Editor and create an Outlet for the custom control (making sure to bind it in the ViewController.h file and not the .m file):

Konfigurieren eines neuen OutletsConfiguring a new Outlet

Speichern Sie die Änderungen, kehren Sie zu Visual Studio für Mac zurück, und lassen Sie die Synchronisierung zu. Bearbeiten Sie die ViewController.cs Datei, und führen Sie die ViewDidLoad Methode wie folgt aus:Save your changes, return to Visual Studio for Mac and allow the changes to sync. Edit the ViewController.cs file and make the ViewDidLoad method look like the following:

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 reagieren wir auf das Ereignis, das ValueChanged wir oben in der Klasse definiert haben, NSFlipSwitch und schreiben den aktuellen Wert , wenn der Benutzer auf das Steuerelement klickt.Here, we respond to the ValueChanged event we defined above on the NSFlipSwitch class and write out the current Value when the user clicks on the control.

Optional könnten wir zu Interface Builder zurückkehren und eine Aktion für das Steuerelement definieren:Optionally, we could return to Interface Builder and define an Action on the control:

Konfigurieren einer neuen AktionConfiguring a new Action

Bearbeiten Sie die Datei erneut, ViewController.cs und fügen Sie die folgende Methode hinzu:Again, edit the ViewController.cs file and add the following method:

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 in Interface Builder definieren, aber Sie sollten nicht beide Methoden gleichzeitig verwenden, oder Sie können miteinander in Konflikt stehen.You should use either the Event or define an Action in Interface Builder, but you should not use both methods at the same time or they can conflict with each other.

ZusammenfassungSummary

In diesem Artikel wurde beschrieben, wie Sie ein wiederverwendbares benutzerdefiniertes Benutzeroberflächen-Steuerelement in einer xamarin. Mac-Anwendung erstellen.This article has taken a detailed look at creating a reusable Custom User Interface Control in a Xamarin.Mac application. Wir haben gesehen, wie die Benutzeroberfläche für benutzerdefinierte Steuerelemente gezeichnet wird, die zwei Hauptmöglichkeiten, auf die Maus-und Benutzereingaben zu reagieren und das neue Steuerelement für Aktionen in der Interface Builder von Xcode verfügbar zu machen.We saw how to draw the custom controls UI, the two main ways to respond to mouse and user input and how to expose the new control to Actions in Xcode's Interface Builder.