Xamarin.Mac のソース リスト
この記事では、Xamarin.Mac アプリケーションでのソース リストの使用について説明しています。 Xcode および Interface Builder でのソース リストの作成と管理、および C# コードでのソース リストの操作について説明します。
Xamarin.Mac アプリケーションで C# と .NET を使用している場合、Objective-C や Xcode で開発者が使用しているのと同じソース リストにアクセスできます。 Xamarin.Mac は直接 Xcode と統合できるため、Xcode の Interface Builder を使用して、ソース リストを作成および管理できます (または、必要に応じて、C# コードで直接作成することもできます)。
ソース リストは、Finder や iTunes のサイド バーなど、アクションのソースを表示するために使用される特殊な種類のアウトライン ビューです。
この記事では、Xamarin.Mac アプリケーションでのソース リストの使用の基本について説明します。 この記事で使用する主要な概念と手法については、まず Hello Mac の記事、特に「Xcode と Interface Builder の概要」および「アウトレットとアクション」のセクションを参照することを強くお勧めします。
Xamarin.Mac Internals ドキュメントの 「C# クラス/メソッドの Objective-C への公開」のセクションも参照することをお勧めします。C# クラスを Objective-C オブジェクトと UI 要素に結び付けるために使われる Register
および Export
コマンドについて説明されています。
ソース リストの概要
前述のように、ソース リストは、Finder や iTunes のサイド バーなど、アクションのソースを表示するために使用される特殊な種類のアウトライン ビューです。 ソース リストとは、ユーザーが階層データの行を展開または折りたたむことができるテーブルの一種です。 テーブル ビューとは異なり、ソース リスト内の項目はフラット リストに含まれていないため、ハード ドライブ上のファイルやフォルダーなどの階層に編成されます。 ソース リスト内のアイテムに他の項目が含まれている場合は、ユーザーが展開または折りたたむことができます。
ソース リストは、特別なスタイルのアウトライン ビュー (NSOutlineView
) で、それ自体がテーブル ビュー (NSTableView
) のサブクラスであるため、その動作の多くを親クラスから継承します。 その結果、アウトライン ビューでサポートされている多くの操作が、ソース リストでもサポートされます。 Xamarin.Mac アプリケーションは、これらの機能を制御でき、ソース リストのパラメータ (コードまたは Interface Builder) を構成して、特定の操作を許可または禁止できます。
ソース リストは、独自のデータを格納するのではなく、必要に応じて必要な行と列の両方を提供するデータ ソース (NSOutlineViewDataSource
) に依存しています。
ソース リストの動作をカスタマイズするには、アウトラインの種類をサポートするアウトライン ビュー デリゲートのサブクラス (NSOutlineViewDelegate
) を指定して、個々の項目の機能、項目の選択と編集、カスタム追跡、カスタム ビューを選択できます。
ソース リストは、テーブル ビューとアウトライン ビューで動作と機能の多くを共有しているため、この記事を読み進める前に、テーブル ビューとアウトライン ビューのドキュメントを参照することをお勧めします。
ソース リストの操作
ソース リストは、Finder や iTunes のサイド バーなど、アクションのソースを表示するために使用される特殊な種類のアウトライン ビューです。 アウトライン ビューとは異なり、Interface Builder でソース リストを定義する前に、Xamarin.Mac でバッキング クラスを作成しましょう。
まず、ソース リストのデータを保持する新しい SourceListItem
クラスを作成しましょう。 ソリューション エクスプローラーでプロジェクトを右クリックし、[追加]>[新規ファイル...] を選択します[全般]>[空のクラス] を選択し、[名前] で「SourceListItem
」と入力して、[新規] ボタンをクリックします。
SourceListItem.cs
ファイルを次のようにします。
using System;
using System.Collections;
using System.Collections.Generic;
using AppKit;
using Foundation;
namespace MacOutlines
{
public class SourceListItem: NSObject, IEnumerator, IEnumerable
{
#region Private Properties
private string _title;
private NSImage _icon;
private string _tag;
private List<SourceListItem> _items = new List<SourceListItem> ();
#endregion
#region Computed Properties
public string Title {
get { return _title; }
set { _title = value; }
}
public NSImage Icon {
get { return _icon; }
set { _icon = value; }
}
public string Tag {
get { return _tag; }
set { _tag=value; }
}
#endregion
#region Indexer
public SourceListItem this[int index]
{
get
{
return _items[index];
}
set
{
_items[index] = value;
}
}
public int Count {
get { return _items.Count; }
}
public bool HasChildren {
get { return (Count > 0); }
}
#endregion
#region Enumerable Routines
private int _position = -1;
public IEnumerator GetEnumerator()
{
_position = -1;
return (IEnumerator)this;
}
public bool MoveNext()
{
_position++;
return (_position < _items.Count);
}
public void Reset()
{_position = -1;}
public object Current
{
get
{
try
{
return _items[_position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
#endregion
#region Constructors
public SourceListItem ()
{
}
public SourceListItem (string title)
{
// Initialize
this._title = title;
}
public SourceListItem (string title, string icon)
{
// Initialize
this._title = title;
this._icon = NSImage.ImageNamed (icon);
}
public SourceListItem (string title, string icon, ClickedDelegate clicked)
{
// Initialize
this._title = title;
this._icon = NSImage.ImageNamed (icon);
this.Clicked = clicked;
}
public SourceListItem (string title, NSImage icon)
{
// Initialize
this._title = title;
this._icon = icon;
}
public SourceListItem (string title, NSImage icon, ClickedDelegate clicked)
{
// Initialize
this._title = title;
this._icon = icon;
this.Clicked = clicked;
}
public SourceListItem (string title, NSImage icon, string tag)
{
// Initialize
this._title = title;
this._icon = icon;
this._tag = tag;
}
public SourceListItem (string title, NSImage icon, string tag, ClickedDelegate clicked)
{
// Initialize
this._title = title;
this._icon = icon;
this._tag = tag;
this.Clicked = clicked;
}
#endregion
#region Public Methods
public void AddItem(SourceListItem item) {
_items.Add (item);
}
public void AddItem(string title) {
_items.Add (new SourceListItem (title));
}
public void AddItem(string title, string icon) {
_items.Add (new SourceListItem (title, icon));
}
public void AddItem(string title, string icon, ClickedDelegate clicked) {
_items.Add (new SourceListItem (title, icon, clicked));
}
public void AddItem(string title, NSImage icon) {
_items.Add (new SourceListItem (title, icon));
}
public void AddItem(string title, NSImage icon, ClickedDelegate clicked) {
_items.Add (new SourceListItem (title, icon, clicked));
}
public void AddItem(string title, NSImage icon, string tag) {
_items.Add (new SourceListItem (title, icon, tag));
}
public void AddItem(string title, NSImage icon, string tag, ClickedDelegate clicked) {
_items.Add (new SourceListItem (title, icon, tag, clicked));
}
public void Insert(int n, SourceListItem item) {
_items.Insert (n, item);
}
public void RemoveItem(SourceListItem item) {
_items.Remove (item);
}
public void RemoveItem(int n) {
_items.RemoveAt (n);
}
public void Clear() {
_items.Clear ();
}
#endregion
#region Events
public delegate void ClickedDelegate();
public event ClickedDelegate Clicked;
internal void RaiseClickedEvent() {
// Inform caller
if (this.Clicked != null)
this.Clicked ();
}
#endregion
}
}
ソリューション エクスプローラーでプロジェクトを右クリックし、[追加]>[新規ファイル...] を選択します[全般]>[空のクラス] を選択し、[名前] で「SourceListDataSource
」と入力して、[新規] ボタンをクリックします。 SourceListDataSource.cs
ファイルを次のようにします。
using System;
using System.Collections;
using System.Collections.Generic;
using AppKit;
using Foundation;
namespace MacOutlines
{
public class SourceListDataSource : NSOutlineViewDataSource
{
#region Private Variables
private SourceListView _controller;
#endregion
#region Public Variables
public List<SourceListItem> Items = new List<SourceListItem>();
#endregion
#region Constructors
public SourceListDataSource (SourceListView controller)
{
// Initialize
this._controller = controller;
}
#endregion
#region Override Properties
public override nint GetChildrenCount (NSOutlineView outlineView, Foundation.NSObject item)
{
if (item == null) {
return Items.Count;
} else {
return ((SourceListItem)item).Count;
}
}
public override bool ItemExpandable (NSOutlineView outlineView, Foundation.NSObject item)
{
return ((SourceListItem)item).HasChildren;
}
public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, Foundation.NSObject item)
{
if (item == null) {
return Items [(int)childIndex];
} else {
return ((SourceListItem)item) [(int)childIndex];
}
}
public override NSObject GetObjectValue (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
{
return new NSString (((SourceListItem)item).Title);
}
#endregion
#region Internal Methods
internal SourceListItem ItemForRow(int row) {
int index = 0;
// Look at each group
foreach (SourceListItem item in Items) {
// Is the row inside this group?
if (row >= index && row <= (index + item.Count)) {
return item [row - index - 1];
}
// Move index
index += item.Count + 1;
}
// Not found
return null;
}
#endregion
}
}
これにより、ソース リストのデータが提供されます。
ソリューション エクスプローラーでプロジェクトを右クリックし、[追加]>[新規ファイル...] を選択します[全般]>[空のクラス] を選択し、[名前] で「SourceListDelegate
」と入力して、[新規] ボタンをクリックします。 SourceListDelegate.cs
ファイルを次のようにします。
using System;
using AppKit;
using Foundation;
namespace MacOutlines
{
public class SourceListDelegate : NSOutlineViewDelegate
{
#region Private variables
private SourceListView _controller;
#endregion
#region Constructors
public SourceListDelegate (SourceListView controller)
{
// Initialize
this._controller = controller;
}
#endregion
#region Override Methods
public override bool ShouldEditTableColumn (NSOutlineView outlineView, NSTableColumn tableColumn, Foundation.NSObject item)
{
return false;
}
public override NSCell GetCell (NSOutlineView outlineView, NSTableColumn tableColumn, Foundation.NSObject item)
{
nint row = outlineView.RowForItem (item);
return tableColumn.DataCellForRow (row);
}
public override bool IsGroupItem (NSOutlineView outlineView, Foundation.NSObject item)
{
return ((SourceListItem)item).HasChildren;
}
public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
{
NSTableCellView view = null;
// Is this a group item?
if (((SourceListItem)item).HasChildren) {
view = (NSTableCellView)outlineView.MakeView ("HeaderCell", this);
} else {
view = (NSTableCellView)outlineView.MakeView ("DataCell", this);
view.ImageView.Image = ((SourceListItem)item).Icon;
}
// Initialize view
view.TextField.StringValue = ((SourceListItem)item).Title;
// Return new view
return view;
}
public override bool ShouldSelectItem (NSOutlineView outlineView, Foundation.NSObject item)
{
return (outlineView.GetParent (item) != null);
}
public override void SelectionDidChange (NSNotification notification)
{
NSIndexSet selectedIndexes = _controller.SelectedRows;
// More than one item selected?
if (selectedIndexes.Count > 1) {
// Not handling this case
} else {
// Grab the item
var item = _controller.Data.ItemForRow ((int)selectedIndexes.FirstIndex);
// Was an item found?
if (item != null) {
// Fire the clicked event for the item
item.RaiseClickedEvent ();
// Inform caller of selection
_controller.RaiseItemSelected (item);
}
}
}
#endregion
}
}
これにより、ソース リストの動作が提供されます。
最後に、ソリューション エクスプローラーでプロジェクトを右クリックし、[追加>[新規ファイル...] を選択します[全般]>[空のクラス] を選択し、[名前] で「SourceListView
」と入力して、[新規] ボタンをクリックします。 SourceListView.cs
ファイルを次のようにします。
using System;
using AppKit;
using Foundation;
namespace MacOutlines
{
[Register("SourceListView")]
public class SourceListView : NSOutlineView
{
#region Computed Properties
public SourceListDataSource Data {
get {return (SourceListDataSource)this.DataSource; }
}
#endregion
#region Constructors
public SourceListView ()
{
}
public SourceListView (IntPtr handle) : base(handle)
{
}
public SourceListView (NSCoder coder) : base(coder)
{
}
public SourceListView (NSObjectFlag t) : base(t)
{
}
#endregion
#region Override Methods
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
}
#endregion
#region Public Methods
public void Initialize() {
// Initialize this instance
this.DataSource = new SourceListDataSource (this);
this.Delegate = new SourceListDelegate (this);
}
public void AddItem(SourceListItem item) {
if (Data != null) {
Data.Items.Add (item);
}
}
#endregion
#region Events
public delegate void ItemSelectedDelegate(SourceListItem item);
public event ItemSelectedDelegate ItemSelected;
internal void RaiseItemSelected(SourceListItem item) {
// Inform caller
if (this.ItemSelected != null) {
this.ItemSelected (item);
}
}
#endregion
}
}
これにより、NSOutlineView
(SourceListView
) の再利用可能なカスタム サブクラスが作成されます。これを使用して、作成した任意の Xamarin.Mac アプリケーションでソース リストを駆動できます。
Xcode でのソース リストの作成と管理
次に、Interface Builder でソース リストを設計しましょう。 Interface Builder で編集するために Main.storyboard
ファイルをダブルクリックで開き、[ライブラリ インスペクター] から分割ビューをドラッグし、ビュー コントローラーに追加して、制約エディターのビューでサイズを変更するように設定します。
次に、[ライブラリインスペクター] からソース リストをドラッグし、分割ビューの左側に追加し、制約エディターのビューでサイズを変更するように設定します。
次に、ID ビューに切り替え、ソース リストを選択して、そのクラスを SourceListView
に変更します。
最後に、ViewController.h
ファイルに SourceList
というソース リストのアウトレットを作成します。
変更内容を保存し、Visual Studio for Mac に戻って Xcode と同期します。
ソース リストの設定
Visual Studio for Mac で RotationWindow.cs
ファイルを編集し、その AwakeFromNib
メソッドを次のようにしてみましょう。
public override void AwakeFromNib ()
{
base.AwakeFromNib ();
// Populate source list
SourceList.Initialize ();
var library = new SourceListItem ("Library");
library.AddItem ("Venues", "house.png", () => {
Console.WriteLine("Venue Selected");
});
library.AddItem ("Singers", "group.png");
library.AddItem ("Genre", "cards.png");
library.AddItem ("Publishers", "box.png");
library.AddItem ("Artist", "person.png");
library.AddItem ("Music", "album.png");
SourceList.AddItem (library);
// Add Rotation
var rotation = new SourceListItem ("Rotation");
rotation.AddItem ("View Rotation", "redo.png");
SourceList.AddItem (rotation);
// Add Kiosks
var kiosks = new SourceListItem ("Kiosks");
kiosks.AddItem ("Sign-in Station 1", "imac");
kiosks.AddItem ("Sign-in Station 2", "ipad");
SourceList.AddItem (kiosks);
// Display side list
SourceList.ReloadData ();
SourceList.ExpandItem (null, true);
}
Initialize ()
メソッドは、ソース リストの Outlet before に対して呼び出す必要があります。 項目のグループごとに、親項目を作成し、そのグループ項目にサブ項目を追加します。 その後、各グループがソース リストのコレクション SourceList.AddItem (...)
に追加されます。 最後の 2 行はソース リストのデータを読み込み、すべてのグループを展開します。
// Display side list
SourceList.ReloadData ();
SourceList.ExpandItem (null, true);
最後に、AppDelegate.cs
ファイルを編集し、DidFinishLaunching
メソッドを次のようにします。
public override void DidFinishLaunching (NSNotification notification)
{
mainWindowController = new MainWindowController ();
mainWindowController.Window.MakeKeyAndOrderFront (this);
var rotation = new RotationWindowController ();
rotation.Window.MakeKeyAndOrderFront (this);
}
アプリケーションを実行すると、以下のように表示されます。
まとめ
この記事では、Xamarin.Mac アプリケーションでのソース リストの使用について詳しく説明しました。 Xcode の Interface Builder でソース リストを作成および管理する方法と、C# コードでソース リストを操作する方法について説明しました。