Udostępnij za pośrednictwem


Tworzenie kontrolek niestandardowych na platformie Xamarin.Mac

Podczas pracy z językami C# i .NET w aplikacji platformy Xamarin.Mac masz dostęp do tych samych kontrolek użytkowników, które deweloper pracujący w Objective-Cusługach , Swift i Xcode . Ponieważ platforma Xamarin.Mac integruje się bezpośrednio z narzędziem Xcode, możesz użyć narzędzia Xcode Interface Builder do tworzenia i obsługi kontrolek użytkownika (lub opcjonalnie tworzenia ich bezpośrednio w kodzie języka C#).

Podczas gdy system macOS udostępnia wiele wbudowanych kontrolek użytkownika, może być konieczne utworzenie niestandardowej kontrolki w celu zapewnienia funkcji, które nie zostały dostarczone poza polem lub dopasowania niestandardowego motywu interfejsu użytkownika (na przykład interfejsu gry).

Przykład niestandardowej kontrolki interfejsu użytkownika

W tym artykule omówimy podstawy tworzenia niestandardowej kontrolki interfejsu użytkownika wielokrotnego użytku w aplikacji platformy Xamarin.Mac. Zdecydowanie zaleca się, aby najpierw zapoznać się z artykułem Hello, Mac , w szczególności wprowadzenie do narzędzi Xcode i Interface Builder i Outlet and Actions , ponieważ obejmuje ona kluczowe pojęcia i techniki, których będziemy używać w tym artykule.

Warto zapoznać się również z sekcją Uwidacznianie klas/ metod Objective-C języka C# w dokumencie Xamarin.Mac Internals . Register Objaśnienie poleceń i Export używanych do podłączania klas języka C# do Objective-C obiektów i elementów interfejsu użytkownika.

Wprowadzenie do kontrolek niestandardowych

Jak wspomniano powyżej, może wystąpić potrzeba utworzenia niestandardowej kontrolki interfejsu użytkownika wielokrotnego użytku w celu zapewnienia unikatowych funkcji interfejsu użytkownika dla interfejsu użytkownika aplikacji Xamarin.Mac lub utworzenia niestandardowego motywu interfejsu użytkownika (takiego jak interfejs gry).

W takich sytuacjach można łatwo dziedziczyć NSControl i tworzyć niestandardowe narzędzie, które można dodać do interfejsu użytkownika aplikacji za pomocą kodu języka C# lub za pomocą narzędzia Interface Builder środowiska Xcode. Dziedziczenie z NSControl kontrolki niestandardowej spowoduje automatyczne posiadanie wszystkich standardowych funkcji wbudowanych kontrolki interfejsu użytkownika (takich jak NSButton).

Jeśli niestandardowa kontrolka interfejsu użytkownika wyświetla tylko informacje (takie jak niestandardowe narzędzie do tworzenia wykresów i grafiki), możesz chcieć dziedziczyć z NSView zamiast NSControl.

Niezależnie od tego, która klasa bazowa jest używana, podstawowe kroki tworzenia kontrolki niestandardowej są takie same.

W tym artykule utworzysz niestandardowy składnik przełącznika przerzucania, który udostępnia unikatowy motyw interfejsu użytkownika i przykład tworzenia w pełni funkcjonalnej niestandardowej kontrolki interfejsu użytkownika.

Kompilowanie kontrolki niestandardowej

Ponieważ kontrolka niestandardowa, która tworzymy, będzie odpowiadać na dane wejściowe użytkownika (kliknięcie lewym przyciskiem myszy), będziemy dziedziczyć z NSControlelementu . W ten sposób nasza kontrolka niestandardowa będzie automatycznie zawierać wszystkie standardowe funkcje, które wbudowane kontrolki interfejsu użytkownika mają i odpowiadają za pomocą standardowej kontrolki systemu macOS.

W Visual Studio dla komputerów Mac otwórz projekt Xamarin.Mac, dla którego chcesz utworzyć niestandardową kontrolkę interfejsu użytkownika (lub utwórz nowy). Dodaj nową klasę i wywołaj ją NSFlipSwitch:

Dodawanie nowej klasy

Następnie zmodyfikuj klasę NSFlipSwitch.cs i ustaw ją tak:

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

    }
}

Pierwszą rzeczą, aby zauważyć naszą klasę niestandardową w tym, że dziedziczymy NSControl i używamy polecenia Register, aby uwidocznić tę klasę w Objective-C narzędziu Interface Builder i Xcode:

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

W poniższych sekcjach przyjrzymy się szczegółowo pozostałej części powyższego kodu.

Śledzenie stanu kontrolki

Ponieważ nasza kontrolka niestandardowa jest przełącznikiem, potrzebujemy sposobu śledzenia stanu Włączone/Wyłączone przełącznika. Obsługujemy to za pomocą następującego kodu w pliku NSFlipSwitch:

private bool _value = false;
...

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

Gdy stan przełącznika ulegnie zmianie, potrzebujemy sposobu na zaktualizowanie interfejsu użytkownika. Robimy to, zmuszając kontrolkę do ponownego wyrysowania interfejsu użytkownika za pomocą polecenia NeedsDisplay = true.

Jeśli nasza kontrolka wymaga więcej niż jeden stan wł./wył. (na przykład przełącznik wielostanowy z 3 pozycjami), moglibyśmy użyć wyliczenia do śledzenia stanu. W naszym przykładzie zrobi to prosty bool .

Dodaliśmy również metodę pomocnika, aby zamienić stan przełącznika między włączonym i wyłączonym:

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

Później rozszerzymy tę klasę pomocnika, aby poinformować obiekt wywołujący o zmianie stanu przełączników.

Rysowanie interfejsu kontrolki

Użyjemy procedur rysowania grafiki podstawowej, aby narysować interfejs użytkownika kontrolki niestandardowej w czasie wykonywania. Zanim to zrobimy, musimy włączyć warstwy dla naszej kontrolki. Robimy to przy użyciu następującej metody prywatnej:

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

Ta metoda jest wywoływana z każdego konstruktora kontrolki, aby upewnić się, że kontrolka jest prawidłowo skonfigurowana. Na przykład:

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

Następnie musimy zastąpić metodę DrawRect i dodać procedury Core Graphic, aby narysować kontrolkę:

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

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

}

Dostosujemy wizualną reprezentację kontrolki, gdy jego stan zmieni się (na przykład od wł. do wył.). Za każdym razem, gdy stan zmieni się, możemy użyć NeedsDisplay = true polecenia , aby wymusić ponowne rysowanie kontrolki dla nowego stanu.

Odpowiadanie na dane wejściowe użytkownika

Istnieją dwa podstawowe sposoby dodawania danych wejściowych użytkownika do naszej niestandardowej kontrolki: zastępowanie procedur obsługi myszy lub rozpoznawania gestów. Która metoda jest używana, będzie oparta na funkcjonalności wymaganej przez naszą kontrolkę.

Ważne

W przypadku dowolnej utworzonej kontrolki niestandardowej należy użyć metodprzesłonięcia lubrozpoznawania gestów, ale nie obu w tym samym czasie, co mogą powodować konflikt ze sobą.

Obsługa danych wejściowych użytkownika za pomocą metod zastępowania

Obiekty dziedziczone z NSControl (lub NSView) mają kilka metod zastąpienia do obsługi wprowadzania myszy lub klawiatury. W naszym przykładzie kontrolki chcemy przerzucić stan przełącznika między włączonym i wyłączonym , gdy użytkownik kliknie kontrolkę za pomocą lewego przycisku myszy. Aby obsłużyć tę obsługę, możemy dodać następujące metody zastąpienia do NSFlipSwitch klasy:

#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

W powyższym kodzie wywołujemy metodę FlipSwitchState (zdefiniowaną powyżej), aby przerzucić stan Wł./Wył. przełącznika w metodzie MouseDown . Spowoduje to również wymusi ponowne wyrysowanie kontrolki w celu odzwierciedlenia bieżącego stanu.

Obsługa danych wejściowych użytkownika za pomocą funkcji rozpoznawania gestów

Opcjonalnie możesz użyć funkcji rozpoznawania gestów do obsługi interakcji użytkownika z kontrolką. Usuń powyższe przesłonięcia, zmodyfikuj metodę Initialize i ustaw ją tak, jakby wyglądała następująco:

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

W tym miejscu tworzymy nową NSClickGestureRecognizer metodę i wywołujemy metodę FlipSwitchState , aby zmienić stan przełącznika, gdy użytkownik kliknie go za pomocą lewego przycisku myszy. Metoda AddGestureRecognizer (click) dodaje rozpoznawanie gestów do kontrolki.

Ponownie, która metoda, której używamy, zależy od tego, co próbujemy osiągnąć za pomocą naszej niestandardowej kontrolki. Jeśli potrzebujemy dostępu niskiego poziomu do interakcji z użytkownikiem, użyj metod zastąpienia. Jeśli potrzebujemy wstępnie zdefiniowanych funkcji, takich jak kliknięcia myszy, użyj funkcji rozpoznawania gestów.

Reagowanie na zdarzenia zmiany stanu

Gdy użytkownik zmieni stan naszej kontrolki niestandardowej, potrzebujemy sposobu reagowania na zmianę stanu w kodzie (na przykład podczas klikania przycisku niestandardowego).

Aby zapewnić tę funkcję, zmodyfikuj klasę NSFlipSwitch i dodaj następujący kod:

#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

Następnie zmodyfikuj metodę FlipSwitchState i ustaw ją tak:

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

Najpierw udostępniamy ValueChanged zdarzenie, do którego możemy dodać program obsługi w kodzie języka C#, abyśmy mogli wykonać akcję, gdy użytkownik zmieni stan przełącznika.

Po drugie, ponieważ nasza kontrolka niestandardowa dziedziczy z NSControlklasy , automatycznie ma akcję , którą można przypisać w narzędziu Xcode Interface Builder. Aby wywołać tę akcję po zmianie stanu, użyjemy następującego kodu:

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

Najpierw sprawdzamy, czy do kontrolki przypisano akcję . Następnie wywołujemy akcję , jeśli została zdefiniowana.

Używanie kontrolki niestandardowej

Dzięki w pełni zdefiniowanej kontrolce niestandardowej możemy dodać ją do interfejsu użytkownika aplikacji Xamarin.Mac przy użyciu kodu języka C# lub w narzędziu Interface Builder programu Xcode.

Aby dodać kontrolkę przy użyciu narzędzia Interface Builder, najpierw wykonaj czystą kompilację projektu Xamarin.Mac, a następnie kliknij Main.storyboard dwukrotnie plik, aby otworzyć go w narzędziu Interface Builder w celu edycji:

Edytowanie scenorysu w programie Xcode

Następnie przeciągnij element Custom View do projektu interfejsu użytkownika:

Wybieranie widoku niestandardowego z biblioteki

Po wybraniu widoku niestandardowego przejdź do inspektora tożsamości i zmień klasę widoku na NSFlipSwitch:

Ustawianie klasy Widoku

Przejdź do Edytora asystentów i utwórz gniazdo dla kontrolki niestandardowej (pamiętaj o powiązaniu ViewController.h.m go w pliku, a nie pliku):

Konfigurowanie nowego gniazda

Zapisz zmiany, wróć do Visual Studio dla komputerów Mac i zezwól na synchronizację zmian. ViewController.cs Edytuj plik i utwórz metodę ViewDidLoad podobną do następującej:

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

W tym miejscu reagujemy na ValueChanged zdarzenie zdefiniowane powyżej w NSFlipSwitch klasie i zapisujemy bieżącą wartość po kliknięciu kontrolki przez użytkownika.

Opcjonalnie możemy wrócić do konstruktora interfejsu i zdefiniować akcję w kontrolce:

Konfigurowanie nowej akcji

Ponownie zmodyfikuj ViewController.cs plik i dodaj następującą metodę:

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

Ważne

Należy użyć zdarzenia lub zdefiniować akcję w konstruktorze interfejsu, ale nie należy używać obu metod jednocześnie lub mogą powodować konflikt ze sobą.

Podsumowanie

W tym artykule szczegółowo przedstawiono tworzenie niestandardowej kontrolki interfejsu użytkownika wielokrotnego użytku w aplikacji platformy Xamarin.Mac. Zobaczyliśmy, jak narysować niestandardowy interfejs użytkownika kontrolek, czyli dwa główne sposoby reagowania na dane wejściowe myszy i użytkownika oraz sposób uwidaczniania nowej kontrolki na akcje w narzędziu Xcode Interface Builder.