Menus no Xamarin.Mac
Este artigo aborda o trabalho com menus em um aplicativo Xamarin.Mac. Ele descreve a criação e manutenção de menus e itens de menu no Xcode e no Interface Builder e como trabalhar com eles programaticamente.
Ao trabalhar com C# e .NET em um aplicativo Xamarin.Mac, você tem acesso aos mesmos menus do Cocoa que um desenvolvedor trabalhando no Objective-C Xcode faz. Como o Xamarin.Mac se integra diretamente ao Xcode, você pode usar o Construtor de Interfaces do Xcode para criar e manter suas barras de menu, menus e itens de menu (ou, opcionalmente, criá-los diretamente no código C#).
Os menus são parte integrante da experiência do usuário de um aplicativo Mac e geralmente aparecem em várias partes da interface do usuário:
- A barra de menus do aplicativo - Este é o menu principal que aparece na parte superior da tela para cada aplicativo Mac.
- Menus contextuais - Aparecem quando o usuário clica com o botão direito do mouse ou clica com o botão direito do mouse em um item em uma janela.
- A barra de status - Esta é a área no lado direito da barra de menus do aplicativo que aparece na parte superior da tela (à esquerda do relógio da barra de menus) e cresce para a esquerda à medida que os itens são adicionados a ela.
- Menu do Dock - O menu de cada aplicativo no dock que aparece quando o usuário clica com o botão direito do mouse ou controla o ícone do aplicativo, ou quando o usuário clica com o botão esquerdo do mouse no ícone e mantém pressionado o botão do mouse.
- Botão pop-up e listas suspensas - Um botão pop-up exibe um item selecionado e apresenta uma lista de opções para selecionar quando clicado pelo usuário. Uma lista suspensa é um tipo de botão pop-up geralmente usado para selecionar comandos específicos para o contexto da tarefa atual. Ambos podem aparecer em qualquer lugar de uma janela.
Neste artigo, abordaremos os conceitos básicos de como trabalhar com barras de menu, menus e itens de menu do Cocoa em um aplicativo Xamarin.Mac. É altamente recomendável que você trabalhe primeiro no artigo Olá, Mac, especificamente nas seções Introdução ao Xcode e ao Construtor de Interface e Saídas e Ações, pois ele aborda os principais conceitos e técnicas que usaremos neste artigo.
Você pode querer dar uma olhada na seção Expondo classes C# / métodos para Objective-Cdo documento Xamarin.Mac Internals também, ele explica os Register
atributos e Export
usados para conectar suas classes C# a Objective-C objetos e elementos da interface do usuário.
A barra de menus do aplicativo
Ao contrário dos aplicativos executados no sistema operacional Windows, onde cada janela pode ter sua própria barra de menus anexada a ela, cada aplicativo executado no macOS tem uma única barra de menus que é executada ao longo da parte superior da tela que é usada para cada janela nesse aplicativo:
Os itens nesta barra de menus são ativados ou desativados com base no contexto ou estado atual do aplicativo e sua interface de usuário a qualquer momento. Por exemplo: se o usuário selecionar um campo de texto, os itens no menu Editar serão habilitados, como Copiar e Recortar.
De acordo com a Apple e por padrão, todos os aplicativos do macOS têm um conjunto padrão de menus e itens de menu que aparecem na barra de menus do aplicativo:
- Menu Apple - Este menu fornece acesso a itens de todo o sistema que estão disponíveis para o usuário em todos os momentos, independentemente do aplicativo que está sendo executado. Esses itens não podem ser modificados pelo desenvolvedor.
- Menu do aplicativo - Este menu exibe o nome do aplicativo em negrito e ajuda o usuário a identificar qual aplicativo está sendo executado no momento. Ele contém itens que se aplicam ao aplicativo como um todo e não a um determinado documento ou processo, como sair do aplicativo.
- Menu Arquivo - Itens usados para criar, abrir ou salvar documentos com os quais seu aplicativo trabalha. Se o aplicativo não for baseado em documento, esse menu poderá ser renomeado ou removido.
- Menu Editar - Mantém comandos como Recortar, Copiar e Colar , que são usados para editar ou modificar elementos na interface do usuário do aplicativo.
- Menu Formatar - Se o aplicativo funcionar com texto, esse menu contém comandos para ajustar a formatação desse texto.
- Menu Exibir - Mantém comandos que afetam como o conteúdo é exibido (visualizado) na interface do usuário do aplicativo.
- Menus específicos do aplicativo - São todos os menus específicos do seu aplicativo (como um menu de favoritos para um navegador da Web). Eles devem aparecer entre os menus Exibir e Janela na barra.
- Menu Janela - Contém comandos para trabalhar com janelas em seu aplicativo, bem como uma lista de janelas abertas atuais.
- Menu Ajuda - Se o aplicativo fornecer ajuda na tela, o menu Ajuda deverá ser o menu mais à direita na barra.
Para obter mais informações sobre a barra de menus do aplicativo e menus e itens de menu padrão, consulte as Diretrizes de interface humana da Apple.
A barra de menus padrão do aplicativo
Sempre que você cria um novo projeto Xamarin.Mac, você obtém automaticamente uma barra de menus de aplicativo padrão padrão que tem os itens típicos que um aplicativo macOS normalmente teria (conforme discutido na seção acima). A barra de menus padrão do aplicativo é definida no arquivo Main.storyboard (junto com o restante da interface do usuário do aplicativo) no projeto no Painel de Soluções:
Clique duas vezes no arquivo Main.storyboard para abri-lo para edição no Construtor de Interfaces do Xcode e você verá a interface do editor de menus:
A partir daqui, podemos clicar em itens como o item de menu Abrir no menu Arquivo e editar ou ajustar suas propriedades no Inspetor de Atributos:
Começaremos a adicionar, editar e excluir menus e itens mais adiante neste artigo. Por enquanto, queremos apenas ver quais menus e itens de menu estão disponíveis por padrão e como eles foram automaticamente expostos ao código por meio de um conjunto de saídas e ações predefinidas (para obter mais informações, consulte nossa documentação de Saídas e Ações ).
Por exemplo, se clicarmos no Inspetor de Conexão para o item de menu Abrir, podemos ver que ele está automaticamente conectado à openDocument:
ação:
Se você selecionar o Primeiro Respondente na Hierarquia de Interface e rolar para baixo no Inspetor de Conexão, verá a definição da ação à qual o item de menu Abrir está anexado openDocument:
(junto com várias outras ações padrão para o aplicativo que estão e não estão conectadas automaticamente aos controles):
Por que isso é importante? Na próxima seção, veremos como essas ações definidas automaticamente funcionam com outros elementos da interface do usuário do Cocoa para habilitar e desabilitar automaticamente os itens de menu, bem como, fornecer funcionalidade interna para os itens.
Mais tarde, usaremos essas ações internas para habilitar e desabilitar itens do código e fornecer nossa própria funcionalidade quando eles forem selecionados.
Funcionalidade de menu integrada
Se você executou um aplicativo Xamarin.Mac recém-criado antes de adicionar qualquer item ou código da interface do usuário, notará que alguns itens são automaticamente conectados e habilitados para você (com funcionalidade totalmente integrada automaticamente), como o item Sair no menu Aplicativo:
Enquanto outros itens de menu, como Recortar, Copiar e Colar não são:
Vamos parar o aplicativo e clicar duas vezes no arquivo Main.storyboard no Solution Pad para abri-lo para edição no Construtor de Interface do Xcode. Em seguida, arraste uma Vista de Texto da Biblioteca para o controlador de vista da janela no Editor de Interfaces:
No Editor de restrições, vamos fixar a exibição de texto nas bordas da janela e defini-la onde ela cresce e diminui com a janela, clicando em todos os quatro feixes de I vermelhos na parte superior do editor e clicando no botão Adicionar 4 restrições:
Salve suas alterações no design da interface do usuário e alterne o Visual Studio para Mac para sincronizar as alterações com seu projeto Xamarin.Mac. Agora inicie o aplicativo, digite algum texto na exibição de texto, selecione-o e abra o menu Editar :
Observe como os itens Recortar, Copiar e Colar são ativados automaticamente e totalmente funcionais, tudo sem escrever uma única linha de código.
O que está acontecendo? Lembre-se das ações predefinidas internas que vêm conectadas aos itens de menu padrão (como apresentado acima), a maioria dos elementos da interface do usuário do Cocoa que fazem parte do macOS tem ganchos integrados para ações específicas (como copy:
). Assim, quando eles são adicionados a uma janela, ativos e selecionados, o item de menu correspondente ou itens anexados a essa ação são ativados automaticamente. Se o usuário selecionar esse item de menu, a funcionalidade incorporada ao elemento da interface do usuário será chamada e executada, tudo sem intervenção do desenvolvedor.
Habilitando e desabilitando menus e itens
Por padrão, sempre que ocorre um evento de usuário, NSMenu
habilita e desabilita automaticamente cada menu e item de menu visível com base no contexto do aplicativo. Há três maneiras de habilitar/desabilitar um item:
- Ativação automática de menu - Um item de menu é habilitado se
NSMenu
puder localizar um objeto apropriado que responda à ação à qual o item está conectado. Por exemplo, a exibição de texto acima que tinha um gancho interno para acopy:
ação. - Ações personalizadas e validateMenuItem: - Para qualquer item de menu vinculado a uma ação personalizada do controlador de janela ou exibição, você pode adicionar a ação e habilitar ou desabilitar manualmente os itens de
validateMenuItem:
menu. - Habilitação manual do menu - Você define manualmente a
Enabled
propriedade de cadaNSMenuItem
um para habilitar ou desabilitar cada item em um menu individualmente.
Para escolher um sistema, defina a AutoEnablesItems
propriedade de um NSMenu
arquivo . true
é automático (o comportamento padrão) e false
é manual.
Importante
Se você optar por usar a habilitação manual de menu, nenhum dos itens de menu, mesmo aqueles controlados por classes AppKit como NSTextView
, serão atualizados automaticamente. Você será responsável por habilitar e desabilitar todos os itens manualmente no código.
Usando validateMenuItem
Como dito acima, para qualquer item de menu vinculado a uma Ação Personalizada do Controlador de Janela ou de Exibição, você pode adicionar a ação e habilitar ou desabilitar manualmente os validateMenuItem:
itens de menu.
No exemplo a seguir, a Tag
propriedade será usada para decidir o tipo de item de menu que será habilitado/desabilitado validateMenuItem:
pela ação com base no estado do texto selecionado em um NSTextView
arquivo . A Tag
propriedade foi definida no Interface Builder para cada item de menu:
E o seguinte código adicionado ao controlador de exibição:
[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;
}
Quando esse código é executado e nenhum texto é selecionado no NSTextView
, os dois itens de menu de quebra automática são desabilitados (mesmo que estejam conectados a ações no controlador de exibição):
Se uma seção de texto for selecionada e o menu reaberto, os dois itens de menu de quebra automática estarão disponíveis:
Habilitando e respondendo a itens de menu no código
Como vimos acima, apenas adicionando elementos específicos da interface do usuário do Cocoa ao nosso design de interface do usuário (como um campo de texto), vários dos itens de menu padrão serão ativados e funcionarão automaticamente, sem ter que escrever nenhum código. Em seguida, vamos analisar como adicionar nosso próprio código C# ao nosso projeto Xamarin.Mac para habilitar um item de menu e fornecer funcionalidade quando o usuário selecioná-lo.
Por exemplo, digamos que queremos que o usuário possa usar o item Abrir no menu Arquivo para selecionar uma pasta. Como queremos que essa seja uma função de todo o aplicativo e não limitada a uma janela ou elemento de interface do usuário, vamos adicionar o código para lidar com isso ao nosso delegado de aplicativo.
No Solution Pad, clique duas vezes no AppDelegate.CS
arquivo para abri-lo para edição:
Adicione o seguinte código ao método DidFinishLaunching
:
[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 ();
}
}
Vamos executar o aplicativo agora e abrir o menu Arquivo :
Observe que o item de menu Abrir agora está habilitado. Se selecioná-lo, a caixa de diálogo aberta será exibida:
Se clicarmos no botão Abrir , nossa mensagem de alerta será exibida:
A linha chave aqui foi [Export ("openDocument:")]
, ele diz NSMenu
que nosso AppDelegate tem um método void OpenDialog (NSObject sender)
que responde à openDocument:
ação. Se você se lembrar de cima, o item de menu Abrir é automaticamente conectado a essa ação por padrão no Construtor de Interfaces:
Em seguida, vamos analisar a criação de nosso próprio menu, itens de menu e ações e responder a eles em código.
Trabalhando com o menu recente aberto
Por padrão, o menu Arquivo contém um item Abrir recente que mantém o controle dos últimos vários arquivos que o usuário abriu com seu aplicativo. Se você estiver criando um NSDocument
aplicativo Xamarin.Mac baseado, este menu será manipulado para você automaticamente. Para qualquer outro tipo de aplicativo Xamarin.Mac, você será responsável por gerenciar e responder a este item de menu manualmente.
Para manipular manualmente o menu Abrir recente , primeiro você precisará informá-lo de que um novo arquivo foi aberto ou salvo usando o seguinte:
// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);
Mesmo que seu aplicativo não esteja usando NSDocuments
o , você ainda usa o NSDocumentController
para manter o menu Abrir recente enviando um NSUrl
com o local do arquivo para o NoteNewRecentDocumentURL
método do SharedDocumentController
.
Em seguida, você precisa substituir o OpenFile
método do delegado do aplicativo para abrir qualquer arquivo selecionado pelo usuário no menu Abrir recente . Por exemplo:
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;
}
}
Retorne true
se o arquivo puder ser aberto, caso contrário, retorne false
e um aviso interno será exibido ao usuário de que o arquivo não pôde ser aberto.
Como o nome do arquivo e o caminho retornados do menu Abrir recente podem incluir um espaço, precisamos escapar corretamente desse caractere antes de criar um NSUrl
ou obteremos um erro. Fazemos isso com o seguinte código:
filename = filename.Replace (" ", "%20");
Finalmente, criamos um NSUrl
que aponta para o arquivo e usamos um método auxiliar no delegado do aplicativo para abrir uma nova janela e carregar o arquivo nele:
var url = new NSUrl ("file://"+filename);
return OpenFile(url);
Para reunir tudo, vamos dar uma olhada em um exemplo de implementação em um arquivo 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
}
}
Com base nos requisitos do seu aplicativo, talvez você não queira que o usuário abra o mesmo arquivo em mais de uma janela ao mesmo tempo. Em nosso aplicativo de exemplo, se o usuário escolher um arquivo que já está aberto (nos itens de menu Abrir recente ou Abrir..), a janela que contém o arquivo será trazida para a frente.
Para fazer isso, usamos o seguinte código em nosso método auxiliar:
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;
}
}
Projetamos nossa ViewController
classe para manter o caminho para o arquivo em sua Path
propriedade. Em seguida, percorremos todas as janelas abertas atualmente no aplicativo. Se o arquivo já estiver aberto em uma das janelas, ele será levado para a frente de todas as outras janelas usando:
NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);
Se nenhuma correspondência for encontrada, uma nova janela será aberta com o arquivo carregado e o arquivo será anotado no menu Abrir recente :
// 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);
Trabalhando com ações de janela personalizadas
Assim como as ações internas do First Responder que vêm pré-conectadas a itens de menu padrão, você pode criar novas ações personalizadas e conectá-las a itens de menu no Construtor de Interfaces.
Primeiro, defina uma ação personalizada em um dos controladores de janela do seu aplicativo. Por exemplo:
[Action("defineKeyword:")]
public void defineKeyword (NSObject sender) {
// Preform some action when the menu is selected
Console.WriteLine ("Request to define keyword");
}
Em seguida, clique duas vezes no arquivo de storyboard do aplicativo no Solution Pad para abri-lo para edição no Construtor de Interfaces do Xcode. Selecione o Primeiro Respondente na Cena do Aplicativo e alterne para o Inspetor de Atributos:
+ Clique no botão na parte inferior do Inspetor de Atributos para adicionar uma nova ação personalizada:
Dê a ele o mesmo nome da ação personalizada que você criou no controlador de janela:
Clique com a tecla Control pressionada e arraste de um item de menu para o Primeiro Respondente na Cena do Aplicativo. Na lista pop-up, selecione a nova ação que você acabou de criar (defineKeyword:
neste exemplo):
Salve as alterações no storyboard e retorne ao Visual Studio para Mac para sincronizar as alterações. Se você executar o aplicativo, o item de menu ao qual você conectou a ação personalizada será automaticamente habilitado/desabilitado (com base na janela com a ação sendo aberta) e a seleção do item de menu disparará a ação:
Adicionando, editando e excluindo menus
Como vimos nas seções anteriores, um aplicativo Xamarin.Mac vem com um número predefinido de menus padrão e itens de menu que controles específicos da interface do usuário ativarão e responderão automaticamente. Também vimos como adicionar código ao nosso aplicativo que também habilitará e responderá a esses itens padrão.
Nesta seção, veremos como remover itens de menu que não precisamos, reorganizar menus e adicionar novos menus, itens de menu e ações.
Clique duas vezes no arquivo Main.storyboard no Solution Pad para abri-lo para edição:
Para o nosso aplicativo Xamarin.Mac específico, não vamos usar o menu de visualização padrão, então vamos removê-lo. Na hierarquia de interface, selecione o item de menu Exibir que faz parte da barra de menus principal:
Pressione delete ou backspace para excluir o menu. Em seguida, não usaremos todos os itens no menu Formatar e queremos mover os itens que usaremos para fora dos submenus. Na hierarquia de interface, selecione os seguintes itens de menu:
Arraste os itens sob o menu pai do submenu onde eles estão atualmente:
Seu menu agora deve ter a seguinte aparência:
Em seguida, vamos arrastar o submenu Texto para fora do menu Formatar e colocá-lo na barra de menus principal entre os menus Formatar e Janela:
Vamos voltar no menu Formatar e excluir o item do submenu Fonte . Em seguida, selecione o menu Formatar e renomeie-o como "Fonte":
Em seguida, vamos criar um menu personalizado de frases predefinidas que serão automaticamente anexadas ao texto na exibição de texto quando forem selecionadas. Na caixa de pesquisa na parte inferior do Inspetor de biblioteca, digite "menu". Isso facilitará a localização e o trabalho com todos os elementos da interface do usuário do menu:
Agora vamos fazer o seguinte para criar nosso menu:
Arraste um Item de Menu do Inspetor de Biblioteca para a barra de menus entre os menus Texto e Janela :
Renomeie o item "Frases":
Em seguida, arraste um Menu do Inspetor de biblioteca:
Solte então Menu no novo Item de Menu que acabamos de criar e altere seu nome para "Frases":
Agora vamos renomear os três itens de menu padrão "Endereço", "Data" e "Saudação":
Vamos adicionar um quarto Item de Menu arrastando um Item de Menu do Inspetor de Biblioteca e chamando-o de "Assinatura":
Salve as alterações na barra de menus.
Agora vamos criar um conjunto de ações personalizadas para que nossos novos itens de menu sejam expostos ao código C#. No Xcode, vamos alternar para o modo de exibição Assistente:
Vamos fazer o seguinte:
Arraste com a tecla Control pressionada do item de menu Endereço para o arquivo AppDelegate.h .
Alterne o tipo de conexão para Ação:
Digite um Nome de "phraseAddress" e pressione o botão Conectar para criar a nova ação:
Repita as etapas acima para os itens de menu Data, Saudação e Assinatura :
Salve as alterações na barra de menus.
Em seguida, precisamos criar uma saída para nossa exibição de texto para que possamos ajustar seu conteúdo a partir do código. Selecione o arquivo ViewController.h no Editor Assistente e crie uma nova saída chamada documentText
:
Retorne ao Visual Studio para Mac para sincronizar as alterações do Xcode. Em seguida, edite o arquivo ViewController.cs e faça com que ele tenha a seguinte aparência:
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
}
}
Isso expõe o texto de nossa exibição de texto fora da ViewController
classe e informa ao representante do aplicativo quando a janela ganha ou perde o foco. Agora edite o arquivo AppDelegate.cs e faça com que ele tenha a seguinte aparência:
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
}
}
Aqui fizemos a AppDelegate
classe a partial para que possamos usar as ações e saídas que definimos no Interface Builder. Também expomos um textEditor
para rastrear qual janela está atualmente em foco.
Os seguintes métodos são usados para lidar com nosso menu personalizado e itens de 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";
}
Agora, se executarmos nosso aplicativo, todos os itens no menu Frase estarão ativos e adicionarão a frase à exibição de texto quando selecionados:
Agora que temos as noções básicas de como trabalhar com a barra de menus do aplicativo, vamos ver como criar um menu contextual personalizado.
Criando menus a partir do código
Além de criar menus e itens de menu com o Construtor de Interfaces do Xcode, pode haver momentos em que um aplicativo Xamarin.Mac precise criar, modificar ou remover um menu, submenu ou item de menu do código.
No exemplo a seguir, uma classe é criada para armazenar as informações sobre os itens de menu e submenus que serão criados dinamicamente em tempo real:
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
}
}
Adicionando menus e itens
Com essa classe definida, a seguinte rotina analisará uma coleção de objetos e criará recursivamente novos menus e itens de LanguageFormatCommand
menu anexando-os à parte inferior do menu existente (criado no Construtor de Interfaces) que foi passado:
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);
}
}
Para qualquer LanguageFormatCommand
objeto que tenha uma propriedade em brancoTitle
, essa rotina cria um item de menu Separador (uma linha cinza fina) entre as seções do menu:
menuItem = NSMenuItem.SeparatorItem;
Se um título for fornecido, um novo item de menu com esse título será criado:
menuItem = new NSMenuItem (command.Title);
Se o LanguageFormatCommand
objeto contiver objetos filho LanguageFormatCommand
, um submenu será criado e o AssembleMenu
método será chamado recursivamente para criar esse menu:
menuItem.Submenu = new NSMenu (command.Title);
AssembleMenu (menuItem.Submenu, command.SubCommands);
Para qualquer novo item de menu que não tenha submenus, o código é adicionado para manipular o item de menu que está sendo selecionado pelo usuário:
menuItem.Activated += (sender, e) => {
// Do something when the menu item is selected
...
};
Testando a criação do menu
Com todo o código acima no lugar, se a seguinte coleção de LanguageFormatCommand
objetos foi criada:
// 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)"));
E essa coleção passada para a AssembleMenu
função (com o Menu Formatar definido como base), os seguintes menus dinâmicos e itens de menu seriam criados:
Removendo menus e itens
Se você precisar remover qualquer menu ou item de menu da interface do usuário do aplicativo, você pode usar o RemoveItemAt
NSMenu
método da classe simplesmente dando-lhe o índice baseado em zero do item a ser removido.
Por exemplo, para remover os menus e itens de menu criados pela rotina acima, você pode usar o seguinte código:
public void UnpopulateFormattingMenu(NSMenu menu) {
// Remove any additional items
for (int n = (int)menu.Count - 1; n > 4; --n) {
menu.RemoveItemAt (n);
}
}
No caso do código acima, os quatro primeiros itens de menu são criados no Construtor de Interfaces do Xcode e estão disponíveis no aplicativo, para que não sejam removidos dinamicamente.
Menus contextuais
Os menus contextuais aparecem quando o usuário clica com o botão direito do mouse ou controla um item em uma janela. Por padrão, vários dos elementos da interface do usuário incorporados ao macOS já têm menus contextuais anexados a eles (como a exibição de texto). No entanto, pode haver momentos em que queremos criar nossos próprios menus contextuais personalizados para um elemento da interface do usuário que adicionamos a uma janela.
Vamos editar nosso arquivo Main.storyboard no Xcode e adicionar uma janela Janela ao nosso design, definir sua Classe como "NSPanel" no Inspetor de Identidade, adicionar um novo item Assistente ao menu Janela e anexá-lo à nova janela usando um Mostrar Segue:
Vamos fazer o seguinte:
Arraste um Rótulo do Inspetor de Biblioteca para a janela Painel e defina seu texto como "Propriedade":
Em seguida, arraste um Menu do Inspetor de Biblioteca para o Controlador de Exibição na Hierarquia de Exibição e renomeie os três itens de menu padrão Documento, Texto e Fonte:
Agora, arraste com a tecla Control pressionada do Rótulo da propriedade para o menu:
Na caixa de diálogo pop-up, selecione Menu:
No Inspetor de Identidade, defina a classe do View Controller como "PanelViewController":
Alterne de volta para o Visual Studio para Mac para sincronizar e, em seguida, retorne ao Construtor de Interfaces.
Alterne para o Editor Assistente e selecione o arquivo PanelViewController.h .
Crie uma ação para o item de menu Documento chamada
propertyDocument
:Repita as ações de criação para os itens de menu restantes:
Finalmente, crie uma saída para o Rótulo de propriedade chamado
propertyLabel
:Salve suas alterações e retorne ao Visual Studio para Mac para sincronizar com o Xcode.
Edite o arquivo PanelViewController.cs e adicione o seguinte código:
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";
}
Agora, se executarmos o aplicativo e clicarmos com o botão direito do mouse no rótulo da propriedade no painel, veremos nosso menu contextual personalizado. Se selecionarmos e item no menu, o valor do rótulo será alterado:
Em seguida, vamos examinar a criação de menus da barra de status.
Menus da barra de status
Os menus da barra de status exibem uma coleção de itens de menu de status que fornecem interação ou feedback ao usuário, como um menu ou uma imagem que reflete o estado de um aplicativo. O menu da barra de status de um aplicativo está habilitado e ativo mesmo se o aplicativo estiver sendo executado em segundo plano. A barra de status de todo o sistema reside no lado direito da barra de menus do aplicativo e é a única barra de status atualmente disponível no macOS.
Vamos editar nosso arquivo AppDelegate.cs e fazer com que o DidFinishLaunching
método tenha a seguinte aparência:
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;
nos dá acesso à barra de status de todo o sistema. var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable);
Cria um novo item da barra de status. A partir daí, criamos um menu e uma série de itens de menu e anexamos o menu ao item da barra de status que acabamos de criar.
Se executarmos o aplicativo, o novo item da barra de status será exibido. A seleção de um item no menu alterará o texto na visualização de texto:
Em seguida, vamos examinar a criação de itens de menu de doca personalizados.
Menus de doca personalizados
O menu dock aparece para o aplicativo Mac quando o usuário clica com o botão direito do mouse ou clica com o botão direito do mouse no ícone do aplicativo no dock:
Vamos criar um menu de doca personalizado para o nosso aplicativo fazendo o seguinte:
No Visual Studio para Mac, clique com o botão direito do mouse no projeto do aplicativo e selecione Adicionar>novo arquivo... Na caixa de diálogo do novo arquivo, selecione Definição de interface vazia do Xamarin.Mac>, use "DockMenu" para o Nome e clique no botão Novo para criar o novo arquivo DockMenu.xib:
No Solution Pad, clique duas vezes no arquivo DockMenu.xib para abri-lo para edição no Xcode. Crie um novo menu com os seguintes itens: Endereço, Data, Saudação e Assinatura
Em seguida, vamos conectar nossos novos itens de menu às nossas ações existentes que criamos para nosso menu personalizado na seção Adicionando, Editando e Excluindo Menus acima. Alterne para o Inspetor de Conexão e selecione o Primeiro Respondente na Hierarquia de Interface. Role para baixo e encontre a
phraseAddress:
ação. Arraste uma linha do círculo nessa ação para o item de menu Endereço :Repita para todos os outros itens de menu anexando-os às suas ações correspondentes:
Em seguida, selecione o Aplicativo na Hierarquia de Interface. No Inspetor de Conexão, arraste uma linha do círculo na
dockMenu
tomada para o menu que acabamos de criar:Salve suas alterações e volte para o Visual Studio para Mac para sincronizar com o Xcode.
Clique duas vezes no arquivo Info.plist para abri-lo para edição:
Clique na guia Origem na parte inferior da tela:
Clique em Adicionar nova entrada, clique no botão verde de mais, defina o nome da propriedade como "AppleDockMenu" e o valor como "DockMenu" (o nome do nosso novo arquivo .xib sem a extensão):
Agora, se executarmos nosso aplicativo e clicarmos com o botão direito do mouse em seu ícone no Dock, nossos novos itens de menu serão exibidos:
Se selecionarmos um dos itens personalizados no menu, o texto em nossa visualização de texto será modificado.
Botão pop-up e listas suspensas
Um botão pop-up exibe um item selecionado e apresenta uma lista de opções para selecionar quando clicado pelo usuário. Uma lista suspensa é um tipo de botão pop-up geralmente usado para selecionar comandos específicos para o contexto da tarefa atual. Ambos podem aparecer em qualquer lugar de uma janela.
Vamos criar um botão pop-up personalizado para o nosso aplicativo fazendo o seguinte:
Edite o arquivo Main.storyboard no Xcode e arraste um botão pop-up do Inspetor de biblioteca para a janela Painel que criamos na seção Menus contextuais:
Adicione um novo item de menu e defina os títulos dos Itens no Pop-up como: Endereço, Data, Saudação e Assinatura
Em seguida, vamos conectar nossos novos itens de menu às ações existentes que criamos para nosso menu personalizado na seção Adicionando, Editando e Excluindo Menus acima. Alterne para o Inspetor de Conexão e selecione o Primeiro Respondente na Hierarquia de Interface. Role para baixo e encontre a
phraseAddress:
ação. Arraste uma linha do círculo nessa ação para o item de menu Endereço :Repita para todos os outros itens de menu anexando-os às suas ações correspondentes:
Salve suas alterações e volte para o Visual Studio para Mac para sincronizar com o Xcode.
Agora, se executarmos nosso aplicativo e selecionarmos um item no pop-up, o texto em nossa exibição de texto será alterado:
Você pode criar e trabalhar com listas suspensas exatamente da mesma forma que os botões pop-up. Em vez de anexar à ação existente, você pode criar suas próprias ações personalizadas, assim como fizemos para nosso menu contextual na seção Menus contextuais .
Resumo
Este artigo deu uma olhada detalhada no trabalho com menus e itens de menu em um aplicativo Xamarin.Mac. Primeiro examinamos a barra de menus do aplicativo, depois analisamos a criação de menus contextuais, em seguida examinamos os menus da barra de status e os menus personalizados do dock. Finalmente, cobrimos menus pop-up e listas suspensas.