Progettazione dell'interfaccia utente senza estensione storyboard/.xib in Xamarin.Mac

Questo articolo illustra la creazione di un'interfaccia utente di un'applicazione Xamarin.Mac direttamente dal codice C#, senza file con estensione storyboard, file xib o Interface Builder.

Panoramica

Quando si lavora con C# e .NET in un'applicazione Xamarin.Mac, è possibile accedere agli stessi elementi e strumenti dell'interfaccia utente usati da uno sviluppatore in Objective-C e Xcode . In genere, quando si crea un'applicazione Xamarin.Mac, si userà Interface Builder di Xcode con file con estensione storyboard o xib per creare e gestire l'interfaccia utente dell'applicazione.

È anche possibile creare un'interfaccia utente dell'applicazione Xamarin.Mac direttamente nel codice C#. In questo articolo verranno illustrate le nozioni di base sulla creazione di interfacce utente ed elementi dell'interfaccia utente nel codice C#.

The Visual Studio for Mac code editor

Passaggio di una finestra per l'uso del codice

Quando si crea una nuova applicazione Xamarin.Mac Cocoa, si ottiene una finestra vuota standard per impostazione predefinita. Questa finestra è definita in un file Main.storyboard (o tradizionalmente un file MainWindow.xib) incluso automaticamente nel progetto. Questo include anche un file di ViewController.cs che gestisce la visualizzazione principale dell'app (o di nuovo tradizionalmente un MainWindow.cs e un file MainWindowController.cs ).

Per passare a una finestra Xibless per un'applicazione, eseguire le operazioni seguenti:

  1. Aprire l'applicazione che si vuole interrompere l'uso .storyboard o i file con estensione xib per definire l'interfaccia utente in Visual Studio per Mac.

  2. Nel riquadro della soluzione fare clic con il pulsante destro del mouse sul file Main.storyboard o MainWindow.xib e scegliere Rimuovi:

    Removing the main storyboard or window

  3. Nella finestra di dialogo Rimuovi fare clic sul pulsante Elimina per rimuovere completamente lo storyboard o xib dal progetto:

    Confirming the deletion

A questo punto è necessario modificare il file di MainWindow.cs per definire il layout della finestra e modificare il file ViewController.cs o MainWindowController.cs per creare un'istanza della MainWindow classe perché non si usa più il file con estensione storyboard o xib.

Le app Xamarin.Mac moderne che usano storyboard per l'interfaccia utente potrebbero non includere automaticamente i file di MainWindow.cs, ViewController.cs o MainWindowController.cs . Come richiesto, è sufficiente aggiungere una nuova classe C# vuota al progetto (Aggiungi>nuovo file...>General>Empty Class) e denominarlo come il file mancante.

Definizione della finestra nel codice

Modificare quindi il file MainWindow.cs e impostarlo come segue:

using System;
using Foundation;
using AppKit;
using CoreGraphics;

namespace MacXibless
{
    public partial class MainWindow : NSWindow
    {
        #region Private Variables
        private int NumberOfTimesClicked = 0;
        #endregion

        #region Computed Properties
        public NSButton ClickMeButton { get; set;}
        public NSTextField ClickMeLabel { get ; set;}
        #endregion

        #region Constructors
        public MainWindow (IntPtr handle) : base (handle)
        {
        }

        [Export ("initWithCoder:")]
        public MainWindow (NSCoder coder) : base (coder)
        {
        }

        public MainWindow(CGRect contentRect, NSWindowStyle aStyle, NSBackingStore bufferingType, bool deferCreation): base (contentRect, aStyle,bufferingType,deferCreation) {
            // Define the user interface of the window here
            Title = "Window From Code";

            // Create the content view for the window and make it fill the window
            ContentView = new NSView (Frame);

            // Add UI elements to window
            ClickMeButton = new NSButton (new CGRect (10, Frame.Height-70, 100, 30)){
                AutoresizingMask = NSViewResizingMask.MinYMargin
            };
            ContentView.AddSubview (ClickMeButton);

            ClickMeLabel = new NSTextField (new CGRect (120, Frame.Height - 65, Frame.Width - 130, 20)) {
                BackgroundColor = NSColor.Clear,
                TextColor = NSColor.Black,
                Editable = false,
                Bezeled = false,
                AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.MinYMargin,
                StringValue = "Button has not been clicked yet."
            };
            ContentView.AddSubview (ClickMeLabel);
        }
        #endregion

        #region Override Methods
        public override void AwakeFromNib ()
        {
            base.AwakeFromNib ();

            // Wireup events
            ClickMeButton.Activated += (sender, e) => {
                // Update count
                ClickMeLabel.StringValue = (++NumberOfTimesClicked == 1) ? "Button clicked one time." : string.Format("Button clicked {0} times.",NumberOfTimesClicked);
            };
        }
        #endregion

    }
}

Verranno ora illustrati alcuni degli elementi chiave.

In primo luogo, sono state aggiunte alcune proprietà calcolate che funzioneranno come outlet (come se la finestra fosse stata creata in un file con estensione storyboard o xib):

public NSButton ClickMeButton { get; set;}
public NSTextField ClickMeLabel { get ; set;}

Questi ci daranno l'accesso agli elementi dell'interfaccia utente che verranno visualizzati nella finestra. Poiché la finestra non viene gonfiata da un file con estensione storyboard o xib, è necessario un modo per crearne un'istanza (come vedremo più avanti nella MainWindowController classe). Ecco cosa fa questo nuovo metodo del costruttore:

public MainWindow(CGRect contentRect, NSWindowStyle aStyle, NSBackingStore bufferingType, bool deferCreation): base (contentRect, aStyle,bufferingType,deferCreation) {
    ...
}

In questo modo si progetta il layout della finestra e si inseriscono gli elementi dell'interfaccia utente necessari per creare l'interfaccia utente necessaria. Prima di poter aggiungere elementi dell'interfaccia utente a una finestra, è necessaria una visualizzazione contenuto per contenere gli elementi:

ContentView = new NSView (Frame);

Verrà creata una visualizzazione contenuto che riempirà la finestra. Ora aggiungiamo il primo elemento dell'interfaccia utente, un NSButtonoggetto , alla finestra:

ClickMeButton = new NSButton (new CGRect (10, Frame.Height-70, 100, 30)){
    AutoresizingMask = NSViewResizingMask.MinYMargin
};
ContentView.AddSubview (ClickMeButton);

La prima cosa da notare qui è che, a differenza di iOS, macOS usa la notazione matematica per definire il sistema di coordinate della finestra. Il punto di origine si trova quindi nell'angolo inferiore sinistro della finestra, con valori crescenti verso destra e verso l'angolo superiore destro della finestra. Quando si crea il nuovo NSButtonoggetto , questo viene preso in considerazione durante la definizione della posizione e delle dimensioni sullo schermo.

La AutoresizingMask = NSViewResizingMask.MinYMargin proprietà indica al pulsante che si vuole che rimanga nella stessa posizione dalla parte superiore della finestra quando la finestra viene ridimensionata verticalmente. Anche in questo caso, è necessario perché (0,0) si trova in basso a sinistra della finestra.

Infine, il ContentView.AddSubview (ClickMeButton) metodo aggiunge l'oggetto NSButton alla visualizzazione contenuto in modo che venga visualizzato sullo schermo quando l'applicazione viene eseguita e la finestra visualizzata.

Successivamente viene aggiunta un'etichetta alla finestra che visualizzerà il numero di volte in cui NSButton è stato fatto clic su :

ClickMeLabel = new NSTextField (new CGRect (120, Frame.Height - 65, Frame.Width - 130, 20)) {
    BackgroundColor = NSColor.Clear,
    TextColor = NSColor.Black,
    Editable = false,
    Bezeled = false,
    AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.MinYMargin,
    StringValue = "Button has not been clicked yet."
};
ContentView.AddSubview (ClickMeLabel);

Poiché macOS non ha un elemento dell'interfaccia utente label specifico, è stato aggiunto un elemento appositamente in stile non modificabile NSTextField per fungere da etichetta. Proprio come il pulsante precedente, le dimensioni e la posizione prendono in considerazione che (0,0) si trova in basso a sinistra della finestra. La AutoresizingMask = NSViewResizingMask.WidthSizable | NSViewResizingMask.MinYMargin proprietà usa l'operatore o per combinare due NSViewResizingMask funzionalità. In questo modo l'etichetta rimarrà nella stessa posizione dalla parte superiore della finestra quando la finestra viene ridimensionata verticalmente e compattata e aumenta in larghezza man mano che la finestra viene ridimensionata orizzontalmente.

Anche in questo caso, il ContentView.AddSubview (ClickMeLabel) metodo aggiunge l'oggetto NSTextField alla visualizzazione contenuto in modo che venga visualizzato sullo schermo quando l'applicazione viene eseguita e la finestra aperta.

Regolazione del controller finestra

Poiché la progettazione di MainWindow non viene più caricata da un file con estensione storyboard o xib, è necessario apportare alcune modifiche al controller della finestra. Modificare il file MainWindowController.cs e impostarlo come segue:

using System;

using Foundation;
using AppKit;
using CoreGraphics;

namespace MacXibless
{
    public partial class MainWindowController : NSWindowController
    {
        public MainWindowController (IntPtr handle) : base (handle)
        {
        }

        [Export ("initWithCoder:")]
        public MainWindowController (NSCoder coder) : base (coder)
        {
        }

        public MainWindowController () : base ("MainWindow")
        {
            // Construct the window from code here
            CGRect contentRect = new CGRect (0, 0, 1000, 500);
            base.Window = new MainWindow(contentRect, (NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Resizable), NSBackingStore.Buffered, false);

            // Simulate Awaking from Nib
            Window.AwakeFromNib ();
        }

        public override void AwakeFromNib ()
        {
            base.AwakeFromNib ();
        }

        public new MainWindow Window {
            get { return (MainWindow)base.Window; }
        }
    }
}

Esaminiamo gli elementi chiave di questa modifica.

Prima di tutto, definiamo una nuova istanza della MainWindow classe e la assegniamo alla proprietà del controller della finestra di Window base:

CGRect contentRect = new CGRect (0, 0, 1000, 500);
base.Window = new MainWindow(contentRect, (NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Resizable), NSBackingStore.Buffered, false);

Viene definita la posizione della finestra dello schermo con un oggetto CGRect. Proprio come il sistema di coordinate della finestra, lo schermo definisce (0,0) come angolo inferiore sinistro. Successivamente, definiamo lo stile della finestra usando l'operatore Or per combinare due o più NSWindowStyle funzionalità:

... (NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Miniaturizable | NSWindowStyle.Resizable) ...

Sono disponibili le funzionalità seguenti NSWindowStyle :

  • Senza bordi: la finestra non avrà alcun bordo.
  • Titolo : la finestra avrà una barra del titolo.
  • Closable : la finestra ha un pulsante Chiudi e può essere chiusa.
  • Miniatureizable - La finestra ha un pulsante Di Miniatureize e può essere ridotta a icona.
  • Ridimensionabile : la finestra avrà un pulsante Ridimensiona e sarà ridimensionabile.
  • Utilità : la finestra è una finestra stile utilità (pannello).
  • DocModal : se la finestra è un pannello, sarà modale documento anziché modale di sistema.
  • NonactivatingPanel : se la finestra è un pannello, non verrà visualizzata la finestra principale.
  • TexturedBackground : la finestra avrà uno sfondo con trama.
  • Non ridimensionato : la finestra non verrà ridimensionata.
  • UnifiedTitleAndToolbar : le aree del titolo e della barra degli strumenti della finestra verranno unite in join.
  • Hud : la finestra verrà visualizzata come pannello di visualizzazione a testa.
  • FullScreenWindow : la finestra può entrare in modalità schermo intero.
  • FullSizeContentView : la visualizzazione contenuto della finestra si trova dietro il titolo e l'area della barra degli strumenti.

Le ultime due proprietà definiscono il tipo di buffering per la finestra e se il disegno della finestra verrà posticipato. Per altre informazioni su NSWindows, vedere la documentazione introduttiva di Apple per Windows .

Infine, poiché la finestra non viene gonfiata da un file con estensione storyboard o xib, è necessario simularla nel MainWindowController.cs chiamando il metodo windows AwakeFromNib :

Window.AwakeFromNib ();

In questo modo è possibile eseguire il codice sulla finestra esattamente come una finestra standard caricata da un file con estensione storyboard o xib.

Visualizzazione della finestra

Con il file con estensione storyboard o xib rimosso e i file MainWindow.cs e MainWindowController.cs modificati, si userà la finestra esattamente come qualsiasi finestra normale creata in Interface Builder di Xcode con un file con estensione xib.

Di seguito verrà creata una nuova istanza della finestra e del relativo controller e verrà visualizzata la finestra sullo schermo:

private MainWindowController mainWindowController;
...

mainWindowController = new MainWindowController ();
mainWindowController.Window.MakeKeyAndOrderFront (this);

A questo punto, se l'applicazione viene eseguita e il pulsante ha fatto clic un paio di volte, verrà visualizzato quanto segue:

An example app run

Aggiunta di una finestra solo codice

Se si vuole aggiungere solo un codice, la finestra xibless a un'applicazione Xamarin.Mac esistente, fare clic con il pulsante destro del mouse sul progetto nel riquadro della soluzione e scegliere Aggiungi>nuovo file... Nella finestra di dialogo Nuovo file scegliere Xamarin.Mac Cocoa Window with Controller (Finestra Cocoa Xamarin.Mac>con controller), come illustrato di seguito:

Adding a new window controller

Proprio come in precedenza, elimineremo il file con estensione storyboard predefinito o xib dal progetto (in questo caso SecondWindow.xib) e seguire i passaggi descritti nella sezione Passaggio a una finestra per usare il codice precedente per coprire la definizione della finestra nel codice.

Aggiunta di un elemento dell'interfaccia utente a una finestra nel codice

Se una finestra è stata creata nel codice o caricata da un file con estensione storyboard o xib, potrebbe essere necessario aggiungere un elemento dell'interfaccia utente a una finestra dal codice. Ad esempio:

var ClickMeButton = new NSButton (new CGRect (10, 10, 100, 30)){
    AutoresizingMask = NSViewResizingMask.MinYMargin
};
MyWindow.ContentView.AddSubview (ClickMeButton);

Il codice precedente crea un nuovo NSButton oggetto e lo aggiunge all'istanza della finestra per la MyWindow visualizzazione. Fondamentalmente qualsiasi elemento dell'interfaccia utente che può essere definito in Interface Builder di Xcode in un file con estensione storyboard o xib può essere creato nel codice e visualizzato in una finestra.

Definizione della barra dei menu nel codice

A causa delle limitazioni correnti in Xamarin.Mac, non è consigliabile creare il codice della barraNSMenuBar dei menu dell'applicazione Xamarin.Mac, ma continuare a usare il file Main.storyboard o MainMenu.xib per definirlo. Detto questo, è possibile aggiungere e rimuovere menu e voci di menu nel codice C#.

Ad esempio, modificare il file AppDelegate.cs e impostare il DidFinishLaunching metodo come segue:

public override void DidFinishLaunching (NSNotification notification)
{
    mainWindowController = new MainWindowController ();
    mainWindowController.Window.MakeKeyAndOrderFront (this);

    // Create a Status Bar Menu
    NSStatusBar statusBar = NSStatusBar.SystemStatusBar;

    var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable);
    item.Title = "Phrases";
    item.HighlightMode = true;
    item.Menu = new NSMenu ("Phrases");

    var address = new NSMenuItem ("Address");
    address.Activated += (sender, e) => {
        Console.WriteLine("Address Selected");
    };
    item.Menu.AddItem (address);

    var date = new NSMenuItem ("Date");
    date.Activated += (sender, e) => {
        Console.WriteLine("Date Selected");
    };
    item.Menu.AddItem (date);

    var greeting = new NSMenuItem ("Greeting");
    greeting.Activated += (sender, e) => {
        Console.WriteLine("Greetings Selected");
    };
    item.Menu.AddItem (greeting);

    var signature = new NSMenuItem ("Signature");
    signature.Activated += (sender, e) => {
        Console.WriteLine("Signature Selected");
    };
    item.Menu.AddItem (signature);
}

Il codice precedente crea un menu Barra di stato e lo visualizza all'avvio dell'applicazione. Per altre informazioni sull'uso dei menu, vedere la documentazione relativa ai menu .

Riepilogo

Questo articolo ha esaminato in dettaglio la creazione di un'interfaccia utente di un'applicazione Xamarin.Mac nel codice C# anziché l'uso di Interface Builder di Xcode con file con estensione storyboard o xib.