Xamarin.Mac のメニュー
この記事では、Xamarin.Mac アプリケーションでのメニューの操作について説明します。 Xcode と Interface Builder でのメニュー項目とメニュー項目の作成とメインを記述し、プログラムで操作する方法について説明します。
Xamarin.Mac アプリケーションで C# と .NET を使用する場合は、開発者が作業しているのと Xcode と同じ Cocoa メニューに Objective-C アクセスできます。 Xamarin.Mac は Xcode と直接統合されるため、Xcode のインターフェイス ビルダーを使用して、メニュー バー、メニュー、メニュー項目を作成してメインできます (または必要に応じて、C# コードで直接作成することもできます)。
メニューは Mac アプリケーションのユーザー エクスペリエンスに不可欠な要素であり、一般的にユーザー インターフェイスのさまざまな部分に表示されます。
- アプリケーションのメニュー バー - これは、すべての Mac アプリケーションの画面の上部に表示されるメイン メニューです。
- コンテキスト メニュー - ユーザーがウィンドウ内の項目を右クリックまたは control キーを押しながらクリックすると表示されます。
- ステータス バー - これは、画面の上部 (メニュー バーの時計の左側) に表示され、項目が追加されると左側に拡大する、アプリケーション メニュー バーの右端にある領域です。
- Dock メニュー - ユーザーがアプリケーションのアイコンを右クリックまたはコントロールでクリックしたとき、またはユーザーがアイコンを左クリックしてマウス ボタンを下に押したときに表示される、ドック内の各アプリケーションのメニュー。
- ポップアップ ボタンとプルダウン リスト - ポップアップ ボタンには、選択した項目が表示され、ユーザーがクリックしたときに選択できるオプションの一覧が表示されます。 プルダウン リストは、通常、現在のタスクのコンテキストに固有のコマンドを選択するために使用されるポップアップ ボタンの一種です。 どちらもウィンドウ内の任意の場所に表示できます。
この記事では、Xamarin.Mac アプリケーションでの Cocoa メニュー バー、メニュー、メニュー項目の操作の基本について説明します。 この記事で使用する 主要な概念と手法については、まず Hello Mac の記事、特 に Xcode とインターフェイス ビルダー と アウトレットとアクション の概要に関するセクションを参照することを強くお勧めします。
Xamarin.Mac Internals ドキュメントのセクションへの C# クラス/メソッドObjective-Cの公開に関するページも参照してください。C# クラスをオブジェクトと UI 要素に結び付けるために使用される属性Objective-CについてもExport
説明Register
しています。
アプリケーションのメニュー バー
すべてのウィンドウに独自のメニュー バーをアタッチできる Windows OS 上で実行されているアプリケーションとは異なり、macOS で実行されているすべてのアプリケーションには、そのアプリケーション内のすべてのウィンドウに使用される画面の上部に沿って実行される 1 つのメニュー バーがあります。
このメニュー バーの項目は、アプリケーションの現在のコンテキストまたは状態とそのユーザー インターフェイスに基づいて、任意の時点でアクティブ化または非アクティブ化されます。 たとえば、ユーザーがテキスト フィールドを選択すると、[編集] メニューの項目 (コピーや切り取りなど) が有効になります。
Apple と既定では、すべての macOS アプリケーションには、アプリケーションのメニュー バーに表示されるメニューとメニュー項目の標準セットがあります。
- Apple メニュー - このメニューでは、実行中のアプリケーションに関係なく、ユーザーが常に使用できるシステム全体の項目にアクセスできます。 これらの項目は、開発者が変更することはできません。
- [アプリ] メニュー - このメニューには、アプリケーションの名前が太字で表示され、ユーザーが現在実行中のアプリケーションを識別するのに役立ちます。 アプリケーション全体に適用される項目が含まれており、特定のドキュメントやプロセス (アプリケーションの終了など) は含まれません。
- [ファイル] メニュー - アプリケーションで使用するドキュメントを作成、開く、または保存するために使用する項目。 アプリケーションがドキュメント ベースでない場合は、このメニューの名前を変更または削除できます。
- [編集] メニュー - アプリケーションのユーザー インターフェイスで要素を編集または変更するために使用される、切り取り、コピー、貼り付けなどのコマンドを保持します。
- [書式] メニュー - アプリケーションがテキストで動作する場合、このメニューには、そのテキストの書式設定を調整するコマンドが保持されます。
- [表示] メニュー - アプリケーションのユーザー インターフェイスでのコンテンツの表示 (表示) 方法に影響するコマンドを保持します。
- アプリケーション固有の メニュー - アプリケーションに固有のメニュー (Web ブラウザーのブックマーク メニューなど) です。 バーの [表示] メニューと [ウィンドウ] メニューの間に表示されます。
- ウィンドウ メニュー - アプリケーションでウィンドウを操作するためのコマンドと、現在開いているウィンドウの一覧が含まれています。
- ヘルプ メニュー - アプリケーションが画面上のヘルプを提供する場合は、ヘルプ メニューがバーの右端のメニューである必要があります。
アプリケーション メニュー バーと標準メニューとメニュー項目の詳細については、Apple の ヒューマン インターフェイス ガイドラインを参照してください。
既定のアプリケーション メニュー バー
新しい Xamarin.Mac プロジェクトを作成するたびに、macOS アプリケーションが通常持つ一般的な項目を含む標準の既定のアプリケーション メニュー バーが自動的に表示されます (前のセクションで説明したように)。 アプリケーションの既定のメニュー バーは、Solution Pad のプロジェクトの下にある Main.storyboard ファイル (アプリの UI の残りの部分と共に) で定義されます。
Main.storyboard ファイルをダブルクリックして Xcode のインターフェイス ビルダーで編集用に開くと、メニュー エディター インターフェイスが表示されます。
ここから、[ファイル] メニューの [開く] メニュー項目などの項目をクリックし、[属性インスペクター] でそのプロパティを編集または調整できます。
メニューと項目の追加、編集、削除については、この記事の後半で説明します。 ここでは、既定で使用できるメニューとメニュー項目と、定義済みのアウトレットとアクションのセットを介してコードに自動的に公開されている方法を確認するだけです (詳細については、アウトレットとアクションのドキュメントを参照してください)。
たとえば、[開く] メニュー項目の [接続インスペクター] をクリックすると、アクションに自動的にopenDocument:
接続されていることがわかります。
インターフェイス階層で最初のレスポンダーを選択し、[接続インスペクター] で下にスクロールすると、[開く] メニュー項目がアタッチされているアクションのopenDocument:
定義が表示されます (アプリケーションの他のいくつかの既定のアクションと、コントロールに自動的に接続されていないアクション)。
これは、なぜ重要ですか。 次のセクションでは、これらの自動的に定義されたアクションが他の Cocoa ユーザー インターフェイス要素と連携して、メニュー項目を自動的に有効または無効にする方法と、項目の組み込み機能を提供する方法について説明します。
後で、これらの組み込みアクションを使用して、コードの項目を有効または無効にし、選択されたときに独自の機能を提供します。
組み込みのメニュー機能
UI 項目やコードを追加する前に、新しく作成した Xamarin.Mac アプリケーションを実行した場合は、アプリ メニューの [終了] 項目など、一部の項目が自動的にワイヤードアップされ、自動的に有効になっていることがわかります (完全に機能が自動的に組み込まれています)。
切り取り、コピー、貼り付けなどの他のメニュー項目は次のものではありません。
アプリケーションを停止し、Solution Pad の Main.storyboard ファイルをダブルクリックして Xcode のインターフェイス ビルダーで編集するために開きます。 次に、ライブラリからインターフェイス エディターでウィンドウのビュー コントローラーにテキスト ビューをドラッグします。
制約エディターで、テキスト ビューをウィンドウの端にピン留めし、エディターの上部にある 4 つの赤い I ビームをすべてクリックし、[4 つの制約の追加] ボタンをクリックしてウィンドウで拡大および縮小する場所を設定します。
変更をユーザー インターフェイス デザインに保存し、Visual Studio for Mac を切り替えて、変更を Xamarin.Mac プロジェクトと同期します。 次に、アプリケーションを起動し、テキスト ビューにテキストを入力して選択し、[編集] メニューを開きます。
1 行のコードを 記述することなく、切り取り、 コピー、 貼り付けの 項目が自動的に有効になり、完全に機能していることに注目してください。
どうなっているのでしょうか? macOS の一部である Cocoa ユーザー インターフェイス要素のほとんどは、(たとえば copy:
) 特定のアクションへのフックを組み込んでいる、既定のメニュー項目 (上に示したように) に接続された組み込みの定義済みアクションを思い出してください。 そのため、それらがウィンドウに追加され、アクティブになり、選択されると、対応するメニュー項目またはそのアクションに関連付けられている項目が自動的に有効になります。 ユーザーがそのメニュー項目を選択すると、UI 要素に組み込まれている機能が呼び出されて実行されます。すべて開発者の介入なしに実行されます。
メニューと項目の有効化と無効化
既定では、ユーザー イベントが発生するたびに、 NSMenu
アプリケーションのコンテキストに基づいて、表示される各メニュー項目とメニュー項目が自動的に有効および無効になります。 項目を有効または無効にするには、次の 3 つの方法があります。
- [自動メニューの 有効化] - 項目がワイヤードアップされているアクションに応答する適切なオブジェクトが見つかると
NSMenu
、メニュー項目が有効になります。 たとえば、アクションへのフックcopy:
が組み込まれている上のテキスト ビューなどです。 - カスタム アクションと validateMenuItem: - ウィンドウまたはビュー コントローラーのカスタム アクションに バインドされているメニュー項目の場合は、アクションを追加し、メニュー項目を
validateMenuItem:
手動で有効または無効にすることができます。 - [手動で有効にする ] メニュー - メニューの各項目を
Enabled
個別に有効または無効にするように、それぞれのNSMenuItem
プロパティを手動で設定します。
システムを選択するには、次のNSMenu
プロパティを設定しますAutoEnablesItems
。 true
は自動 (既定の動作) であり、 false
手動です。
重要
手動メニュー有効化を使用する場合、AppKit クラス NSTextView
によって制御されるメニュー項目であっても、どのメニュー項目も自動的に更新されません。 コード内のすべての項目を手動で有効または無効にする必要があります。
validateMenuItem の使用
前述のように、ウィンドウまたはビュー コントローラーのカスタム アクションに バインドされているメニュー項目については、アクションを追加し、メニュー項目を validateMenuItem:
手動で有効または無効にすることができます。
次の例では、プロパティを Tag
使用して、アクションで有効または無効にするメニュー項目の種類を validateMenuItem:
、選択したテキスト NSTextView
の状態に基づいて決定します。 このプロパティは Tag
、各メニュー項目のインターフェイス ビルダーで設定されています。
次のコードがビュー コントローラーに追加されました。
[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;
}
このコードが実行され、テキストが選択 NSTextView
されていない場合、2 つの折り返しメニュー項目は無効になります (ビュー コントローラー上のアクションにワイヤリングされている場合でも)。
テキストのセクションが選択され、メニューが再度開かれた場合、2 つの折り返しメニュー項目を使用できます。
コード内のメニュー項目の有効化と応答
前述のように、UI デザインに特定の Cocoa ユーザー インターフェイス要素 (テキスト フィールドなど) を追加するだけで、いくつかの既定のメニュー項目が有効になり、コードを記述しなくても自動的に機能します。 次に、Xamarin.Mac プロジェクトに独自の C# コードを追加してメニュー項目を有効にし、ユーザーが選択したときに機能を提供する方法を見てみましょう。
たとえば、ユーザーが [ファイル] メニューの [開く] 項目を使用してフォルダーを選択できるようにするとします。 これはアプリケーション全体の関数であり、Give ウィンドウまたは UI 要素に限定されないため、これを処理するコードをアプリケーション デリゲートに追加します。
Solution Pad で、ファイルをAppDelegate.CS
ダブルクリックして編集用に開きます。
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 ();
}
}
ここでアプリケーションを実行し、[ファイル] メニューを開きます。
[開く] メニュー項目が有効になっていることに注意してください。 これを選択すると、開いているダイアログが表示されます。
[開く] ボタンをクリックすると、アラート メッセージが表示されます。
ここでの重要な行は[Export ("openDocument:")]
、AppDelegate にアクションにopenDocument:
応答するメソッドvoid OpenDialog (NSObject sender)
があることを示NSMenu
しています。 上記から覚えている場合は、[開く] メニュー項目は、インターフェイス ビルダーで既定でこのアクションに自動的にワイヤードアップされます。
次に、独自のメニュー、メニュー項目、アクションを作成し、コードでそれらに応答する方法を見てみましょう。
最近開いたメニューの操作
既定では、[ファイル] メニューには、ユーザーがアプリで開いた最後のいくつかのファイルを追跡する [最近開く] 項目が含まれています。 ベースの Xamarin.Mac アプリを作成する NSDocument
場合、このメニューは自動的に処理されます。 その他の種類の Xamarin.Mac アプリについては、このメニュー項目を手動で管理および応答する必要があります。
[最近使ったファイルを開く] メニューを手動で処理するには、まず、次のコマンドを使用して新しいファイルが開かれたか保存されたことを通知する必要があります。
// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);
アプリが使用NSDocuments
していない場合でも、ファイルの場所を持つファイルをNSDocumentController
メソッドに送信NSUrl
して[最近開く]メニューをメインをNoteNewRecentDocumentURL
SharedDocumentController
使用します。
次に、アプリ デリゲートのメソッドをOpenFile
オーバーライドして、[最近使ったファイルを開く] メニューからユーザーが選択したファイルを開く必要があります。 次に例を示します。
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;
}
}
ファイルを開くことができる場合は戻 true
り、それ以外の場合は戻り false
、ファイルを開けなかったという組み込みの警告がユーザーに表示されます。
[最近開く] メニューから返されるファイル名とパスにはスペースが含まれている可能性があるため、作成する前にこの文字を適切にエスケープするNSUrl
必要があります。エラーが発生します。 これを行うには、次のコードを使用します。
filename = filename.Replace (" ", "%20");
最後に、ファイルを指すオブジェクトを作成 NSUrl
し、アプリ デリゲートでヘルパー メソッドを使用して新しいウィンドウを開き、ファイルを読み込みます。
var url = new NSUrl ("file://"+filename);
return OpenFile(url);
すべてをまとめるために、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
}
}
アプリの要件に基づいて、ユーザーが同じファイルを複数のウィンドウで同時に開く必要がない場合があります。 このサンプル アプリでは、ユーザーが既に開いているファイル ([最近使ったファイルを開く] メニュー項目または [開く] メニュー項目から) を選択した場合、そのファイルを含むウィンドウが前面に表示されます。
これを実現するために、ヘルパー メソッドで次のコードを使用しました。
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;
}
}
プロパティ内の ViewController
ファイルへのパスを保持するようにクラスを Path
設計しました。 次に、アプリで現在開いているすべてのウィンドウをループ処理します。 ファイルがいずれかのウィンドウで既に開いている場合は、次を使用して他のすべてのウィンドウの前面に表示されます。
NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);
一致するものが見つからない場合は、ファイルが読み込まれた新しいウィンドウが開き、[最近使ったファイルを開く] メニューにファイルが表示されます。
// 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);
カスタム ウィンドウ アクションの操作
標準のメニュー項目に 事前に接続されている組み込みの First Responder アクションと同様に、新しいカスタム アクションを作成し、Interface Builder のメニュー項目に接続できます。
まず、アプリのいずれかのウィンドウ コントローラーでカスタム アクションを定義します。 次に例を示します。
[Action("defineKeyword:")]
public void defineKeyword (NSObject sender) {
// Preform some action when the menu is selected
Console.WriteLine ("Request to define keyword");
}
次に、Solution Pad でアプリのストーリーボード ファイルをダブルクリックして、Xcode のインターフェイス ビルダーで編集するために開きます。 アプリケーション シーンで最初のレスポンダーを選択し、[属性インスペクター] に切り替えます。
属性インスペクターの+下部にあるボタンをクリックして、新しいカスタム アクションを追加します。
ウィンドウ コントローラーで作成したカスタム アクションと同じ名前を付けます。
Control キーを押しながらメニュー項目をクリックし、アプリケーション シーンの下の最初のレスポンダーにドラッグします。 ポップアップ リストから、先ほど作成した新しいアクション (defineKeyword:
この例では) を選択します。
ストーリーボードへの変更を保存し、Visual Studio for Mac に戻って変更を同期します。 アプリを実行すると、カスタム アクションを接続したメニュー項目が (アクションが開いているウィンドウに基づいて) 自動的に有効または無効になり、メニュー項目を選択するとアクションが起動します。
メニューの追加、編集、および削除
前のセクションで説明したように、Xamarin.Mac アプリケーションには、特定の UI コントロールが自動的にアクティブ化および応答する既定のメニューとメニュー項目があらかじめ用意されています。 また、これらの既定の項目を有効にして応答するコードをアプリケーションに追加する方法についても説明しました。
このセクションでは、不要なメニュー項目の削除、メニューの再構成、新しいメニュー、メニュー項目、アクションの追加について説明します。
Solution Pad で Main.storyboard ファイルを ダブルクリックして、編集用に開 きます。
特定の Xamarin.Mac アプリケーションでは、既定 の [表示 ] メニューを使用しないため、削除します。 インターフェイス階層で、メインメニュー バーの一部である [表示] メニュー項目を選択します。
Delete キーまたは Backspace キーを押してメニューを削除します。 次に、[書式] メニューのすべての項目を使用する予定はありません。使用する項目をサブメニューの下から移動します。 インターフェイス階層で、次のメニュー項目を選択します。
親 メニュー の下にある項目を、現在のサブメニューからドラッグします。
メニューは次のようになります。
次に、[書式] メニューの下から [テキスト] サブメニューをドラッグし、[書式] メニューと [ウィンドウ] メニューの間の メイン メニュー バーに配置します。
[書式] メニューの下に戻り、[フォント] サブメニュー項目を削除しましょう。 次に、[書式] メニューを選択し、"Font" という名前を変更します。
次に、テキスト ビューのテキストが選択されたときに自動的に追加される定義済みのフレーズのカスタム メニューを作成しましょう。 ライブラリインスペクターの下部にある検索ボックスに、「メニュー」と入力します。これにより、すべてのメニュー UI 要素を簡単に見つけて操作できるようになります。
次に、メニューを作成するために次の操作を行います。
ライブラリ インスペクターからメニュー バーの [テキスト] メニューと [ウィンドウ] メニューの間にメニュー項目をドラッグします。
項目 "Phrases" の名前を変更します。
次に、ライブラリインスペクターからメニューをドラッグします。
先ほど作成した新しいメニュー項目にメニューをドロップし、その名前を "Phrases" に変更します。
次に、既定 の 3 つのメニュー項目 の名前を "Address"、"Date"、および "Greeting" に変更してみましょう。
ライブラリ インスペクターからメニュー項目をドラッグし、それを "Signature" と呼んで、4 番目のメニュー項目を追加してみましょう。
変更をメニュー バーに保存します。
次に、新しいメニュー項目が C# コードに公開されるように、一連のカスタム アクションを作成してみましょう。 Xcode でアシスタント ビューに切り替えてみましょう。
次の操作を行います。
[アドレス] メニュー項目から AppDelegate.h ファイルに Control キーを押しながらドラッグします。
[接続の種類] を [アクション] に切り替えます。
"phraseAddress" という名前を入力し、[接続] ボタンを押して新しいアクションを作成します。
[日付]、[あいさつ]、[署名] メニュー項目に対して、上記の手順を繰り返します。
変更をメニュー バーに保存します。
次に、コードからコンテンツを調整できるように、テキスト ビューの出口を作成する必要があります。 アシスタント エディターで ViewController.h ファイルを選択し、次の名前documentText
の新しいアウトレットを作成します。
Visual Studio for Mac に戻り、Xcode から変更を同期します。 次に、 ViewController.cs ファイルを編集し、次のようにします。
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
}
}
これにより、クラスの外部にあるテキスト ビューのテキストが ViewController
公開され、ウィンドウがフォーカスを取得または失ったときにアプリ デリゲートに通知されます。 次に、 AppDelegate.cs ファイルを編集し、次のようにします。
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
}
}
ここでは、Interface Builder で定義した AppDelegate
アクションとアウトレットを使用できるように、部分クラスを作成しました。 また、どのウィンドウが現在フォーカスされているかを追跡するためにも公開 textEditor
します。
カスタム メニューとメニュー項目を処理するには、次のメソッドを使用します。
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";
}
アプリケーションを実行すると、[フレーズ] メニューのすべての項目がアクティブになり、選択するとテキスト ビューに give フレーズが追加されます。
アプリケーション メニュー バーの操作の基本を説明したので、カスタム コンテキスト メニューの作成を見てみましょう。
コードからメニューを作成する
Xcode のインターフェイス ビルダーを使用してメニューとメニュー項目を作成するだけでなく、Xamarin.Mac アプリでコードからメニュー、サブメニュー、またはメニュー項目を作成、変更、または削除する必要がある場合があります。
次の例では、その場で動的に作成されるメニュー項目とサブメニューに関する情報を保持するクラスが作成されます。
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
}
}
メニューと項目の追加
このクラスを定義すると、次のルーチンはオブジェクトの LanguageFormatCommand
コレクションを解析し、渡された既存のメニュー (Interface Builder で作成) の下部に追加することで、新しいメニューとメニュー項目を再帰的に作成します。
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);
}
}
空白Title
のプロパティを持つオブジェクトLanguageFormatCommand
の場合、このルーチンは、メニュー セクション間に区切り記号メニュー項目 (薄い灰色の線) を作成します。
menuItem = NSMenuItem.SeparatorItem;
タイトルを指定すると、そのタイトルを含む新しいメニュー項目が作成されます。
menuItem = new NSMenuItem (command.Title);
オブジェクトに LanguageFormatCommand
子 LanguageFormatCommand
オブジェクトが含まれている場合は、サブメニューが作成され、そのメニューを AssembleMenu
構築するためにメソッドが再帰的に呼び出されます。
menuItem.Submenu = new NSMenu (command.Title);
AssembleMenu (menuItem.Submenu, command.SubCommands);
サブメニューがない新しいメニュー項目の場合、ユーザーが選択しているメニュー項目を処理するコードが追加されます。
menuItem.Activated += (sender, e) => {
// Do something when the menu item is selected
...
};
メニュー作成のテスト
上記のすべてのコードを配置して、次のオブジェクトの LanguageFormatCommand
コレクションが作成された場合:
// 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)"));
また、関数にAssembleMenu
渡されたコレクション ([書式] メニューを基本として設定) は、次の動的メニューとメニュー項目が作成されます。
メニューと項目の削除
アプリのユーザー インターフェイスからメニューまたはメニュー項目を削除する必要がある場合は、削除する項目の NSMenu
0 から始まるインデックスを指定するだけで、クラスのメソッドを使用RemoveItemAt
できます。
たとえば、上記のルーチンによって作成されたメニューとメニュー項目を削除するには、次のコードを使用できます。
public void UnpopulateFormattingMenu(NSMenu menu) {
// Remove any additional items
for (int n = (int)menu.Count - 1; n > 4; --n) {
menu.RemoveItemAt (n);
}
}
上記のコードの場合、最初の 4 つのメニュー項目は Xcode のインターフェイス ビルダーで作成され、アプリで使用可能な場所に配置されるため、動的に削除されません。
コンテキスト メニュー
コンテキスト メニューは、ユーザーがウィンドウ内の項目を右クリックまたは control キーを押しながらクリックしたときに表示されます。 既定では、macOS に組み込まれているいくつかの UI 要素には、コンテキスト メニュー (テキスト ビューなど) が既にアタッチされています。 ただし、ウィンドウに追加した UI 要素に対して独自のカスタム コンテキスト メニューを作成したい場合があります。
Xcode で Main.storyboard ファイルを編集し、デザインにウィンドウ ウィンドウを追加し、そのクラスを Identity Inspector で "NSPanel" に設定し、ウィンドウ メニューに新しいアシスタント項目を追加して、Show Segue を使用して新しいウィンドウに添付してみましょう。
次の操作を行います。
ライブラリインスペクタからパネルウィンドウにラベルをドラッグし、そのテキストを「プロパティ」に設定します。
次に、ライブラリインスペクタからビュー階層のビューコントローラにメニューをドラッグし、3つのデフォルトメニュー項目ドキュメント、テキスト、フォントの名前を変更します。
次に、プロパティ ラベルからメニューに control キーを押しながらドラッグします。
ポップアップ ダイアログでメニューを選択 します。
Identity Inspector から、ビュー コントローラーのクラスを "PanelViewController" に設定します。
同期するには Visual Studio for Mac に戻り、インターフェイス ビルダーに戻ります。
アシスタント エディターに切り替え、PanelViewController.h ファイルを選択します。
次の名前
propertyDocument
の [ドキュメント] メニュー項目のアクションを作成します。再メインメニュー項目に対してアクションの作成を繰り返します。
最後に、次のプロパティ
propertyLabel
ラベルのアウトレットを作成します。変更を保存し、Visual Studio for Mac に戻って Xcode と同期します。
PanelViewController.cs ファイルを編集し、次のコードを追加します。
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";
}
アプリケーションを実行し、パネルのプロパティ ラベルを右クリックすると、カスタム コンテキスト メニューが表示されます。 メニューから項目を選択すると、ラベルの値が変更されます。
次に、ステータス バー メニューの作成を見てみましょう。
ステータス バーのメニュー
ステータス バー メニューには、メニューやアプリケーションの状態を反映した画像など、ユーザーとの対話やフィードバックを提供するステータス メニュー項目のコレクションが表示されます。 アプリケーションがバックグラウンドで実行されている場合でも、アプリケーションのステータス バー メニューが有効になり、アクティブになります。 システム全体のステータス バーは、アプリケーション メニュー バーの右側にあり、macOS で現在使用できる唯一のステータス バーです。
AppDelegate.cs ファイルを編集し、メソッドをDidFinishLaunching
次のようにしましょう。
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;
では、システム全体のステータス バーにアクセスできます。 var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable);
は、新しいステータス バー項目を作成します。 そこからメニューといくつかのメニュー項目を作成し、先ほど作成したステータス バー項目にメニューを添付します。
アプリケーションを実行すると、新しいステータス バー項目が表示されます。 メニューから項目を選択すると、テキスト ビューのテキストが変更されます。
次に、カスタム ドック メニュー項目の作成を見てみましょう。
カスタム ドック メニュー
ユーザーがドックでアプリケーションのアイコンを右クリックまたは control キーを押しながらクリックすると、Mac アプリケーションのドック メニューが表示されます。
次の手順を実行して、アプリケーションのカスタム ドック メニューを作成しましょう。
Visual Studio for Mac で、アプリケーションのプロジェクトを右クリックし、[新しいファイルの追加>]を選択します。新しいファイル ダイアログで、Xamarin.Mac の空のインターフェイス定義を選択し、[名前] に "DockMenu" を使用し、[新規] ボタンをクリックして新しい DockMenu.xib ファイルを作成します。>
Solution Pad で DockMenu.xib ファイルをダブルクリックし、Xcode で編集するために開きます。 次の項目を含む新しいメニューを作成します:住所、日付、あいさつ、署名
次に、上記の「メニューの追加、編集、削除」セクションでカスタム メニュー用に 作成した既存のアクションに新しいメニュー 項目を接続してみましょう。 接続インスペクターに切り替え、インターフェイス階層の最初のレスポンダーを選択します。 下にスクロールしてアクションを
phraseAddress:
見つけます。 そのアクションの円から [アドレス] メニュー項目に行をドラッグします。他のすべてのメニュー項目に対して繰り返し、対応するアクションに添付します。
次に、インターフェイス階層内の アプリケーション を 選択します。 接続インスペクターで、コンセントの円
dockMenu
から先ほど作成したメニューに線をドラッグします。変更を保存し、Visual Studio for Mac に戻って Xcode と同期します。
Info.plist ファイルをダブルクリックして、編集用に開きます。
画面の 下部にある [ソース ] タブをクリックします。
[新しいエントリの追加] をクリックし、緑色のプラスボタンをクリックし、プロパティ名を "AppleDockMenu" に設定し、値を "DockMenu" (拡張子のない新しい .xib ファイルの名前) に設定します。
ここで、アプリケーションを実行し、Dock でそのアイコンを右クリックすると、新しいメニュー項目が表示されます。
メニューからカスタム項目の 1 つを選択すると、テキスト ビューのテキストが変更されます。
ポップアップ ボタンとプルダウン リスト
ポップアップ ボタンには、選択した項目が表示され、ユーザーがクリックしたときに選択するオプションの一覧が表示されます。 プルダウン リストは、通常、現在のタスクのコンテキストに固有のコマンドを選択するために使用されるポップアップ ボタンの一種です。 どちらもウィンドウ内の任意の場所に表示できます。
次の手順を実行して、アプリケーションのカスタム ポップアップ ボタンを作成しましょう。
Xcode で Main.storyboard ファイルを編集し、[ライブラリ インスペクター] から [コンテキスト メニュー] セクションで作成したパネル ウィンドウにポップアップ ボタンをドラッグします。
新しいメニュー項目を追加し、[ポップアップ] 内のアイテムのタイトルを [住所]、[日付]、[案内応答]、および [署名] に設定します
次に、上記の「メニューの追加、編集、削除」セクションでカスタム メニュー用に 作成した既存のアクションに、新しいメニュー 項目を接続してみましょう。 接続インスペクターに切り替え、インターフェイス階層の最初のレスポンダーを選択します。 下にスクロールしてアクションを
phraseAddress:
見つけます。 そのアクションの円から [アドレス] メニュー項目に行をドラッグします。他のすべてのメニュー項目に対して繰り返し、対応するアクションに添付します。
変更を保存し、Visual Studio for Mac に戻って Xcode と同期します。
アプリケーションを実行し、ポップアップから項目を選択すると、テキスト ビューのテキストが変更されます。
ポップアップ ボタンとまったく同じ方法で、プルダウン リストを作成して操作できます。 既存のアクションにアタッチする代わりに、[コンテキスト メニュー] セクションのコンテキスト メニューの場合と同様に、独自のカスタム アクションを作成できます。
まとめ
この記事では、Xamarin.Mac アプリケーションでのメニューとメニュー項目の操作について詳しく説明しました。 まず、アプリケーションのメニュー バーを調べ、次にコンテキスト メニューの作成を見て、次にステータス バーのメニューとカスタム ドック メニューを調べました。 最後に、ポップアップ メニューとプルダウン リストについて説明しました。