Listas de origen en Xamarin.Mac

En este artículo se describe cómo trabajar con listas de origen en una aplicación de Xamarin.Mac. Describe cómo crear y mantener listas de código fuente en Xcode Interface Builder e interactuar con ellas en código de C#.

Al trabajar con C# y .NET en una aplicación de Xamarin.Mac, tiene acceso a las mismas listas de origen en las que trabaja un desarrollador Objective-CObjective-C Dado que Xamarin.Mac se integra directamente con Xcode, puede usar el Interface Builder de Xcode para crear y mantener las listas de origen (u, opcionalmente, crearlas directamente en código de C#).

Una lista de origen es un tipo especial de vista esquema que se usa para mostrar el origen de una acción, como la barra lateral en Finder o iTunes.

Una lista de origen de ejemplo

En este artículo, se tratarán los conceptos básicos de trabajar con listas de origen en una aplicación de Xamarin.Mac. Se recomienda encarecidamente trabajar primero en el artículo Hola, Mac, en concreto en las secciones Introducción a Xcode y Interface Builder y Salidas y acciones, ya que trata los conceptos y técnicas clave que se usarán en este artículo.

Es posible que también quiera echar un vistazo a la sección del documento Aspectos internos de Exposing C# classes / methods to Objective-CExposing C# classes / methods to Objective-C donde se explican los comandos y que se usan para conectar las clases de C# a objetos y elementos de la interfaz de RegisterExportObjective-C usuario.

Introducción a las listas de origen

Como se indicó anteriormente, una lista de origen es un tipo especial de vista de esquema que se usa para mostrar el origen de una acción, como la barra lateral en Finder o iTunes. Una lista de origen es un tipo de tabla que permite al usuario expandir o contraer filas de datos jerárquicos. A diferencia de una vista de tabla, los elementos de una lista de origen no están en una lista plana, se organizan en una jerarquía, como archivos y carpetas en un disco duro. Si un elemento de una lista de origen contiene otros elementos, el usuario puede expandirlo o contraerlo.

La lista de origen es una vista de esquema con un estilo especial ( ), que a su vez es una subclase de la vista tabla ( ) y, como tal, hereda gran parte de su comportamiento de su NSOutlineViewNSTableView clase primaria. Como resultado, muchas operaciones compatibles con una vista esquema también son compatibles con una lista de origen. Una aplicación de Xamarin.Mac tiene el control de estas características y puede configurar los parámetros de la lista de origen (ya sea en código o Interface Builder) para permitir o no determinadas operaciones.

Una lista de origen no almacena sus propios datos, sino que se basa en un origen de datos ( ) para proporcionar las filas y columnas necesarias, según sea NSOutlineViewDataSource necesario.

El comportamiento de una lista de origen se puede personalizar proporcionando una subclase del delegado de vista esquema ( ) para admitir el tipo esquema para seleccionar la funcionalidad, la selección y edición de elementos, el seguimiento personalizado y las vistas personalizadas para elementos NSOutlineViewDelegate individuales.

Dado que una lista de origen comparte gran parte de su comportamiento y funcionalidad con una vista de tabla y una vista esquema, es posible que quiera consultar nuestra documentación sobre vistas de tabla y vistas de esquema antes de continuar con este artículo.

Trabajar con listas de origen

Una lista de origen es un tipo especial de vista esquema que se usa para mostrar el origen de una acción, como la barra lateral en Finder o iTunes. A diferencia de las vistas de esquema, antes de definir la lista de origen en Interface Builder, vamos a crear las clases de respaldo en Xamarin.Mac.

En primer lugar, vamos a crear una nueva SourceListItem clase para contener los datos de la lista de origen. En la Explorador de soluciones, haga clic con el botón derecho en el Project y seleccione Agregar nuevo archivo... Seleccione GeneralEmpty Class (Clase vacíageneral), escriba en Name (Nombre) y haga clic en el botón New (Nuevo):

Agregar una clase vacía

Haga que SourceListItem.cs el archivo sea parecido al siguiente:

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
    }
}

En la Explorador de soluciones, haga clic con el botón derecho en el Project y seleccione Agregar nuevo archivo... Seleccione GeneralEmpty Class (Clase vacíageneral), escriba en Name (Nombre) y haga clic en el botón New (Nuevo). Haga que SourceListDataSource.cs el archivo sea parecido al siguiente:

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
    }
}

Esto proporcionará los datos de nuestra lista de origen.

En la Explorador de soluciones, haga clic con el botón derecho en el Project y seleccione Agregar nuevo archivo... Seleccione GeneralEmpty Class (Clase vacíageneral), escriba en Name (Nombre) y haga clic en el botón New (Nuevo). Haga que SourceListDelegate.cs el archivo sea parecido al siguiente:

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
    }
}

Esto proporcionará el comportamiento de nuestra lista de origen.

Por último, en la Explorador de soluciones, haga clic con el botón derecho en el Project y seleccione Agregar nuevo archivo... Seleccione GeneralEmpty Class (Clase vacíageneral), escriba en Name (Nombre) y haga clic en el botón New (Nuevo). Haga que SourceListView.cs el archivo sea parecido al siguiente:

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
    }
}

Esto crea una subclase reutilizable personalizada de ( ) que podemos usar para impulsar la lista de origen en cualquier NSOutlineViewSourceListView aplicación de Xamarin.Mac que realicemos.

Crear y mantener listas de origen en Xcode

Ahora, vamos a diseñar nuestra lista de origen en Interface Builder. Haga doble clic en el archivo para abrirlo para editarlo en Interface Builder y arrastre una vista dividida desde el Inspector de biblioteca, agrégrelo al controlador de vistas y estable para cambiar su tamaño con la vista en el Editor de Main.storyboardrestriccionesMain.storyboard:

Restricciones de edición en el Interface Builder.

A continuación, arrastre una lista de origen desde el Inspectorde biblioteca, agrégrela al lado izquierdo de la vista dividida y estarróla para cambiar su tamaño con la vista en el Editor de restricciones:

Editar restricciones arrastrando una lista de origen a la vista dividida.

A continuación, cambie a la Vista de identidad,seleccione la Lista de origen y cambie su clase a :

Establecimiento del nombre de clase

Por último, cree una salida para nuestra lista de origen llamada en el archivo ViewController.h :

Configuración de una salida

Guarde los cambios y vuelva a Visual Studio para Mac para sincronizar con Xcode.

Rellenar la lista de origen

Vamos a editar el archivo en Visual Studio para Mac para que su método RotationWindow.csAwakeFromNib sea parecido al siguiente:

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);

}

Es necesario llamar al método en la salida de la lista de origen antes de que se le Initialize () agregó Initialize () cualquier elemento. Para cada grupo de elementos, creamos un elemento primario y, a continuación, agregamos los sub items a ese elemento de grupo. A continuación, cada grupo se agrega a la colección de la lista de SourceList.AddItem (...) origen. Las dos últimas líneas cargan los datos de la lista de origen y expanden todos los grupos:

// Display side list
SourceList.ReloadData ();
SourceList.ExpandItem (null, true);

Por último, AppDelegate.cs edite el archivo y haga DidFinishLaunching que el método sea parecido al siguiente:

public override void DidFinishLaunching (NSNotification notification)
{
    mainWindowController = new MainWindowController ();
    mainWindowController.Window.MakeKeyAndOrderFront (this);

    var rotation = new RotationWindowController ();
    rotation.Window.MakeKeyAndOrderFront (this);
}

Si ejecutamos la aplicación, se mostrará lo siguiente:

Ejecución de una aplicación de ejemplo

Resumen

En este artículo se ha detallado cómo trabajar con listas de origen en una aplicación de Xamarin.Mac. Hemos visto cómo crear y mantener listas de origen en el código Interface Builder Xcode y cómo trabajar con listas de origen en código C#.