Création de contrôles personnalisés dans Xamarin.Mac
Lorsque vous utilisez C# et .NET dans une application Xamarin.Mac, vous avez accès aux mêmes contrôles utilisateur qu’un développeur travaillant dans Objective-C, Swift et Xcode . Étant donné que Xamarin.Mac s’intègre directement à Xcode, vous pouvez utiliser le Générateur d’interface de Xcode pour créer et gérer vos contrôles utilisateur (ou éventuellement les créer directement dans du code C#).
Bien que macOS offre une multitude de contrôles utilisateur intégrés, il peut arriver que vous deviez créer un contrôle personnalisé pour fournir des fonctionnalités non fournies prêtes à l’emploi ou pour correspondre à un thème d’interface utilisateur personnalisé (par exemple, une interface de jeu).
Dans cet article, nous allons aborder les principes de base de la création d’un contrôle d’interface utilisateur personnalisé réutilisable dans une application Xamarin.Mac. Il est fortement suggéré de commencer par l’article Hello, Mac , en particulier les sections Introduction to Xcode et Interface Builderet Outlets and Actions , car il couvre les concepts et techniques clés que nous allons utiliser dans cet article.
Vous pouvez également consulter la section Exposing C# classes/méthodes to Objective-C du document Xamarin.Mac Internals . Il explique les Register
commandes et Export
utilisées pour connecter vos classes C# à des objets et des éléments d’interface Objective-C utilisateur.
Présentation des contrôles personnalisés
Comme indiqué ci-dessus, il peut arriver que vous deviez créer un contrôle d’interface utilisateur personnalisé réutilisable pour fournir des fonctionnalités uniques à l’interface utilisateur de votre application Xamarin.Mac ou pour créer un thème d’interface utilisateur personnalisé (par exemple, une interface de jeu).
Dans ces situations, vous pouvez facilement hériter de NSControl
et créer un outil personnalisé qui peut être ajouté à l’interface utilisateur de votre application via le code C# ou via le Générateur d’interface de Xcode. En hériter de NSControl
votre contrôle personnalisé aura automatiquement toutes les fonctionnalités standard d’un contrôle d’interface utilisateur intégré (comme NSButton
).
Si votre contrôle d’interface utilisateur personnalisé affiche simplement des informations (comme un outil graphique et graphique personnalisé), vous pouvez en hériter NSView
au lieu de NSControl
.
Quelle que soit la classe de base utilisée, les étapes de base pour créer un contrôle personnalisé sont les mêmes.
Dans cet article, vous allez créer un composant bascule personnalisé qui fournit un thème d’interface utilisateur unique et un exemple de création d’un contrôle d’interface utilisateur personnalisé entièrement fonctionnel.
Génération du contrôle personnalisé
Étant donné que le contrôle personnalisé que nous créons répond à l’entrée utilisateur (clics sur le bouton gauche de la souris), nous allons hériter de NSControl
. De cette façon, notre contrôle personnalisé aura automatiquement toutes les fonctionnalités standard d’un contrôle d’interface utilisateur intégré et répondra comme un contrôle macOS standard.
Dans Visual Studio pour Mac, ouvrez le projet Xamarin.Mac pour lequel vous souhaitez créer un contrôle d’interface utilisateur personnalisé (ou en créer un nouveau). Ajoutez une nouvelle classe et appelez-la NSFlipSwitch
:
Ensuite, modifiez la NSFlipSwitch.cs
classe et faites-la ressembler à ce qui suit :
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 première chose à remarquer à propos de notre classe personnalisée dans le fait que nous héritons de NSControl
et utilisons la commande Register pour exposer cette classe à et au Objective-C Générateur d’interface de Xcode :
[Register("NSFlipSwitch")]
public class NSFlipSwitch : NSControl
Dans les sections suivantes, nous allons examiner le reste du code ci-dessus en détail.
Suivi de l’état du contrôle
Étant donné que notre contrôle personnalisé est un commutateur, nous avons besoin d’un moyen de suivre l’état Activé/Désactivé du commutateur. Nous gérons cela avec le code suivant dans NSFlipSwitch
:
private bool _value = false;
...
public bool Value {
get { return _value; }
set {
// Save value and force a redraw
_value = value;
NeedsDisplay = true;
}
}
Lorsque l’état du commutateur change, nous avons besoin d’un moyen de mettre à jour l’interface utilisateur. Pour ce faire, nous forçons le contrôle à redessiner son interface utilisateur avec NeedsDisplay = true
.
Si notre contrôle nécessitait plus qu’un seul état On/Off (par exemple, un commutateur multi-états avec 3 positions), nous aurions pu utiliser une énumération pour suivre l’état. Pour notre exemple, un simple bool fera l’opération.
Nous avons également ajouté une méthode d’assistance pour échanger l’état du commutateur entre Activé et Désactivé :
private void FlipSwitchState() {
// Update state
Value = !Value;
}
Plus tard, nous développerons cette classe d’assistance pour informer l’appelant lorsque l’état des commutateurs a changé.
Dessin de l’interface du contrôle
Nous allons utiliser des routines de dessin Core Graphic pour dessiner l’interface utilisateur de notre contrôle personnalisé au moment de l’exécution. Avant de pouvoir effectuer cette opération, nous devons activer les couches pour notre contrôle. Nous le faisons avec la méthode privée suivante :
private void Initialize() {
this.WantsLayer = true;
this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
}
Cette méthode est appelée à partir de chacun des constructeurs du contrôle pour s’assurer que le contrôle est correctement configuré. Par exemple :
public NSFlipSwitch (IntPtr handle) : base (handle)
{
// Init
Initialize();
}
Ensuite, nous devons remplacer la DrawRect
méthode et ajouter les routines Core Graphic pour dessiner le contrôle :
public override void DrawRect (CGRect dirtyRect)
{
base.DrawRect (dirtyRect);
// Use Core Graphic routines to draw our UI
...
}
Nous allons ajuster la représentation visuelle du contrôle lorsque son état change (par exemple, passer de Activé à Désactivé). Chaque fois que l’état change, nous pouvons utiliser la NeedsDisplay = true
commande pour forcer le contrôle à redessiner pour le nouvel état.
Réponse à l’entrée utilisateur
Il existe deux façons de base d’ajouter une entrée utilisateur à notre contrôle personnalisé : remplacer les routines de gestion de la souris ou les reconnaissancesde mouvements. La méthode que nous utilisons sera basée sur les fonctionnalités requises par notre contrôle.
Important
Pour tout contrôle personnalisé que vous créez, vous devez utiliser des méthodes de remplacementoudes modules de reconnaissance de mouvement, mais pas les deux en même temps, car ils peuvent entrer en conflit l’un avec l’autre.
Gestion des entrées utilisateur avec des méthodes de remplacement
Les objets qui héritent de NSControl
(ou NSView
) ont plusieurs méthodes de remplacement pour gérer l’entrée de souris ou de clavier. Pour notre exemple de contrôle, nous voulons basculer l’état du commutateur entre Activé et Désactivé lorsque l’utilisateur clique sur le contrôle avec le bouton gauche de la souris. Nous pouvons ajouter les méthodes de remplacement suivantes à la NSFlipSwitch
classe pour gérer cela :
#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
Dans le code ci-dessus, nous appelons la FlipSwitchState
méthode (définie ci-dessus) pour retourner l’état Activé/Désactivé du commutateur dans la MouseDown
méthode. Cela force également le contrôle à être redessiné pour refléter l’état actuel.
Gestion des entrées utilisateur avec des reconnaissances de mouvements
Si vous le souhaitez, vous pouvez utiliser des reconnaissances de mouvement pour gérer l’utilisateur qui interagit avec le contrôle. Supprimez les remplacements ajoutés ci-dessus, modifiez la Initialize
méthode et faites-la ressembler à ce qui suit :
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);
}
Ici, nous créons une nouvelle NSClickGestureRecognizer
méthode et nous appelons notre FlipSwitchState
méthode pour modifier l’état du commutateur lorsque l’utilisateur clique dessus avec le bouton gauche de la souris. La AddGestureRecognizer (click)
méthode ajoute le gesture recognizer au contrôle.
Là encore, la méthode que nous utilisons dépend de ce que nous essayons d’accomplir avec notre contrôle personnalisé. Si nous avons besoin d’un accès de bas niveau à l’interaction utilisateur, utilisez les méthodes de remplacement. Si nous avons besoin de fonctionnalités prédéfinies, telles que des clics de souris, utilisez Les reconnaissances de mouvement.
Réponse aux événements de changement d’état
Lorsque l’utilisateur modifie l’état de notre contrôle personnalisé, nous avons besoin d’un moyen de répondre au changement d’état dans le code (par exemple, en effectuant une action lorsque vous cliquez sur un bouton personnalisé).
Pour fournir cette fonctionnalité, modifiez la NSFlipSwitch
classe et ajoutez le code suivant :
#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
Ensuite, modifiez la FlipSwitchState
méthode et faites-la ressembler à ce qui suit :
private void FlipSwitchState() {
// Update state
Value = !Value;
RaiseValueChanged ();
}
Tout d’abord, nous fournissons un ValueChanged
événement auquel nous pouvons ajouter un gestionnaire dans le code C# afin d’effectuer une action lorsque l’utilisateur modifie l’état du commutateur.
Deuxièmement, parce que notre contrôle personnalisé hérite de NSControl
, il a automatiquement une action qui peut être affectée dans le Générateur d’interface de Xcode. Pour appeler cette action lorsque l’état change, nous utilisons le code suivant :
if (this.Action !=null)
NSApplication.SharedApplication.SendAction (this.Action, this.Target, this);
Tout d’abord, nous case activée pour voir si une action a été affectée au contrôle. Ensuite, nous appelons l’action si elle a été définie.
Utilisation du contrôle personnalisé
Avec notre contrôle personnalisé entièrement défini, nous pouvons l’ajouter à l’interface utilisateur de notre application Xamarin.Mac à l’aide du code C# ou dans le générateur d’interface de Xcode.
Pour ajouter le contrôle à l’aide du Générateur d’interface, commencez par effectuer une propre build du projet Xamarin.Mac, puis double-cliquez sur le fichier pour l’ouvrir Main.storyboard
dans le Générateur d’interface pour le modifier :
Ensuite, faites glisser un Custom View
dans la conception de l’interface utilisateur :
Avec l’affichage personnalisé toujours sélectionné, basculez vers l’inspecteur d’identité et remplacez la classe de la vue par NSFlipSwitch
:
Basculez vers l’Éditeur Assistant et créez une prise pour le contrôle personnalisé (en veillant à le lier dans le ViewController.h
fichier et non dans le .m
fichier) :
Enregistrez vos modifications, revenez à Visual Studio pour Mac et autorisez la synchronisation des modifications. Modifiez le ViewController.cs
fichier et faites en sorte que la ViewDidLoad
méthode ressemble à ce qui suit :
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);
};
}
Ici, nous répondons à l’événement ValueChanged
que nous avons défini ci-dessus sur la NSFlipSwitch
classe et écrivons la valeur actuelle lorsque l’utilisateur clique sur le contrôle.
Si vous le souhaitez, nous pouvons revenir au Générateur d’interface et définir une action sur le contrôle :
Là encore, modifiez le ViewController.cs
fichier et ajoutez la méthode suivante :
partial void OptionTwoFlipped (Foundation.NSObject sender) {
// Display the state of the option switch
Console.WriteLine("Option Two: {0}", OptionTwo.Value);
}
Important
Vous devez utiliser l’événement ou définir une action dans le Générateur d’interface, mais vous ne devez pas utiliser les deux méthodes en même temps ou elles peuvent entrer en conflit l’une avec l’autre.
Résumé
Cet article a examiné en détail la création d’un contrôle d’interface utilisateur personnalisé réutilisable dans une application Xamarin.Mac. Nous avons vu comment dessiner l’interface utilisateur des contrôles personnalisés, les deux main façons de répondre aux entrées de la souris et de l’utilisateur et comment exposer le nouveau contrôle à Actions dans le Générateur d’interface de Xcode.