Menu na platformie Xamarin.Mac
W tym artykule opisano pracę z menu w aplikacji platformy Xamarin.Mac. W tym artykule opisano tworzenie i konserwowanie menu oraz elementów menu w programie Xcode i Interface Builder oraz pracę z nimi programowo.
Podczas pracy z językiem C# i platformą .NET w aplikacji platformy Xamarin.Mac masz dostęp do tych samych menu cocoa, w Objective-C których pracuje deweloper i czy program Xcode. Ponieważ platforma Xamarin.Mac integruje się bezpośrednio z narzędziem Xcode, możesz użyć narzędzia Interface Builder środowiska Xcode do tworzenia i obsługi pasków menu, menu i elementów menu (lub opcjonalnie tworzenia ich bezpośrednio w kodzie języka C#).
Menu są integralną częścią środowiska użytkownika aplikacji dla komputerów Mac i często pojawiają się w różnych częściach interfejsu użytkownika:
- Pasek menu aplikacji — to jest menu główne wyświetlane w górnej części ekranu dla każdej aplikacji dla komputerów Mac.
- Menu kontekstowe — są one wyświetlane, gdy użytkownik kliknie prawym przyciskiem myszy lub kliknie element w oknie.
- Pasek stanu — jest to obszar po prawej stronie paska menu aplikacji, który jest wyświetlany w górnej części ekranu (po lewej stronie zegara paska menu) i rośnie do lewej strony, gdy do niego są dodawane elementy.
- Menu Dock — menu dla każdej aplikacji w doku, które jest wyświetlane, gdy użytkownik kliknie prawym przyciskiem myszy lub kliknie ikonę aplikacji lub gdy użytkownik kliknie ikonę po lewej stronie i przytrzymuje przycisk myszy w dół.
- Przycisk wyskakujące i listy rozwijane — przycisk podręczny wyświetla wybrany element i przedstawia listę opcji do wybrania po kliknięciu przez użytkownika. Lista ściągania to typ wyskakującego przycisku, który jest zwykle używany do wybierania poleceń specyficznych dla kontekstu bieżącego zadania. Oba mogą być wyświetlane w dowolnym miejscu w oknie.
W tym artykule omówimy podstawy pracy z paskami menu, menu i elementami menu platformy Cocoa 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.
Możesz również zapoznać się z sekcją Uwidacznianie klas/metod Objective-C języka C# w dokumencie Xamarin.Mac Internals , a także objaśnienie Register
atrybutów i Export
używanych do podłączania klas języka C# do Objective-C obiektów i elementów interfejsu użytkownika.
Pasek menu aplikacji
W przeciwieństwie do aplikacji działających w systemie operacyjnym Windows, w których każde okno może mieć dołączony własny pasek menu, każda aplikacja uruchomiona w systemie macOS ma jeden pasek menu uruchamiany w górnej części ekranu używanego dla każdego okna w tej aplikacji:
Elementy na tym pasku menu są aktywowane lub dezaktywowane na podstawie bieżącego kontekstu lub stanu aplikacji i interfejsu użytkownika w danym momencie. Na przykład: jeśli użytkownik wybierze pole tekstowe, elementy w menu Edycja zostaną włączone, takie jak Kopiowanie i wycinanie.
Zgodnie z firmą Apple i domyślnie wszystkie aplikacje systemu macOS mają standardowy zestaw menu i elementów menu, które są wyświetlane na pasku menu aplikacji:
- Menu firmy Apple — to menu zapewnia dostęp do elementów szerokiego systemu, które są dostępne dla użytkownika przez cały czas, niezależnie od uruchomionej aplikacji. Nie można modyfikować tych elementów przez dewelopera.
- Menu Aplikacji — to menu wyświetla nazwę aplikacji pogrubioną i pomaga użytkownikowi zidentyfikować aktualnie uruchomioną aplikację. Zawiera on elementy, które mają zastosowanie do aplikacji jako całości, a nie do danego dokumentu lub procesu, takie jak zamykanie aplikacji.
- Menu Plik — elementy używane do tworzenia, otwierania lub zapisywania dokumentów, z którymi działa aplikacja. Jeśli aplikacja nie jest oparta na dokumentach, można zmienić nazwę lub usunąć to menu.
- Menu Edytuj — przechowuje polecenia, takie jak wycinanie, kopiowanie i wklejanie , które są używane do edytowania lub modyfikowania elementów w interfejsie użytkownika aplikacji.
- Menu Format — jeśli aplikacja współdziała z tekstem, to menu przechowuje polecenia w celu dostosowania formatowania tego tekstu.
- Menu Widok — przechowuje polecenia wpływające na sposób wyświetlania zawartości (wyświetlanej) w interfejsie użytkownika aplikacji.
- Menu specyficzne dla aplikacji — są to wszystkie menu specyficzne dla aplikacji (np. menu zakładek dla przeglądarki internetowej). Powinny one być wyświetlane między menu Widok i Okno na pasku.
- Menu okna — zawiera polecenia do pracy z oknami w aplikacji, a także listę bieżących otwartych okien.
- Menu Pomoc — jeśli aplikacja udostępnia pomoc ekranową, menu Pomoc powinno być menu najbardziej prawym przyciskiem myszy na pasku.
Aby uzyskać więcej informacji na temat paska menu aplikacji oraz standardowych menu i elementów menu, zobacz Wytyczne dotyczące interfejsu ludzkiego firmy Apple.
Domyślny pasek menu aplikacji
Za każdym razem, gdy tworzysz nowy projekt platformy Xamarin.Mac, automatycznie otrzymujesz standardowy, domyślny pasek menu aplikacji zawierający typowe elementy, które zwykle będą miały aplikację systemu macOS (zgodnie z opisem w powyższej sekcji). Domyślny pasek menu aplikacji jest zdefiniowany w pliku Main.storyboard (wraz z resztą interfejsu użytkownika aplikacji) w projekcie w okienku rozwiązania:
Kliknij dwukrotnie plik Main.storyboard, aby otworzyć go do edycji w narzędziu Interface Builder programu Xcode i zostanie wyświetlony interfejs edytora menu:
W tym miejscu możemy kliknąć elementy, takie jak otwórz element menu w menu Plik i edytować lub dostosować jego właściwości w Inspektorze atrybutów:
W dalszej części tego artykułu przejdziemy do dodawania, edytowania i usuwania menu i elementów. Na razie chcemy zobaczyć, jakie menu i elementy menu są domyślnie dostępne i jak zostały automatycznie uwidocznione dla kodu za pośrednictwem zestawu wstępnie zdefiniowanych placówek i akcji (aby uzyskać więcej informacji, zobacz naszą dokumentację placówek i akcji ).
Jeśli na przykład klikniemy inspektora Połączenie ion dla elementu menu Otwórz, zobaczymy, że jest on automatycznie podłączony do openDocument:
akcji:
Jeśli wybierzesz pozycję Pierwszy obiekt odpowiadający w hierarchii interfejsu i przewiniesz w dół w inspektorze Połączenie ion i zobaczysz definicję openDocument:
akcji dołączonej do elementu menu Otwórz (wraz z kilkoma innymi akcjami domyślnymi dla aplikacji, które są i nie są automatycznie połączone do kontrolek):
Dlaczego to jest ważne? W następnej sekcji zobaczysz, jak te akcje zdefiniowane automatycznie współpracują z innymi elementami interfejsu użytkownika cocoa, aby automatycznie włączać i wyłączać elementy menu, a także zapewniać wbudowane funkcje dla elementów.
Później będziemy używać tych wbudowanych akcji, aby włączać i wyłączać elementy z kodu i udostępniać własne funkcje po ich wybraniu.
Wbudowana funkcja menu
Jeśli uruchomiono nowo utworzoną aplikację platformy Xamarin.Mac przed dodaniem dowolnych elementów interfejsu użytkownika lub kodu, zauważysz, że niektóre elementy są automatycznie połączone i włączone (z w pełni funkcją wbudowaną), taką jak element Zamknij w menu Aplikacja :
Inne elementy menu, takie jak wycinanie, kopiowanie i wklejanie , nie są następujące:
Zatrzymajmy aplikację i kliknij dwukrotnie plik Main.storyboard w okienku rozwiązania, aby otworzyć go do edycji w narzędziu Interface Builder programu Xcode. Następnie przeciągnij widok tekstu z biblioteki na kontroler widoku okna w Edytorze interfejsów:
W Edytorze ograniczeń przypnijmy widok tekstowy do krawędzi okna i ustawimy go w miejscu, w którym rośnie i zmniejsza się wraz z oknem, klikając wszystkie cztery czerwone belki we/w górnej części edytora i klikając przycisk Dodaj 4 ograniczenia:
Zapisz zmiany w projekcie interfejsu użytkownika i przełącz Visual Studio dla komputerów Mac, aby zsynchronizować zmiany z projektem Xamarin.Mac. Teraz uruchom aplikację, wpisz jakiś tekst w widoku tekstowym, wybierz go i otwórz menu Edytuj :
Zwróć uwagę, że elementy wycinania, kopiowania i wklejania są automatycznie włączone i w pełni funkcjonalne bez konieczności pisania pojedynczego wiersza kodu.
Co się tu dzieje? Pamiętaj wbudowane wstępnie zdefiniowane akcje, które są połączone z domyślnymi elementami menu (jak pokazano powyżej), większość elementów interfejsu użytkownika cocoa, które są częścią systemu macOS mają wbudowane punkty zaczepienia do określonych akcji (takich jak copy:
). Dlatego po dodaniu ich do okna, aktywnego i wybranego odpowiedniego elementu menu lub elementów dołączonych do tej akcji są automatycznie włączone. Jeśli użytkownik wybierze ten element menu, funkcja wbudowana w element interfejsu użytkownika jest wywoływana i wykonywana bez interwencji dewelopera.
Włączanie i wyłączanie menu i elementów
Domyślnie za każdym razem, gdy wystąpi zdarzenie użytkownika, NSMenu
automatycznie włącza i wyłącza każde widoczne menu i element menu na podstawie kontekstu aplikacji. Istnieją trzy sposoby włączania/wyłączania elementu:
- Automatyczne włączanie menu — element menu jest włączony, jeśli
NSMenu
można znaleźć odpowiedni obiekt, który odpowiada na akcję, do którego element jest podłączony. Na przykład widok tekstowy powyżej, który miał wbudowany element zaczepienia docopy:
akcji. - Akcje niestandardowe i validateMenuItem: — w przypadku każdego elementu menu powiązanego z akcją niestandardową okna lub kontrolera widoku można dodać
validateMenuItem:
akcję i ręcznie włączyć lub wyłączyć elementy menu. - Włączanie menu ręcznego — ręcznie ustaw
Enabled
właściwość każdej z nichNSMenuItem
tak, aby włączała lub wyłączała poszczególne elementy w menu osobno.
Aby wybrać system, ustaw AutoEnablesItems
właściwość NSMenu
. true
jest automatyczne (zachowanie domyślne) i false
jest ręczne.
Ważne
Jeśli zdecydujesz się użyć ręcznego włączania menu, żadne z elementów menu, nawet te kontrolowane przez klasy AppKit, takie jak NSTextView
, zostaną automatycznie zaktualizowane. Użytkownik będzie odpowiedzialny za włączenie i wyłączenie wszystkich elementów za pomocą kodu.
Korzystanie z elementu validateMenuItem
Jak wspomniano powyżej, w przypadku każdego elementu menu powiązanego z akcją niestandardową okna lub kontrolera widoku można dodać validateMenuItem:
akcję i ręcznie włączyć lub wyłączyć elementy menu.
W poniższym przykładzie Tag
właściwość będzie używana do decydowania o typie elementu menu, który zostanie włączony/wyłączony przez validateMenuItem:
akcję na podstawie stanu zaznaczonego tekstu w elemencie NSTextView
. Właściwość została ustawiona Tag
w narzędziu Interface Builder dla każdego elementu menu:
I następujący kod dodany do kontrolera widoku:
[Action("validateMenuItem:")]
public bool ValidateMenuItem (NSMenuItem item) {
// Take action based on the menu item type
// (As specified in its Tag)
switch (item.Tag) {
case 1:
// Wrap menu items should only be available if
// a range of text is selected
return (TextEditor.SelectedRange.Length > 0);
case 2:
// Quote menu items should only be available if
// a range is NOT selected.
return (TextEditor.SelectedRange.Length == 0);
}
return true;
}
Po uruchomieniu NSTextView
tego kodu i w obiekcie nie zaznaczono żadnego tekstu , dwa elementy menu zawijania są wyłączone (mimo że są podłączone do akcji na kontrolerze widoku):
Jeśli zaznaczono sekcję tekstu i ponownie otwarte menu, dostępne będą dwa elementy menu zawijania:
Włączanie elementów menu w kodzie i odpowiadanie na nie
Jak widzieliśmy powyżej, po prostu dodając określone elementy interfejsu użytkownika Cocoa do naszego projektu interfejsu użytkownika (na przykład pole tekstowe), kilka domyślnych elementów menu zostanie włączonych i funkcja automatycznie, bez konieczności pisania kodu. Następnie przyjrzyjmy się dodaniu własnego kodu C# do naszego projektu platformy Xamarin.Mac, aby włączyć element menu i zapewnić funkcjonalność po wybraniu go przez użytkownika.
Załóżmy na przykład, że chcemy, aby użytkownik mógł użyć pozycji Otwórz w menu Plik , aby wybrać folder. Ponieważ chcemy, aby ta funkcja działała w całej aplikacji, a nie ogranicza się do elementu give lub UI, dodamy kod do obsługi tego elementu do delegata aplikacji.
W okienku rozwiązania kliknij AppDelegate.CS
dwukrotnie plik, aby otworzyć go do edycji:
Dodaj następujący kod poniżej DidFinishLaunching
metody :
[Export ("openDocument:")]
void OpenDialog (NSObject sender)
{
var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = false;
dlg.CanChooseDirectories = true;
if (dlg.RunModal () == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Informational,
InformativeText = "At this point we should do something with the folder that the user just selected in the Open File Dialog box...",
MessageText = "Folder Selected"
};
alert.RunModal ();
}
}
Teraz uruchomimy aplikację i otwórz menu Plik :
Zwróć uwagę, że element menu Otwórz jest teraz włączony. Jeśli go wybierzemy, zostanie wyświetlone otwarte okno dialogowe:
Jeśli klikniemy przycisk Otwórz , zostanie wyświetlony komunikat alertu:
Wiersz klucza w tym miejscu to [Export ("openDocument:")]
, informuje NSMenu
, że nasza aplikacja AppDelegate ma metodę void OpenDialog (NSObject sender)
, która odpowiada na openDocument:
akcję. Jeśli pamiętasz z powyższego , element menu Otwórz jest automatycznie podłączony do tej akcji domyślnie w narzędziu Interface Builder:
Następnie przyjrzyjmy się tworzeniu własnego menu, elementów menu i akcji oraz reagowaniu na nie w kodzie.
Praca z otwartym menu ostatnio używanym
Domyślnie menu Plik zawiera element Otwórz ostatnio używane , który śledzi ostatnie kilka plików, które użytkownik otworzył w aplikacji. Jeśli tworzysz opartą NSDocument
na platformie Xamarin.Mac aplikację, to menu zostanie automatycznie obsłużone. W przypadku dowolnego innego typu aplikacji Xamarin.Mac będziesz odpowiadać za ręczne zarządzanie tym elementem menu i odpowiadanie na nie.
Aby ręcznie obsłużyć menu Otwórz ostatnio , należy najpierw poinformować go, że nowy plik został otwarty lub zapisany przy użyciu następujących elementów:
// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);
Mimo że aplikacja nie używa NSDocuments
elementu , nadal używasz NSDocumentController
polecenia , aby zachować menu Otwórz ostatnio, wysyłając element NSUrl
z lokalizacją pliku do NoteNewRecentDocumentURL
metody .SharedDocumentController
Następnie należy zastąpić OpenFile
metodę delegata aplikacji, aby otworzyć dowolny plik wybrany przez użytkownika z menu Otwórz ostatnie . Na przykład:
public override bool OpenFile (NSApplication sender, string filename)
{
// Trap all errors
try {
filename = filename.Replace (" ", "%20");
var url = new NSUrl ("file://"+filename);
return OpenFile(url);
} catch {
return false;
}
}
Zwróć, true
jeśli można otworzyć plik, w przeciwnym razie zostanie false
wyświetlone ostrzeżenie wbudowane dla użytkownika, że nie można otworzyć pliku.
Ponieważ nazwa pliku i ścieżka zwrócona z menu Otwórz ostatnie mogą zawierać spację, musimy prawidłowo użyć tego znaku przed utworzeniem NSUrl
znaku lub zostanie wyświetlony błąd. Robimy to za pomocą następującego kodu:
filename = filename.Replace (" ", "%20");
Na koniec utworzymy obiekt NSUrl
wskazujący plik i użyjemy metody pomocniczej w delegatorze aplikacji, aby otworzyć nowe okno i załadować do niego plik:
var url = new NSUrl ("file://"+filename);
return OpenFile(url);
Aby zebrać wszystko razem, przyjrzyjmy się przykładowej implementacji w pliku AppDelegate.cs :
using AppKit;
using Foundation;
using System.IO;
using System;
namespace MacHyperlink
{
[Register ("AppDelegate")]
public class AppDelegate : NSApplicationDelegate
{
#region Computed Properties
public int NewWindowNumber { get; set;} = -1;
#endregion
#region Constructors
public AppDelegate ()
{
}
#endregion
#region Override Methods
public override void DidFinishLaunching (NSNotification notification)
{
// Insert code here to initialize your application
}
public override void WillTerminate (NSNotification notification)
{
// Insert code here to tear down your application
}
public override bool OpenFile (NSApplication sender, string filename)
{
// Trap all errors
try {
filename = filename.Replace (" ", "%20");
var url = new NSUrl ("file://"+filename);
return OpenFile(url);
} catch {
return false;
}
}
#endregion
#region Private Methods
private bool OpenFile(NSUrl url) {
var good = false;
// Trap all errors
try {
var path = url.Path;
// Is the file already open?
for(int n=0; n<NSApplication.SharedApplication.Windows.Length; ++n) {
var content = NSApplication.SharedApplication.Windows[n].ContentViewController as ViewController;
if (content != null && path == content.FilePath) {
// Bring window to front
NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);
return true;
}
}
// Get new window
var storyboard = NSStoryboard.FromName ("Main", null);
var controller = storyboard.InstantiateControllerWithIdentifier ("MainWindow") as NSWindowController;
// Display
controller.ShowWindow(this);
// Load the text into the window
var viewController = controller.Window.ContentViewController as ViewController;
viewController.Text = File.ReadAllText(path);
viewController.SetLanguageFromPath(path);
viewController.View.Window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
viewController.View.Window.RepresentedUrl = url;
// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);
// Make as successful
good = true;
} catch {
// Mark as bad file on error
good = false;
}
// Return results
return good;
}
#endregion
#region actions
[Export ("openDocument:")]
void OpenDialog (NSObject sender)
{
var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = true;
dlg.CanChooseDirectories = false;
if (dlg.RunModal () == 1) {
// Nab the first file
var url = dlg.Urls [0];
if (url != null) {
// Open the document in a new window
OpenFile (url);
}
}
}
#endregion
}
}
Na podstawie wymagań aplikacji możesz nie chcieć, aby użytkownik otworzył ten sam plik w więcej niż jednym oknie w tym samym czasie. W naszej przykładowej aplikacji, jeśli użytkownik wybierze plik, który jest już otwarty (z elementów menu Otwórz ostatnio lub Otwórz), okno zawierające plik zostanie przeniesione do przodu.
W tym celu użyliśmy następującego kodu w naszej metodzie pomocniczej:
var path = url.Path;
// Is the file already open?
for(int n=0; n<NSApplication.SharedApplication.Windows.Length; ++n) {
var content = NSApplication.SharedApplication.Windows[n].ContentViewController as ViewController;
if (content != null && path == content.FilePath) {
// Bring window to front
NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);
return true;
}
}
Zaprojektowaliśmy naszą ViewController
klasę tak, aby przechowywała ścieżkę do pliku we właściwości Path
. Następnie przechodzimy w pętli przez wszystkie aktualnie otwarte okna w aplikacji. Jeśli plik jest już otwarty w jednym z okien, jest on przywieziony do przodu wszystkich innych okien przy użyciu:
NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);
Jeśli nie zostanie znalezione dopasowanie, zostanie otwarte nowe okno z załadowanym plikiem, a plik zostanie zanotowany w menu Otwórz ostatnie :
// Get new window
var storyboard = NSStoryboard.FromName ("Main", null);
var controller = storyboard.InstantiateControllerWithIdentifier ("MainWindow") as NSWindowController;
// Display
controller.ShowWindow(this);
// Load the text into the window
var viewController = controller.Window.ContentViewController as ViewController;
viewController.Text = File.ReadAllText(path);
viewController.SetLanguageFromPath(path);
viewController.View.Window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
viewController.View.Window.RepresentedUrl = url;
// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);
Praca z niestandardowymi akcjami okien
Podobnie jak wbudowane akcje pierwszej odpowiedzi , które są wstępnie połączone ze standardowymi elementami menu, można tworzyć nowe, niestandardowe akcje i połączyć je z elementami menu w narzędziu Interface Builder.
Najpierw zdefiniuj akcję niestandardową na jednym z kontrolerów okien aplikacji. Na przykład:
[Action("defineKeyword:")]
public void defineKeyword (NSObject sender) {
// Preform some action when the menu is selected
Console.WriteLine ("Request to define keyword");
}
Następnie kliknij dwukrotnie plik scenorysu aplikacji w okienku rozwiązania, aby otworzyć go do edycji w narzędziu Xcode Interface Builder. Wybierz pozycję Pierwszy obiekt odpowiadający w obszarze Scena aplikacji, a następnie przejdź do inspektora atrybutów:
+ Kliknij przycisk w dolnej części inspektora atrybutów, aby dodać nową akcję niestandardową:
Nadaj mu taką samą nazwę jak akcja niestandardowa utworzona na kontrolerze okna:
Kliknij kontrolkę i przeciągnij z elementu menu do funkcji Pierwsza osoba odpowiadająca w obszarze Scena aplikacji. Z listy podręcznej wybierz nowo utworzoną akcję (defineKeyword:
w tym przykładzie):
Zapisz zmiany w scenorysie i wróć do Visual Studio dla komputerów Mac, aby zsynchronizować zmiany. Jeśli uruchomisz aplikację, element menu, do którego połączono akcję niestandardową, zostanie automatycznie włączony/wyłączony (na podstawie okna z otwartą akcją) i wybranie elementu menu spowoduje wyzwolenie akcji:
Dodawanie, edytowanie i usuwanie menu
Jak widzieliśmy w poprzednich sekcjach, aplikacja platformy Xamarin.Mac zawiera wstępnie ustawioną liczbę domyślnych menu i elementów menu, na które będą automatycznie aktywowane i odpowiadane określone kontrolki interfejsu użytkownika. Zobaczyliśmy również, jak dodać kod do naszej aplikacji, który będzie również włączać i reagować na te elementy domyślne.
W tej sekcji przyjrzymy się usuwaniu elementów menu, których nie potrzebujemy, reorganizacji menu i dodawania nowych menu, elementów menu i akcji.
Kliknij dwukrotnie plik Main.storyboard w okienku rozwiązania, aby otworzyć go do edycji:
W przypadku naszej konkretnej aplikacji Xamarin.Mac nie będziemy używać domyślnego menu Widok , więc go usuniemy. W hierarchii interfejsu wybierz element menu Widok, który jest częścią paska menu głównego:
Naciśnij klawisz Delete lub backspace, aby usunąć menu. Następnie nie będziemy używać wszystkich elementów w menu Format i chcemy przenieść elementy, z których będziemy korzystać z menu podrzędnych. W hierarchii interfejsu wybierz następujące elementy menu:
Przeciągnij elementy w menu nadrzędnym z menu podrzędnego, w którym znajdują się one obecnie:
Menu powinno teraz wyglądać następująco:
Następnie przeciągnijmy menu podrzędne Text z menu Format i umieśćmy je na pasku menu głównym między menu Format i Okno :
Wróćmy do menu Format i usuńmy element menu podrzędnego Czcionka. Następnie wybierz menu Format i zmień jego nazwę na "Czcionka":
Następnie utwórzmy niestandardowe menu wstępnie zdefiniowanych fraz, które automatycznie zostaną dołączone do tekstu w widoku tekstowym po ich wybraniu. W polu wyszukiwania u dołu inspektora biblioteki wpisz "menu". Ułatwi to znajdowanie i pracę ze wszystkimi elementami interfejsu użytkownika menu:
Teraz wykonajmy następujące czynności, aby utworzyć nasze menu:
Przeciągnij element menu z Inspektora biblioteki na pasek menu między menu Tekst i Okno :
Zmień nazwę elementu "Frazy":
Następnie przeciągnij menu z inspektora biblioteki:
Upuść polecenie Menu w nowo utworzonym elemencie menu i zmień jego nazwę na "Frazy":
Teraz zmieńmy nazwę trzech domyślnych elementów menu "Adres", "Date" i "Greeting":
Dodajmy czwarty element menu, przeciągając element menu z Inspektora biblioteki i nazywając go "Podpis":
Zapisz zmiany na pasku menu.
Teraz utwórzmy zestaw akcji niestandardowych, aby nowe elementy menu zostały uwidocznione w kodzie języka C#. W programie Xcode przejdźmy do widoku Asystent :
Wykonajmy następujące czynności:
Przeciągnij kontrolkę z elementu menu Adres do pliku AppDelegate.h .
Przełącz typ Połączenie ion na Akcja:
Wprowadź nazwę "phraseAddress" i naciśnij przycisk Połączenie, aby utworzyć nową akcję:
Powtórz powyższe kroki dla elementów menu Data, Powitanie i Podpis :
Zapisz zmiany na pasku menu.
Następnie musimy utworzyć gniazdo dla naszego widoku tekstowego, abyśmy mogli dostosować jego zawartość na podstawie kodu. Wybierz plik ViewController.h w Edytorze Asystenta i utwórz nowe gniazdo o nazwie documentText
:
Wróć do Visual Studio dla komputerów Mac, aby zsynchronizować zmiany z programu Xcode. Następnie zmodyfikuj plik ViewController.cs i utwórz go w następujący sposób:
using System;
using AppKit;
using Foundation;
namespace MacMenus
{
public partial class ViewController : NSViewController
{
#region Application Access
public static AppDelegate App {
get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion
#region Computed Properties
public override NSObject RepresentedObject {
get {
return base.RepresentedObject;
}
set {
base.RepresentedObject = value;
// Update the view, if already loaded.
}
}
public string Text {
get { return documentText.Value; }
set { documentText.Value = value; }
}
#endregion
#region Constructors
public ViewController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Do any additional setup after loading the view.
}
public override void ViewWillAppear ()
{
base.ViewWillAppear ();
App.textEditor = this;
}
public override void ViewWillDisappear ()
{
base.ViewDidDisappear ();
App.textEditor = null;
}
#endregion
}
}
Uwidacznia to tekst naszego widoku tekstowego poza ViewController
klasą i informuje delegata aplikacji, gdy okno zyskuje lub traci fokus. Teraz zmodyfikuj plik AppDelegate.cs i utwórz go w następujący sposób:
using AppKit;
using Foundation;
using System;
namespace MacMenus
{
[Register ("AppDelegate")]
public partial class AppDelegate : NSApplicationDelegate
{
#region Computed Properties
public ViewController textEditor { get; set;} = null;
#endregion
#region Constructors
public AppDelegate ()
{
}
#endregion
#region Override Methods
public override void DidFinishLaunching (NSNotification notification)
{
// Insert code here to initialize your application
}
public override void WillTerminate (NSNotification notification)
{
// Insert code here to tear down your application
}
#endregion
#region Custom actions
[Export ("openDocument:")]
void OpenDialog (NSObject sender)
{
var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = false;
dlg.CanChooseDirectories = true;
if (dlg.RunModal () == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Informational,
InformativeText = "At this point we should do something with the folder that the user just selected in the Open File Dialog box...",
MessageText = "Folder Selected"
};
alert.RunModal ();
}
}
partial void phrasesAddress (Foundation.NSObject sender) {
textEditor.Text += "Xamarin HQ\n394 Pacific Ave, 4th Floor\nSan Francisco CA 94111\n\n";
}
partial void phrasesDate (Foundation.NSObject sender) {
textEditor.Text += DateTime.Now.ToString("D");
}
partial void phrasesGreeting (Foundation.NSObject sender) {
textEditor.Text += "Dear Sirs,\n\n";
}
partial void phrasesSignature (Foundation.NSObject sender) {
textEditor.Text += "Sincerely,\n\nKevin Mullins\nXamarin,Inc.\n";
}
#endregion
}
}
W tym miejscu utworzyliśmy AppDelegate
klasę częściową, aby można było użyć akcji i gniazd zdefiniowanych w narzędziu Interface Builder. Udostępniamy również element do textEditor
śledzenia, które okno jest obecnie w centrum uwagi.
Następujące metody służą do obsługi niestandardowych elementów menu i menu:
partial void phrasesAddress (Foundation.NSObject sender) {
if (textEditor == null) return;
textEditor.Text += "Xamarin HQ\n394 Pacific Ave, 4th Floor\nSan Francisco CA 94111\n\n";
}
partial void phrasesDate (Foundation.NSObject sender) {
if (textEditor == null) return;
textEditor.Text += DateTime.Now.ToString("D");
}
partial void phrasesGreeting (Foundation.NSObject sender) {
if (textEditor == null) return;
textEditor.Text += "Dear Sirs,\n\n";
}
partial void phrasesSignature (Foundation.NSObject sender) {
if (textEditor == null) return;
textEditor.Text += "Sincerely,\n\nKevin Mullins\nXamarin,Inc.\n";
}
Teraz, jeśli uruchomimy naszą aplikację, wszystkie elementy w menu Fraza będą aktywne i dodamy frazę do widoku tekstowego po wybraniu:
Teraz, gdy mamy podstawy pracy z paskiem menu aplikacji w dół, przyjrzyjmy się tworzeniu niestandardowego menu kontekstowego.
Tworzenie menu na podstawie kodu
Oprócz tworzenia menu i elementów menu za pomocą narzędzia Interface Builder środowiska Xcode może wystąpić czas, kiedy aplikacja Xamarin.Mac musi utworzyć, zmodyfikować lub usunąć menu, pod menu lub element menu z kodu.
W poniższym przykładzie tworzona jest klasa do przechowywania informacji o elementach menu i podrzędnych menu, które będą dynamicznie tworzone na bieżąco:
using System;
using System.Collections.Generic;
using Foundation;
using AppKit;
namespace AppKit.TextKit.Formatter
{
public class LanguageFormatCommand : NSObject
{
#region Computed Properties
public string Title { get; set; } = "";
public string Prefix { get; set; } = "";
public string Postfix { get; set; } = "";
public List<LanguageFormatCommand> SubCommands { get; set; } = new List<LanguageFormatCommand>();
#endregion
#region Constructors
public LanguageFormatCommand () {
}
public LanguageFormatCommand (string title)
{
// Initialize
this.Title = title;
}
public LanguageFormatCommand (string title, string prefix)
{
// Initialize
this.Title = title;
this.Prefix = prefix;
}
public LanguageFormatCommand (string title, string prefix, string postfix)
{
// Initialize
this.Title = title;
this.Prefix = prefix;
this.Postfix = postfix;
}
#endregion
}
}
Dodawanie menu i elementów
Po zdefiniowaniu tej klasy następująca rutyna analizuje kolekcję LanguageFormatCommand
obiektów i rekursywnie kompiluje nowe menu i elementy menu, dołączając je do dołu istniejącego menu (utworzonego w narzędziu Interface Builder), które zostało przekazane:
private void AssembleMenu(NSMenu menu, List<LanguageFormatCommand> commands) {
NSMenuItem menuItem;
// Add any formatting commands to the Formatting menu
foreach (LanguageFormatCommand command in commands) {
// Add separator or item?
if (command.Title == "") {
menuItem = NSMenuItem.SeparatorItem;
} else {
menuItem = new NSMenuItem (command.Title);
// Submenu?
if (command.SubCommands.Count > 0) {
// Yes, populate submenu
menuItem.Submenu = new NSMenu (command.Title);
AssembleMenu (menuItem.Submenu, command.SubCommands);
} else {
// No, add normal menu item
menuItem.Activated += (sender, e) => {
// Apply the command on the selected text
TextEditor.PerformFormattingCommand (command);
};
}
}
menu.AddItem (menuItem);
}
}
W przypadku dowolnego LanguageFormatCommand
obiektu, który ma pustą Title
właściwość, ta rutyna tworzy element menu Separator (cienka szara linia) między sekcjami menu:
menuItem = NSMenuItem.SeparatorItem;
Jeśli zostanie podany tytuł, zostanie utworzony nowy element menu o tym tytule:
menuItem = new NSMenuItem (command.Title);
LanguageFormatCommand
Jeśli obiekt zawiera obiekty podrzędneLanguageFormatCommand
, zostanie utworzone podrzędne menu, a AssembleMenu
metoda jest rekursywnie wywoływana w celu skompilowania tego menu:
menuItem.Submenu = new NSMenu (command.Title);
AssembleMenu (menuItem.Submenu, command.SubCommands);
W przypadku każdego nowego elementu menu, który nie ma pod menu, kod jest dodawany do obsługi elementu menu wybranego przez użytkownika:
menuItem.Activated += (sender, e) => {
// Do something when the menu item is selected
...
};
Testowanie tworzenia menu
We wszystkich powyższych kodzie, jeśli utworzono następującą kolekcję LanguageFormatCommand
obiektów:
// Define formatting commands
FormattingCommands.Add(new LanguageFormatCommand("Strong","**","**"));
FormattingCommands.Add(new LanguageFormatCommand("Emphasize","_","_"));
FormattingCommands.Add(new LanguageFormatCommand("Inline Code","`","`"));
FormattingCommands.Add(new LanguageFormatCommand("Code Block","```\n","\n```"));
FormattingCommands.Add(new LanguageFormatCommand("Comment","<!--","-->"));
FormattingCommands.Add (new LanguageFormatCommand ());
FormattingCommands.Add(new LanguageFormatCommand("Unordered List","* "));
FormattingCommands.Add(new LanguageFormatCommand("Ordered List","1. "));
FormattingCommands.Add(new LanguageFormatCommand("Block Quote","> "));
FormattingCommands.Add (new LanguageFormatCommand ());
var Headings = new LanguageFormatCommand ("Headings");
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 1","# "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 2","## "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 3","### "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 4","#### "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 5","##### "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 6","###### "));
FormattingCommands.Add (Headings);
FormattingCommands.Add(new LanguageFormatCommand ());
FormattingCommands.Add(new LanguageFormatCommand("Link","[","]()"));
FormattingCommands.Add(new LanguageFormatCommand("Image","![](",")"));
FormattingCommands.Add(new LanguageFormatCommand("Image Link","[![](",")](LinkImageHere)"));
A ta kolekcja przekazana do AssembleMenu
funkcji (z menu Format ustawionym jako podstawa) zostaną utworzone następujące dynamiczne menu i elementy menu:
Usuwanie menu i elementów
Jeśli musisz usunąć dowolny element menu lub menu z interfejsu użytkownika aplikacji, możesz użyć RemoveItemAt
metody NSMenu
klasy po prostu dając jej indeks zerowy elementu do usunięcia.
Aby na przykład usunąć menu i elementy menu utworzone przez procedurę powyżej, możesz użyć następującego kodu:
public void UnpopulateFormattingMenu(NSMenu menu) {
// Remove any additional items
for (int n = (int)menu.Count - 1; n > 4; --n) {
menu.RemoveItemAt (n);
}
}
W przypadku powyższego kodu pierwsze cztery elementy menu są tworzone w narzędziu Interface Builder środowiska Xcode i są niedostępne w aplikacji, więc nie są one usuwane dynamicznie.
Menu kontekstowe
Menu kontekstowe są wyświetlane, gdy użytkownik kliknie prawym przyciskiem myszy lub kliknie element w oknie. Domyślnie kilka elementów interfejsu użytkownika wbudowanych w system macOS ma już dołączone menu kontekstowe (takie jak widok tekstowy). Jednak czasami chcemy utworzyć własne niestandardowe menu kontekstowe dla elementu interfejsu użytkownika dodanego do okna.
Zmodyfikujmy plik Main.storyboard w programie Xcode i dodajmy okno okna do naszego projektu, ustawmy jego klasę na "NSPanel" w Inspektorze tożsamości, dodajmy nowy element Asystenta do menu Okno i dołączmy go do nowego okna przy użyciu programu Show Segue:
Wykonajmy następujące czynności:
Przeciągnij etykietę z Inspektora biblioteki do okna Panel i ustaw jego tekst na "Właściwość":
Następnie przeciągnij menu z Inspektora biblioteki na kontroler widoku w hierarchii widoków i zmień nazwę trzech domyślnych elementów menu Dokument, Tekst i Czcionka:
Teraz przeciągnij kontrolkę z etykiety właściwości do menu:
W oknie podręcznym wybierz pozycję Menu:
W inspektorze tożsamości ustaw klasę kontrolera widoku na "PanelViewController":
Wróć do Visual Studio dla komputerów Mac, aby przeprowadzić synchronizację, a następnie wróć do narzędzia Interface Builder.
Przejdź do edytora Asystenta i wybierz plik PanelViewController.h.
Utwórz akcję dla elementu menu Dokument o nazwie
propertyDocument
:Powtórz tworzenie akcji dla pozostałych elementów menu:
Na koniec utwórz gniazdo dla etykiety właściwości o nazwie
propertyLabel
:Zapisz zmiany i wróć do Visual Studio dla komputerów Mac, aby przeprowadzić synchronizację z programem Xcode.
Edytuj plik PanelViewController.cs i dodaj następujący kod:
partial void propertyDocument (Foundation.NSObject sender) {
propertyLabel.StringValue = "Document";
}
partial void propertyFont (Foundation.NSObject sender) {
propertyLabel.StringValue = "Font";
}
partial void propertyText (Foundation.NSObject sender) {
propertyLabel.StringValue = "Text";
}
Teraz, jeśli uruchomimy aplikację i klikniemy prawym przyciskiem myszy etykietę właściwości w panelu, zobaczymy nasze niestandardowe menu kontekstowe. Jeśli wybierzemy element i z menu, wartość etykiety zmieni się:
Następnie przyjrzyjmy się tworzeniu menu paska stanu.
Menu paska stanu
Menu paska stanu wyświetlają kolekcję elementów menu stanu, które zapewniają interakcję z użytkownikiem lub opinie, takie jak menu lub obraz odzwierciedlający stan aplikacji. Menu paska stanu aplikacji jest włączone i aktywne, nawet jeśli aplikacja jest uruchomiona w tle. Pasek stanu dla całego systemu znajduje się po prawej stronie paska menu aplikacji i jest jedynym paskiem stanu dostępnym obecnie w systemie macOS.
Zmodyfikujmy nasz plik AppDelegate.cs i utwórzmy metodę DidFinishLaunching
podobną do następującej:
public override void DidFinishLaunching (NSNotification notification)
{
// Create a status bar menu
NSStatusBar statusBar = NSStatusBar.SystemStatusBar;
var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable);
item.Title = "Text";
item.HighlightMode = true;
item.Menu = new NSMenu ("Text");
var address = new NSMenuItem ("Address");
address.Activated += (sender, e) => {
PhraseAddress(address);
};
item.Menu.AddItem (address);
var date = new NSMenuItem ("Date");
date.Activated += (sender, e) => {
PhraseDate(date);
};
item.Menu.AddItem (date);
var greeting = new NSMenuItem ("Greeting");
greeting.Activated += (sender, e) => {
PhraseGreeting(greeting);
};
item.Menu.AddItem (greeting);
var signature = new NSMenuItem ("Signature");
signature.Activated += (sender, e) => {
PhraseSignature(signature);
};
item.Menu.AddItem (signature);
}
NSStatusBar statusBar = NSStatusBar.SystemStatusBar;
zapewnia nam dostęp do paska stanu całego systemu. var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable);
tworzy nowy element paska stanu. W tym miejscu utworzymy menu i kilka elementów menu oraz dołączymy menu do właśnie utworzonego elementu paska stanu.
Jeśli uruchomimy aplikację, zostanie wyświetlony nowy element paska stanu. Wybranie elementu z menu spowoduje zmianę tekstu w widoku tekstowym:
Następnie przyjrzyjmy się tworzeniu niestandardowych elementów menu Dock.
Niestandardowe menu docku
Po kliknięciu prawym przyciskiem myszy lub kliknięciu prawym przyciskiem myszy ikony aplikacji w docku zostanie wyświetlone menu Dock w aplikacji Dock.
Utwórzmy niestandardowe menu docku dla naszej aplikacji, wykonując następujące czynności:
W Visual Studio dla komputerów Mac kliknij prawym przyciskiem myszy projekt aplikacji i wybierz pozycję Dodaj>nowy plik... W oknie dialogowym nowy plik wybierz pozycję Xamarin.Mac Empty Interface Definition (Pusta definicja interfejsu Xamarin.Mac>), użyj opcji "DockMenu" w polu Nazwa i kliknij przycisk Nowy, aby utworzyć nowy plik DockMenu.xib:
W okienku rozwiązania kliknij dwukrotnie plik DockMenu.xib, aby otworzyć go do edycji w programie Xcode. Utwórz nowe menu z następującymi elementami: Adres, Data, Powitanie i Podpis
Następnie połączmy nasze nowe elementy menu z istniejącymi akcjami utworzonymi dla naszego menu niestandardowego w sekcji Dodawanie, edytowanie i usuwanie menu powyżej. Przejdź do inspektora Połączenie ion i wybierz pozycję Pierwszy obiekt odpowiadający w hierarchii interfejsu. Przewiń w dół i znajdź
phraseAddress:
akcję. Przeciągnij linię z okręgu w tej akcji do elementu menu Adres :Powtórz dla wszystkich innych elementów menu dołączających je do odpowiednich akcji:
Następnie wybierz pozycję Aplikacja w hierarchii interfejsu. W inspektorze Połączenie ion przeciągnij linię z okręgu na wylot do
dockMenu
właśnie utworzonego menu:Zapisz zmiany i wróć do Visual Studio dla komputerów Mac, aby przeprowadzić synchronizację z programem Xcode.
Kliknij dwukrotnie plik Info.plist, aby otworzyć go do edycji:
Kliknij kartę Źródło w dolnej części ekranu:
Kliknij pozycję Dodaj nowy wpis, kliknij zielony przycisk plus, ustaw nazwę właściwości na "AppleDockMenu" i wartość "DockMenu" (nazwa nowego pliku xib bez rozszerzenia):
Teraz, jeśli uruchomimy naszą aplikację i klikniemy prawym przyciskiem myszy jej ikonę w Docku, zostaną wyświetlone nowe elementy menu:
Jeśli wybierzemy jeden z elementów niestandardowych z menu, tekst w naszym widoku tekstowym zostanie zmodyfikowany.
Przycisk wyskakujące i listy rozwijane
Przycisk podręczny wyświetla wybrany element i przedstawia listę opcji do wyboru po kliknięciu przez użytkownika. Lista ściągania to typ wyskakującego przycisku, który jest zwykle używany do wybierania poleceń specyficznych dla kontekstu bieżącego zadania. Oba mogą być wyświetlane w dowolnym miejscu w oknie.
Utwórzmy niestandardowy przycisk wyskakujących okienek dla naszej aplikacji, wykonując następujące czynności:
Edytuj plik Main.storyboard w programie Xcode i przeciągnij przycisk podręczny z Inspektora biblioteki do okna Panel utworzonego w sekcji Menu kontekstowe:
Dodaj nowy element menu i ustaw tytuły elementów w menu podręcznym na: Adres, Data, Powitanie i Podpis
Następnie połączmy nowe elementy menu z istniejącymi akcjami utworzonymi dla naszego menu niestandardowego w sekcji Dodawanie, edytowanie i usuwanie menu powyżej. Przejdź do inspektora Połączenie ion i wybierz pozycję Pierwszy obiekt odpowiadający w hierarchii interfejsu. Przewiń w dół i znajdź
phraseAddress:
akcję. Przeciągnij linię z okręgu w tej akcji do elementu menu Adres :Powtórz dla wszystkich innych elementów menu dołączających je do odpowiednich akcji:
Zapisz zmiany i wróć do Visual Studio dla komputerów Mac, aby przeprowadzić synchronizację z programem Xcode.
Teraz, jeśli uruchomimy aplikację i wybierzemy element z wyskakującego okienka, tekst w naszym widoku tekstowym zmieni się:
Możesz tworzyć listy ściągania i pracować z tymi samymi opcjami, co przyciski podręczne. Zamiast dołączać do istniejącej akcji, możesz utworzyć własne akcje niestandardowe tak jak w przypadku naszego menu kontekstowego w sekcji Menu kontekstowe .
Podsumowanie
W tym artykule szczegółowo przedstawiono pracę z menu i elementami menu w aplikacji platformy Xamarin.Mac. Najpierw zbadaliśmy pasek menu aplikacji, a następnie przyjrzeliśmy się tworzeniu menu kontekstowych, następnie zbadaliśmy menu paska stanu i niestandardowe menu docku. Na koniec omówiliśmy wyskakujące menu i listy rozwijane.