次の方法で共有


Xamarin.Mac のソース リスト

この記事では、Xamarin.Mac アプリケーションでのソース リストの使用について説明しています。 Xcode および Interface Builder でのソース リストの作成と管理、および C# コードでのソース リストの操作について説明します。

Xamarin.Mac アプリケーションで C# と .NET を使用している場合、Objective-CXcode で開発者が使用しているのと同じソース リストにアクセスできます。 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 ファイルをダブルクリックで開き、[ライブラリ インスペクター] から分割ビューをドラッグし、ビュー コントローラーに追加して、制約エディターのビューでサイズを変更するように設定します。

Interface Builder での制約の編集。

次に、[ライブラリインスペクター] からソース リストをドラッグし、分割ビューの左側に追加し、制約エディターのビューでサイズを変更するように設定します。

ソース リストを分割ビューにドラッグして制約を編集する。

次に、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 () メソッドを呼び出す必要があります。 項目のグループごとに、親項目を作成し、そのグループ項目にサブ項目を追加します。 その後、各グループがソース リストのコレクション 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# コードでソース リストを操作する方法について説明しました。