共用方式為


Xamarin.Mac 中的來源清單

本文涵蓋在 Xamarin.Mac 應用程式中使用來源清單。 它描述如何在 Xcode 和 Interface Builder 中建立和維護來源清單,並在 C# 程式代碼中與其互動。

在 Xamarin.Mac 應用程式中使用 C# 和 .NET 時,您可以存取開發人員在 和 XcodeObjective-C運作的相同來源清單。 由於 Xamarin.Mac 直接與 Xcode 整合,因此您可以使用 Xcode 的 介面產生器 來建立和維護來源清單(或選擇性地直接在 C# 程式代碼中建立它們)。

[來源清單] 是一種特殊的大綱檢視類型,用來顯示動作的來源,例如 Finder 或 iTunes 中的側邊列。

範例來源清單

在本文中,我們將討論在 Xamarin.Mac 應用程式中使用來源清單的基本概念。 強烈建議您先完成 Hello,Mac 文章,特別是 Xcode 和 Interface Builder 和 Outlets 和 Actions 簡介小節,因為它涵蓋我們將在本文中使用的重要概念和技術。

您可能也想要查看 Xamarin.Mac Internals 檔的公開 C# 類別/方法Objective-C一節,它也會說明 Register 用來將 C# 類別連接至Objective-C物件和 UI 元素的 和 Export 命令。

來源清單簡介

如上所述,[來源清單] 是特殊類型的大綱檢視,用來顯示動作的來源,例如 Finder 或 iTunes 中的側邊列。 [來源清單] 是一種數據表類型,可讓用戶展開或折疊階層式數據的數據列。 不同於數據表檢視,來源清單中的專案不在一般清單中,它們會組織在階層中,例如硬碟上的檔案和資料夾。 如果 [來源清單] 中的專案包含其他專案,使用者就可以展開或折迭該專案。

[來源清單] 是特別樣式的大綱檢視 (NSOutlineView),本身是數據表檢視 (NSTableView) 的子類別,因此會繼承其父類別的大部分行為。 因此,[大綱檢視] 也支援許多作業。來源清單也支援。 Xamarin.Mac 應用程式可控制這些功能,並可設定來源清單的參數(在程式代碼或介面產生器中),以允許或不允許某些作業。

來源清單不會儲存自己的數據,而是依賴數據源 (NSOutlineViewDataSource) 視需要提供所需的數據列和數據行。

您可以藉由提供大綱檢視委派的子類別來NSOutlineViewDelegate自定義來源清單的行為,以支援大綱類型來選取功能、項目選取和編輯、自定義追蹤,以及個別專案的自定義檢視。

由於來源清單會與數據表檢視和大綱檢視共用大部分的行為和功能,因此建議您先瀏覽我們的數據表檢視和大綱檢視檔,再繼續進行本文。

使用來源清單

[來源清單] 是一種特殊的大綱檢視類型,用來顯示動作的來源,例如 Finder 或 iTunes 中的側邊列。 不同於大綱檢視,在介面產生器中定義來源清單之前,讓我們在 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 中建立和維護來源清單

現在,讓我們在介面產生器中設計來源清單。 雙擊檔案以在介面產生器中開啟以進行編輯,並從連結Main.storyboard庫偵測器拖曳分割檢視、將它新增至檢視控制器,並將它設定為使用 [條件約束編輯器] 中的 [檢視] 重設大小:

在介面產生器中編輯條件約束。

接下來,從 [鏈接庫偵測器] 拖曳 [來源列表],將它新增至 [分割檢視] 左側,並將它設定為使用 [條件約束編輯器] 中的 [檢視] 重設大小:

將 [來源清單] 拖曳至 [分割檢視] 來編輯條件約束。

接下來,切換至 [身分識別檢視],選取 [來源清單],並將它變更為 [類別] :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 (...)。 最後兩行會載入來源清單的數據,並展開所有群組:

// 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 的介面產生器中建立和維護來源清單,以及如何在 C# 程式代碼中使用源清單。