Udostępnij za pośrednictwem


Okna dialogowe na platformie Xamarin.Mac

Podczas pracy z językami C# i .NET w aplikacji platformy Xamarin.Mac masz dostęp do tych samych okien dialogowych i modalnych, które wykonuje deweloper pracujący w Objective-C środowisku Xcode. Ponieważ platforma Xamarin.Mac integruje się bezpośrednio z programem Xcode, możesz użyć narzędzia Interface Builder środowiska Xcode, aby utworzyć i obsługiwać modalny system Windows (lub opcjonalnie utworzyć je bezpośrednio w kodzie języka C#).

Okno dialogowe jest wyświetlane w odpowiedzi na akcję użytkownika i zazwyczaj zapewnia sposób, w jaki użytkownicy mogą ukończyć akcję. Okno dialogowe wymaga odpowiedzi od użytkownika, zanim będzie można go zamknąć.

System Windows może być używany w stanie Modeless (na przykład edytor tekstu, który może mieć wiele dokumentów otwieranych jednocześnie) lub modalny (na przykład okno dialogowe Eksportowanie, które musi zostać odrzucone, zanim aplikacja będzie mogła kontynuować).

Otwarte okno dialogowe

W tym artykule omówimy podstawy pracy z oknami dialogowymi i modalnymi systemami Windows 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 okien dialogowych

Zostanie wyświetlone okno dialogowe w odpowiedzi na akcję użytkownika (na przykład zapisanie pliku) i umożliwia użytkownikom ukończenie tej akcji. Okno dialogowe wymaga odpowiedzi od użytkownika, zanim będzie można go zamknąć.

Według Apple istnieją trzy sposoby prezentowania okna dialogowego:

  • Modalny dokument — modalne okno dialogowe dokumentu uniemożliwia użytkownikowi wykonywanie niczego innego w danym dokumencie, dopóki nie zostanie ono odrzucone.
  • Modalne aplikacje — modalne okno dialogowe aplikacji uniemożliwia użytkownikowi interakcję z aplikacją, dopóki nie zostanie odrzucona.
  • Modeless Okno dialogowe bez moderowania umożliwia użytkownikom zmianę ustawień w oknie dialogowym podczas interakcji z oknem dokumentu.

Dowolny standard NSWindow może być używany jako dostosowane okno dialogowe, wyświetlając go modalnie:

Przykładowe okno modalne

Arkusze okien dialogowych modalnych dokumentu

Arkusz to modalne okno dialogowe, które jest dołączone do danego okna dokumentu, co uniemożliwia użytkownikom interakcję z oknem, dopóki nie odrzuci okna dialogowego. Arkusz jest dołączony do okna, z którego się pojawia i tylko jeden arkusz może być otwarty dla okna w dowolnym momencie.

Przykładowy arkusz modalny

Preferencje systemu Windows

Okno preferencji to okno bez moderowania zawierające ustawienia aplikacji, które użytkownik zmienia rzadko. Preferencje systemu Windows często zawierają pasek narzędzi, który umożliwia użytkownikowi przełączanie się między różnymi grupami ustawień:

Przykładowe okno preferencji

Otwieranie okna dialogowego

Otwarte okno dialogowe zapewnia użytkownikom spójny sposób znajdowania i otwierania elementu w aplikacji:

Otwarte okno dialogowe

System macOS udostępnia standardowe okna dialogowe Ustawienia wydruku i strony, które aplikacja może wyświetlać, aby użytkownicy mogli korzystać ze spójnego środowiska drukowania w każdej używanej aplikacji.

Okno dialogowe Drukowanie może być wyświetlane jako wolne przestawne okno dialogowe:

Okno dialogowe drukowania

Można go również wyświetlić jako arkusz:

Arkusz wydruku

Okno dialogowe Ustawienia strony może być wyświetlane jako wolne przestawne okno dialogowe:

Okno dialogowe konfiguracji strony

Można go również wyświetlić jako arkusz:

Arkusz konfiguracji strony

Okna dialogowe Zapisywania

Okno dialogowe Zapisywanie zapewnia użytkownikom spójny sposób zapisywania elementu w aplikacji. Okno dialogowe Zapisywanie ma dwa stany: Minimalny (znany również jako Zwinięty):

Okno dialogowe zapisywania

I stan Rozwinięty:

Rozwinięte okno dialogowe zapisywania

Okno dialogowe Minimalne zapisywanie może być również wyświetlane jako arkusz:

Minimalny arkusz zapisywania

Jak można rozwinąć okno dialogowe Zapisywanie:

Rozszerzony arkusz zapisywania

Aby uzyskać więcej informacji, zobacz sekcję Dialogs (Okna dialogowe) wytycznych dotyczących interfejsu człowieka systemu OS X firmy Apple

Dodawanie modalnego okna do projektu

Oprócz głównego okna dokumentu aplikacja Xamarin.Mac może wymagać wyświetlenia innych typów okien dla użytkownika, takich jak Preferencje lub Panele inspektorów.

Aby dodać nowe okno, wykonaj następujące czynności:

  1. W Eksplorator rozwiązań otwórz Main.storyboard plik do edycji w narzędziu Interface Builder programu Xcode.

  2. Przeciągnij nowy kontroler widoku do powierzchni projektowej:

    Wybieranie kontrolera widoku z biblioteki

  3. W inspektorze tożsamości wprowadź CustomDialogController nazwę klasy:

    Ustawianie nazwy klasy na CustomDialogController.

  4. Wróć do Visual Studio dla komputerów Mac, zezwól na synchronizację z programem Xcode i utwórz CustomDialogController.h plik.

  5. Wróć do środowiska Xcode i zaprojektuj interfejs:

    Projektowanie interfejsu użytkownika w środowisku Xcode

  6. Utwórz modalny segue z okna głównego aplikacji do nowego kontrolera widoku, przeciągając kontrolkę z elementu interfejsu użytkownika, który otworzy okno dialogowe do okna dialogowego. Przypisz identyfikator ModalSegue:

    Modalny segue

  7. Podłączanie wszystkich akcji i placówek:

    Konfigurowanie akcji

  8. Zapisz zmiany i wróć do Visual Studio dla komputerów Mac, aby przeprowadzić synchronizację z programem Xcode.

CustomDialogController.cs Utwórz plik w następujący sposób:

using System;
using Foundation;
using AppKit;

namespace MacDialog
{
    public partial class CustomDialogController : NSViewController
    {
        #region Private Variables
        private string _dialogTitle = "Title";
        private string _dialogDescription = "Description";
        private NSViewController _presentor;
        #endregion

        #region Computed Properties
        public string DialogTitle {
            get { return _dialogTitle; }
            set { _dialogTitle = value; }
        }

        public string DialogDescription {
            get { return _dialogDescription; }
            set { _dialogDescription = value; }
        }

        public NSViewController Presentor {
            get { return _presentor; }
            set { _presentor = value; }
        }
        #endregion

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

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

            // Set initial title and description
            Title.StringValue = DialogTitle;
            Description.StringValue = DialogDescription;
        }
        #endregion

        #region Private Methods
        private void CloseDialog() {
            Presentor.DismissViewController (this);
        }
        #endregion

        #region Custom Actions
        partial void AcceptDialog (Foundation.NSObject sender) {
            RaiseDialogAccepted();
            CloseDialog();
        }

        partial void CancelDialog (Foundation.NSObject sender) {
            RaiseDialogCanceled();
            CloseDialog();
        }
        #endregion

        #region Events
        public EventHandler DialogAccepted;

        internal void RaiseDialogAccepted() {
            if (this.DialogAccepted != null)
                this.DialogAccepted (this, EventArgs.Empty);
        }

        public EventHandler DialogCanceled;

        internal void RaiseDialogCanceled() {
            if (this.DialogCanceled != null)
                this.DialogCanceled (this, EventArgs.Empty);
        }
        #endregion
    }
}

Ten kod uwidacznia kilka właściwości, aby ustawić tytuł i opis okna dialogowego oraz kilka zdarzeń reagujące na anulowanie lub zaakceptowanie okna dialogowego.

Następnie zmodyfikuj ViewController.cs plik, zastąpij metodę PrepareForSegue i ustaw ją tak:

public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
    base.PrepareForSegue (segue, sender);

    // Take action based on the segue name
    switch (segue.Identifier) {
    case "ModalSegue":
        var dialog = segue.DestinationController as CustomDialogController;
        dialog.DialogTitle = "MacDialog";
        dialog.DialogDescription = "This is a sample dialog.";
        dialog.DialogAccepted += (s, e) => {
            Console.WriteLine ("Dialog accepted");
            DismissViewController (dialog);
        };
        dialog.Presentor = this;
        break;
    }
}

Ten kod inicjuje segue zdefiniowany w narzędziu Interface Builder programu Xcode do naszego okna dialogowego i konfiguruje tytuł i opis. Obsługuje również wybór, który użytkownik wykonuje w oknie dialogowym.

Możemy uruchomić naszą aplikację i wyświetlić okno dialogowe niestandardowe:

Przykładowe okno dialogowe

Aby uzyskać więcej informacji na temat korzystania z systemu Windows w aplikacji platformy Xamarin.Mac, zobacz dokumentację dotyczącą pracy z systemem Windows .

Tworzenie arkusza niestandardowego

Arkusz to modalne okno dialogowe, które jest dołączone do danego okna dokumentu, co uniemożliwia użytkownikom interakcję z oknem, dopóki nie odrzuci okna dialogowego. Arkusz jest dołączony do okna, z którego się pojawia i tylko jeden arkusz może być otwarty dla okna w dowolnym momencie.

Aby utworzyć arkusz niestandardowy na platformie Xamarin.Mac, wykonajmy następujące czynności:

  1. W Eksplorator rozwiązań otwórz Main.storyboard plik do edycji w narzędziu Interface Builder programu Xcode.

  2. Przeciągnij nowy kontroler widoku do powierzchni projektowej:

    Wybieranie kontrolera widoku z biblioteki

  3. Projektowanie interfejsu użytkownika:

    Projekt interfejsu użytkownika

  4. Utwórz segue arkusza z okna głównego do nowego kontrolera widoku:

    Wybieranie typu segue arkusza

  5. W inspektorze tożsamości nazwij klasę SheetViewControllerkontrolera widoku:

    Ustawianie nazwy klasy na SheetViewController.

  6. Zdefiniuj wszystkie wymagane punkty i akcje:

    Definiowanie wymaganych gniazd i akcji

  7. Zapisz zmiany i wróć do Visual Studio dla komputerów Mac, aby przeprowadzić synchronizację.

Następnie zmodyfikuj SheetViewController.cs plik i utwórz go w następujący sposób:

using System;
using Foundation;
using AppKit;

namespace MacDialog
{
    public partial class SheetViewController : NSViewController
    {
        #region Private Variables
        private string _userName = "";
        private string _password = "";
        private NSViewController _presentor;
        #endregion

        #region Computed Properties
        public string UserName {
            get { return _userName; }
            set { _userName = value; }
        }

        public string Password {
            get { return _password;}
            set { _password = value;}
        }

        public NSViewController Presentor {
            get { return _presentor; }
            set { _presentor = value; }
        }
        #endregion

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

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

            // Set initial values
            NameField.StringValue = UserName;
            PasswordField.StringValue = Password;

            // Wireup events
            NameField.Changed += (sender, e) => {
                UserName = NameField.StringValue;
            };
            PasswordField.Changed += (sender, e) => {
                Password = PasswordField.StringValue;
            };
        }
        #endregion

        #region Private Methods
        private void CloseSheet() {
            Presentor.DismissViewController (this);
        }
        #endregion

        #region Custom Actions
        partial void AcceptSheet (Foundation.NSObject sender) {
            RaiseSheetAccepted();
            CloseSheet();
        }

        partial void CancelSheet (Foundation.NSObject sender) {
            RaiseSheetCanceled();
            CloseSheet();
        }
        #endregion

        #region Events
        public EventHandler SheetAccepted;

        internal void RaiseSheetAccepted() {
            if (this.SheetAccepted != null)
                this.SheetAccepted (this, EventArgs.Empty);
        }

        public EventHandler SheetCanceled;

        internal void RaiseSheetCanceled() {
            if (this.SheetCanceled != null)
                this.SheetCanceled (this, EventArgs.Empty);
        }
        #endregion
    }
}

Następnie zmodyfikuj plik, zmodyfikuj ViewController.cs metodę PrepareForSegue i ustaw ją tak:

public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
    base.PrepareForSegue (segue, sender);

    // Take action based on the segue name
    switch (segue.Identifier) {
    case "ModalSegue":
        var dialog = segue.DestinationController as CustomDialogController;
        dialog.DialogTitle = "MacDialog";
        dialog.DialogDescription = "This is a sample dialog.";
        dialog.DialogAccepted += (s, e) => {
            Console.WriteLine ("Dialog accepted");
            DismissViewController (dialog);
        };
        dialog.Presentor = this;
        break;
    case "SheetSegue":
        var sheet = segue.DestinationController as SheetViewController;
        sheet.SheetAccepted += (s, e) => {
            Console.WriteLine ("User Name: {0} Password: {1}", sheet.UserName, sheet.Password);
        };
        sheet.Presentor = this;
        break;
    }
}

Jeśli uruchomimy aplikację i otworzymy arkusz, zostanie on dołączony do okna:

Przykładowy arkusz

Okno dialogowe Tworzenie preferencji

Zanim ułożymy widok preferencji w narzędziu Interface Builder, musimy dodać niestandardowy typ segue, aby obsłużyć przełączanie preferencji. Dodaj nową klasę do projektu i wywołaj ją ReplaceViewSeque. Zmodyfikuj klasę i ustaw ją tak, jakby wyglądała następująco:

using System;
using AppKit;
using Foundation;

namespace MacWindows
{
    [Register("ReplaceViewSeque")]
    public class ReplaceViewSeque : NSStoryboardSegue
    {
        #region Constructors
        public ReplaceViewSeque() {

        }

        public ReplaceViewSeque (string identifier, NSObject sourceController, NSObject destinationController) : base(identifier,sourceController,destinationController) {

        }

        public ReplaceViewSeque (IntPtr handle) : base(handle) {
        }

        public ReplaceViewSeque (NSObjectFlag x) : base(x) {
        }
        #endregion

        #region Override Methods
        public override void Perform ()
        {
            // Cast the source and destination controllers
            var source = SourceController as NSViewController;
            var destination = DestinationController as NSViewController;

            // Is there a source?
            if (source == null) {
                // No, get the current key window
                var window = NSApplication.SharedApplication.KeyWindow;

                // Swap the controllers
                window.ContentViewController = destination;

                // Release memory
                window.ContentViewController?.RemoveFromParentViewController ();
            } else {
                // Swap the controllers
                source.View.Window.ContentViewController = destination;

                // Release memory
                source.RemoveFromParentViewController ();
            }

        }
        #endregion

    }

}

Po utworzeniu niestandardowego segue możemy dodać nowe okno w narzędziu Interface Builder programu Xcode, aby obsłużyć nasze preferencje.

Aby dodać nowe okno, wykonaj następujące czynności:

  1. W Eksplorator rozwiązań otwórz Main.storyboard plik do edycji w narzędziu Interface Builder programu Xcode.

  2. Przeciągnij nowy kontroler okna na powierzchnię projektową:

    Wybieranie kontrolera okna z biblioteki

  3. Rozmieść okno w pobliżu projektanta paska menu:

    Dodawanie nowego okna

  4. Utwórz kopie dołączonego kontrolera widoku, ponieważ w widoku preferencji będą znajdować się karty:

    Dodawanie wymaganych kontrolerów widoku

  5. Przeciągnij nowy kontroler paska narzędzi z biblioteki:

    Wybieranie kontrolera paska narzędzi z biblioteki

  6. I upuść go na oknie na powierzchni projektowej:

    Dodawanie nowego kontrolera paska narzędzi

  7. Układ projektu paska narzędzi:

    Układ paska narzędzi

  8. Kliknij i przeciągnij z każdego przycisku paska narzędzi do widoków utworzonych powyżej. Wybierz niestandardowy typ segue:

    Ustawianie niestandardowego typu segue.

  9. Wybierz nowy segue i ustaw klasę na :ReplaceViewSegue

    Ustawianie klasy segue

  10. W Projektancie paska menu na powierzchni projektowej z menu aplikacji wybierz pozycję Preferencje..., kliknij i przeciągnij do okna Preferencje, aby utworzyć segue Show:

    Ustawianie typu segue przez przeciągnięcie preferencji do okna Preferencje.

  11. Zapisz zmiany i wróć do Visual Studio dla komputerów Mac, aby przeprowadzić synchronizację.

Jeśli uruchomimy kod i wybierzemy pozycję Preferencje... z menu Aplikacji, zostanie wyświetlone okno:

Przykładowe okno preferencji z wyświetlonym wyrazem Profil.

Aby uzyskać więcej informacji na temat pracy z systemami Windows i Paskami narzędzi, zobacz dokumentację dotyczącą okien i pasków narzędzi.

Zapisywanie i ładowanie preferencji

W typowej aplikacji systemu macOS, gdy użytkownik wprowadza zmiany w dowolnej preferencji użytkownika aplikacji, te zmiany są zapisywane automatycznie. Najprostszym sposobem obsługi tej funkcji w aplikacji platformy Xamarin.Mac jest utworzenie jednej klasy do zarządzania wszystkimi preferencjami użytkownika i udostępniania jej całego systemu.

Najpierw dodaj nową AppPreferences klasę do projektu i dziedzicz z NSObjectklasy . Preferencje zostaną zaprojektowane tak, aby korzystały z powiązania danych i kodowania klucz-wartość, dzięki czemu proces tworzenia i obsługi formularzy preferencji będzie znacznie prostszy. Ponieważ preferencje będą składać się z niewielkiej ilości prostych typów danych, użyj wbudowanej funkcji NSUserDefaults do przechowywania i pobierania wartości.

Zmodyfikuj AppPreferences.cs plik i utwórz go w następujący sposób:

using System;
using Foundation;
using AppKit;

namespace SourceWriter
{
    [Register("AppPreferences")]
    public class AppPreferences : NSObject
    {
        #region Computed Properties
        [Export("DefaultLanguage")]
        public int DefaultLanguage {
            get {
                var value = LoadInt ("DefaultLanguage", 0);
                return value;
            }
            set {
                WillChangeValue ("DefaultLanguage");
                SaveInt ("DefaultLanguage", value, true);
                DidChangeValue ("DefaultLanguage");
            }
        }

        [Export("SmartLinks")]
        public bool SmartLinks {
            get { return LoadBool ("SmartLinks", true); }
            set {
                WillChangeValue ("SmartLinks");
                SaveBool ("SmartLinks", value, true);
                DidChangeValue ("SmartLinks");
            }
        }

        // Define any other required user preferences in the same fashion
        ...

        [Export("EditorBackgroundColor")]
        public NSColor EditorBackgroundColor {
            get { return LoadColor("EditorBackgroundColor", NSColor.White); }
            set {
                WillChangeValue ("EditorBackgroundColor");
                SaveColor ("EditorBackgroundColor", value, true);
                DidChangeValue ("EditorBackgroundColor");
            }
        }
        #endregion

        #region Constructors
        public AppPreferences ()
        {
        }
        #endregion

        #region Public Methods
        public int LoadInt(string key, int defaultValue) {
            // Attempt to read int
            var number = NSUserDefaults.StandardUserDefaults.IntForKey(key);

            // Take action based on value
            if (number == null) {
                return defaultValue;
            } else {
                return (int)number;
            }
        }

        public void SaveInt(string key, int value, bool sync) {
            NSUserDefaults.StandardUserDefaults.SetInt(value, key);
            if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
        }

        public bool LoadBool(string key, bool defaultValue) {
            // Attempt to read int
            var value = NSUserDefaults.StandardUserDefaults.BoolForKey(key);

            // Take action based on value
            if (value == null) {
                return defaultValue;
            } else {
                return value;
            }
        }

        public void SaveBool(string key, bool value, bool sync) {
            NSUserDefaults.StandardUserDefaults.SetBool(value, key);
            if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
        }

        public string NSColorToHexString(NSColor color, bool withAlpha) {
            //Break color into pieces
            nfloat red=0, green=0, blue=0, alpha=0;
            color.GetRgba (out red, out green, out blue, out alpha);

            // Adjust to byte
            alpha *= 255;
            red *= 255;
            green *= 255;
            blue *= 255;

            //With the alpha value?
            if (withAlpha) {
                return String.Format ("#{0:X2}{1:X2}{2:X2}{3:X2}", (int)alpha, (int)red, (int)green, (int)blue);
            } else {
                return String.Format ("#{0:X2}{1:X2}{2:X2}", (int)red, (int)green, (int)blue);
            }
        }

        public NSColor NSColorFromHexString (string hexValue)
        {
            var colorString = hexValue.Replace ("#", "");
            float red, green, blue, alpha;

            // Convert color based on length
            switch (colorString.Length) {
            case 3 : // #RGB
                red = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(0, 1)), 16) / 255f;
                green = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(1, 1)), 16) / 255f;
                blue = Convert.ToInt32(string.Format("{0}{0}", colorString.Substring(2, 1)), 16) / 255f;
                return NSColor.FromRgba(red, green, blue, 1.0f);
            case 6 : // #RRGGBB
                red = Convert.ToInt32(colorString.Substring(0, 2), 16) / 255f;
                green = Convert.ToInt32(colorString.Substring(2, 2), 16) / 255f;
                blue = Convert.ToInt32(colorString.Substring(4, 2), 16) / 255f;
                return NSColor.FromRgba(red, green, blue, 1.0f);
            case 8 : // #AARRGGBB
                alpha = Convert.ToInt32(colorString.Substring(0, 2), 16) / 255f;
                red = Convert.ToInt32(colorString.Substring(2, 2), 16) / 255f;
                green = Convert.ToInt32(colorString.Substring(4, 2), 16) / 255f;
                blue = Convert.ToInt32(colorString.Substring(6, 2), 16) / 255f;
                return NSColor.FromRgba(red, green, blue, alpha);
            default :
                throw new ArgumentOutOfRangeException(string.Format("Invalid color value '{0}'. It should be a hex value of the form #RBG, #RRGGBB or #AARRGGBB", hexValue));
            }
        }

        public NSColor LoadColor(string key, NSColor defaultValue) {

            // Attempt to read color
            var hex = NSUserDefaults.StandardUserDefaults.StringForKey(key);

            // Take action based on value
            if (hex == null) {
                return defaultValue;
            } else {
                return NSColorFromHexString (hex);
            }
        }

        public void SaveColor(string key, NSColor color, bool sync) {
            // Save to default
            NSUserDefaults.StandardUserDefaults.SetString(NSColorToHexString(color,true), key);
            if (sync) NSUserDefaults.StandardUserDefaults.Synchronize ();
        }
        #endregion
    }
}

Ta klasa zawiera kilka procedur pomocników, takich jak SaveInt, LoadInt, SaveColor, LoadColoritp., aby ułatwić pracę NSUserDefaults . Ponadto, ponieważ NSUserDefaults nie ma wbudowanego sposobu obsługi NSColors, NSColorToHexString metody i NSColorFromHexString są używane do konwertowania kolorów na internetowe ciągi szesnastkowe (#RRGGBBAA gdzie AA jest przezroczystość alfa), które można łatwo przechowywać i pobierać.

AppDelegate.cs W pliku utwórz wystąpienie obiektu AppPreferences, które będzie używane dla całej aplikacji:

using AppKit;
using Foundation;
using System.IO;
using System;

namespace SourceWriter
{
    [Register ("AppDelegate")]
    public class AppDelegate : NSApplicationDelegate
    {
        #region Computed Properties
        public int NewWindowNumber { get; set;} = -1;

        public AppPreferences Preferences { get; set; } = new AppPreferences();
        #endregion

        #region Constructors
        public AppDelegate ()
        {
        }
        #endregion

        ...

Preferencje okablowania do widoków preferencji

Następnie połącz klasę preferencji z elementami interfejsu użytkownika w oknie preferencji i widokami utworzonymi powyżej. W narzędziu Interface Builder wybierz kontroler widoku preferencji i przejdź do inspektora tożsamości, utwórz niestandardową klasę dla kontrolera:

Inspektor tożsamości

Wróć do Visual Studio dla komputerów Mac, aby zsynchronizować zmiany i otworzyć nowo utworzoną klasę do edycji. Ustaw klasę tak, jakby wyglądała następująco:

using System;
using Foundation;
using AppKit;

namespace SourceWriter
{
    public partial class EditorPrefsController : NSViewController
    {
        #region Application Access
        public static AppDelegate App {
            get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
        }
        #endregion

        #region Computed Properties
        [Export("Preferences")]
        public AppPreferences Preferences {
            get { return App.Preferences; }
        }
        #endregion

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

Zwróć uwagę, że ta klasa wykonała w tym miejscu dwie rzeczy: Najpierw istnieje właściwość pomocnika App , aby ułatwić dostęp do elementu AppDelegate . Po drugie właściwość Preferences uwidacznia globalną klasę AppPreferences na potrzeby powiązania danych z dowolnymi kontrolkami interfejsu użytkownika umieszczonymi w tym widoku.

Następnie kliknij dwukrotnie plik Scenorysu, aby otworzyć go ponownie w narzędziu Interface Builder (i zobacz zmiany wprowadzone powyżej). Przeciągnij wszystkie kontrolki interfejsu użytkownika wymagane do skompilowania interfejsu preferencji do widoku. Dla każdej kontrolki przejdź do inspektora powiązań i powiąż z poszczególnymi właściwościami klasy AppPreference:

Inspektor powiązań

Powtórz powyższe kroki dla wszystkich wymaganych paneli (kontrolerów widoku) i właściwości preferencji.

Stosowanie zmian preferencji do wszystkich otwartych okien

Jak wspomniano powyżej, w typowej aplikacji systemu macOS, gdy użytkownik wprowadza zmiany w dowolnej preferencji użytkownika aplikacji, te zmiany są zapisywane automatycznie i stosowane do wszystkich okien, które użytkownik mógł otworzyć w aplikacji.

Staranne planowanie i projektowanie preferencji i okien aplikacji umożliwi bezproblemowe i przejrzyste wykonanie tego procesu użytkownikowi końcowemu przy minimalnym liczbie prac związanych z kodowaniem.

W przypadku dowolnego okna, które będzie korzystać z preferencji aplikacji, dodaj następującą właściwość pomocnika do kontrolera widoku zawartości, aby ułatwić dostęp do naszej aplikacjiDelegate :

#region Application Access
public static AppDelegate App {
    get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion

Następnie dodaj klasę, aby skonfigurować zawartość lub zachowanie na podstawie preferencji użytkownika:

public void ConfigureEditor() {

    // General Preferences
    TextEditor.AutomaticLinkDetectionEnabled = App.Preferences.SmartLinks;
    TextEditor.AutomaticQuoteSubstitutionEnabled = App.Preferences.SmartQuotes;
    ...

}

Należy wywołać metodę konfiguracji po pierwszym otwarciu okna, aby upewnić się, że jest ona zgodna z preferencjami użytkownika:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    // Configure editor from user preferences
    ConfigureEditor ();
    ...
}

Następnie zmodyfikuj AppDelegate.cs plik i dodaj następującą metodę, aby zastosować wszelkie zmiany preferencji do wszystkich otwartych okien:

public void UpdateWindowPreferences() {

    // Process all open windows
    for(int n=0; n<NSApplication.SharedApplication.Windows.Length; ++n) {
        var content = NSApplication.SharedApplication.Windows[n].ContentViewController as ViewController;
        if (content != null ) {
            // Reformat all text
            content.ConfigureEditor ();
        }
    }

}

Następnie dodaj klasę PreferenceWindowDelegate do projektu i utwórz ją w następujący sposób:

using System;
using AppKit;
using System.IO;
using Foundation;

namespace SourceWriter
{
    public class PreferenceWindowDelegate : NSWindowDelegate
    {
        #region Application Access
        public static AppDelegate App {
            get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
        }
        #endregion

        #region Computed Properties
        public NSWindow Window { get; set;}
        #endregion

        #region constructors
        public PreferenceWindowDelegate (NSWindow window)
        {
            // Initialize
            this.Window = window;

        }
        #endregion

        #region Override Methods
        public override bool WindowShouldClose (Foundation.NSObject sender)
        {

            // Apply any changes to open windows
            App.UpdateWindowPreferences();

            return true;
        }
        #endregion
    }
}

Spowoduje to wysłanie wszelkich zmian preferencji do wszystkich otwartych systemu Windows po zamknięciu okna preferencji.

Na koniec zmodyfikuj kontroler okna preferencji i dodaj pełnomocnika utworzonego powyżej:

using System;
using Foundation;
using AppKit;

namespace SourceWriter
{
    public partial class PreferenceWindowController : NSWindowController
    {
        #region Constructors
        public PreferenceWindowController (IntPtr handle) : base (handle)
        {
        }
        #endregion

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

            // Initialize
            Window.Delegate = new PreferenceWindowDelegate(Window);
            Toolbar.SelectedItemIdentifier = "General";
        }
        #endregion
    }
}

Po wprowadzeniu wszystkich tych zmian, jeśli użytkownik edytuje preferencje aplikacji i zamyka okno preferencji, zmiany zostaną zastosowane do wszystkich otwartych systemu Windows:

Przykładowe okno preferencji wyświetlane z kilkoma innymi otwartymi oknami.

Otwarte okno dialogowe

Otwarte okno dialogowe zapewnia użytkownikom spójny sposób znajdowania i otwierania elementu w aplikacji. Aby wyświetlić otwarte okno dialogowe w aplikacji Xamarin.Mac, użyj następującego kodu:

var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = true;
dlg.CanChooseDirectories = false;
dlg.AllowedFileTypes = new string[] { "txt", "html", "md", "css" };

if (dlg.RunModal () == 1) {
    // Nab the first file
    var url = dlg.Urls [0];

    if (url != null) {
        var path = url.Path;

        // Create a new window to hold the text
        var newWindowController = new MainWindowController ();
        newWindowController.Window.MakeKeyAndOrderFront (this);

        // Load the text into the window
        var window = newWindowController.Window as MainWindow;
        window.Text = File.ReadAllText(path);
        window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
        window.RepresentedUrl = url;

    }
}

W powyższym kodzie otwieramy nowe okno dokumentu, aby wyświetlić zawartość pliku. Musisz zastąpić ten kod funkcją wymaganą przez aplikację.

Podczas pracy z programem NSOpenPanelsą dostępne następujące właściwości:

  • CanChooseFiles — jeśli true użytkownik może wybrać pliki.
  • CanChooseDirectories — jeśli true użytkownik może wybrać katalogi.
  • AllowsMultipleSelection — jeśli true użytkownik może wybrać więcej niż jeden plik jednocześnie.
  • ResolveAliases — jeśli true wybierzesz i alias, rozpozna go w ścieżce oryginalnego pliku.
  • AllowedFileTypes — to tablica ciągów typów plików, które użytkownik może wybrać jako rozszerzenie lub utI. Wartość domyślna to null, która umożliwia otwieranie dowolnego pliku.

Metoda RunModal () wyświetla otwarte okno dialogowe i zezwala użytkownikowi na wybieranie plików lub katalogów (zgodnie z właściwościami) i zwraca 1 , jeśli użytkownik kliknie przycisk Otwórz .

Otwarte okno dialogowe zwraca wybrane pliki lub katalogi użytkownika jako tablicę adresów URL we URL właściwości .

Jeśli uruchomimy program i wybierzemy element Otwórz... z menu Plik , zostanie wyświetlony następujący komunikat:

Otwarte okno dialogowe

Okna dialogowe Ustawienia wydruku i strony

System macOS udostępnia standardowe okna dialogowe Ustawienia wydruku i strony, które aplikacja może wyświetlać, aby użytkownicy mogli korzystać ze spójnego środowiska drukowania w każdej używanej aplikacji.

W poniższym kodzie zostanie wyświetlone standardowe okno dialogowe drukowania:

public bool ShowPrintAsSheet { get; set;} = true;
...

[Export ("showPrinter:")]
void ShowDocument (NSObject sender) {
    var dlg = new NSPrintPanel();

    // Display the print dialog as dialog box
    if (ShowPrintAsSheet) {
        dlg.BeginSheet(new NSPrintInfo(),this,this,null,new IntPtr());
    } else {
        if (dlg.RunModalWithPrintInfo(new NSPrintInfo()) == 1) {
            var alert = new NSAlert () {
                AlertStyle = NSAlertStyle.Critical,
                InformativeText = "We need to print the document here...",
                MessageText = "Print Document",
            };
            alert.RunModal ();
        }
    }
}

Jeśli ustawimy ShowPrintAsSheet właściwość na false, uruchom aplikację i wyświetlimy okno dialogowe drukowania, zostaną wyświetlone następujące elementy:

Okno dialogowe drukowania

W przypadku ustawienia ShowPrintAsSheet właściwości na true, uruchom aplikację i wyświetl okno dialogowe drukowania, zostaną wyświetlone następujące elementy:

Arkusz wydruku

W poniższym kodzie zostanie wyświetlone okno dialogowe Układ strony:

[Export ("showLayout:")]
void ShowLayout (NSObject sender) {
    var dlg = new NSPageLayout();

    // Display the print dialog as dialog box
    if (ShowPrintAsSheet) {
        dlg.BeginSheet (new NSPrintInfo (), this);
    } else {
        if (dlg.RunModal () == 1) {
            var alert = new NSAlert () {
                AlertStyle = NSAlertStyle.Critical,
                InformativeText = "We need to print the document here...",
                MessageText = "Print Document",
            };
            alert.RunModal ();
        }
    }
}

Jeśli ustawimy ShowPrintAsSheet właściwość na false, uruchom aplikację i wyświetlimy okno dialogowe układu wydruku, zostaną wyświetlone następujące elementy:

Okno dialogowe konfiguracji strony

Jeśli dla właściwości ustaw ShowPrintAsSheet wartość true, uruchom aplikację i wyświetl okno dialogowe układu wydruku, zostaną wyświetlone następujące elementy:

Arkusz konfiguracji strony

Aby uzyskać więcej informacji na temat pracy z oknami dialogowymi Konfiguracji wydruku i strony, zobacz dokumentację NSPrintPanel i NSPageLayout firmy Apple.

Okno dialogowe Zapisywanie

Okno dialogowe Zapisywanie zapewnia użytkownikom spójny sposób zapisywania elementu w aplikacji.

W poniższym kodzie zostanie wyświetlone standardowe okno dialogowe Zapisywanie:

public bool ShowSaveAsSheet { get; set;} = true;
...

[Export("saveDocumentAs:")]
void ShowSaveAs (NSObject sender)
{
    var dlg = new NSSavePanel ();
    dlg.Title = "Save Text File";
    dlg.AllowedFileTypes = new string[] { "txt", "html", "md", "css" };

    if (ShowSaveAsSheet) {
        dlg.BeginSheet(mainWindowController.Window,(result) => {
            var alert = new NSAlert () {
                AlertStyle = NSAlertStyle.Critical,
                InformativeText = "We need to save the document here...",
                MessageText = "Save Document",
            };
            alert.RunModal ();
        });
    } else {
        if (dlg.RunModal () == 1) {
            var alert = new NSAlert () {
                AlertStyle = NSAlertStyle.Critical,
                InformativeText = "We need to save the document here...",
                MessageText = "Save Document",
            };
            alert.RunModal ();
        }
    }

}

Właściwość AllowedFileTypes jest tablicą ciągów typów plików, które użytkownik może wybrać, aby zapisać plik jako. Typ pliku można określić jako rozszerzenie lub utI. Wartość domyślna to null, która umożliwia używanie dowolnego typu pliku.

Jeśli ustawimy ShowSaveAsSheet właściwość na false, uruchom aplikację i wybierz pozycję Zapisz jako... z menu Plik zostaną wyświetlone następujące opcje:

Okno dialogowe zapisywania

Użytkownik może rozwinąć okno dialogowe:

Rozwinięte okno dialogowe zapisywania

Jeśli ustawimy ShowSaveAsSheet właściwość na true, uruchom aplikację i wybierz pozycję Zapisz jako... z menu Plik zostaną wyświetlone następujące opcje:

Arkusz zapisywania

Użytkownik może rozwinąć okno dialogowe:

Rozszerzony arkusz zapisywania

Aby uzyskać więcej informacji na temat pracy z dialogiem zapisywania, zobacz dokumentację NSSavePanel firmy Apple.

Podsumowanie

W tym artykule szczegółowo przedstawiono pracę z modalnymi oknami, arkuszami i standardowymi oknami dialogowymi systemu w aplikacji Xamarin.Mac. Zobaczyliśmy różne typy i zastosowania modalnych okien, arkuszy i okien dialogowych, jak tworzyć i obsługiwać modalne okna i arkusze w konstruktorze interfejsów Xcode oraz jak pracować z modalnymi oknami, arkuszami i oknami dialogowymi w kodzie języka C#.