Исходные списки в Xamarin.Mac

В этой статье рассматривается работа с исходными списками в приложении Xamarin.Mac. В нем описывается создание и обслуживание исходных списков в Xcode и Конструкторе интерфейсов и взаимодействие с ними в коде C#.

При работе с C# и .NET в приложении Xamarin.Mac у вас есть доступ к тем же спискам источников, в которых работает Objective-C разработчик, и Xcode . Так как Xamarin.Mac интегрируется непосредственно с Xcode, с помощью конструктора интерфейсов Xcode можно создавать и поддерживать исходные списки (или при необходимости создавать их непосредственно в коде C#).

Исходный список — это особый тип представления структуры, используемый для отображения источника действия, например боковой панели в Finder или iTunes.

Пример исходного списка

В этой статье мы рассмотрим основы работы с исходными списками в приложении Xamarin.Mac. Настоятельно рекомендуется сначала ознакомиться со статьей Hello, Mac , в частности в разделах "Введение в Xcode" и "Конструктор интерфейсов" и "Торговых точек" и "Действия ", поскольку рассматриваются основные понятия и методы, которые мы будем использовать в этой статье.

Вы можете ознакомиться с классами И методами C# вObjective-Cразделе документа Xamarin.Mac Internals, а также объяснить RegisterExport команды, используемые для подключения классов C# к Objective-C объектам и элементам пользовательского интерфейса.

Общие сведения о исходных списках

Как упоминалось выше, исходный список — это особый тип представления структуры, используемый для отображения источника действия, например боковой панели в 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

Задание имени класса

Наконец, создайте выход для нашего исходного списка, вызываемого SourceListViewController.h в файле:

Настройка выхода

Сохраните изменения и вернитесь к Visual Studio для Mac для синхронизации с Xcode.

Заполнение исходного списка

Давайте отредактируем RotationWindow.cs файл в Visual Studio для Mac и сделайте его 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#.