Listas de origen en Xamarin.Mac
En este artículo, se explica cómo trabajar con listas de origen en una aplicación de Xamarin.Mac. Describe cómo crear y mantener listas de origen en Xcode e Interface Builder e interactuar con ellas en el código de C#.
Cuando se trabaja con C# y .NET en una aplicación de Xamarin.Mac, se tiene acceso a las mismas alertas que un desarrollador que trabaje en Objective-C y Xcode. Dado que Xamarin.Mac se integra directamente con Xcode, puede usar Interface Builder de Xcode para crear y mantener las vistas de tabla (u opcionalmente crearlas directamente en código C#).
Una lista de origen es un tipo especial de vista esquema utilizada para mostrar el origen de una acción, como la barra lateral en Finder o iTunes.
En este artículo, trataremos los conceptos básicos de trabajar con alertas en una aplicación de Xamarin.Mac. Se recomienda encarecidamente primero revisar el artículo Hello, Mac; específicamente las secciones Introducción a Xcode e Interface Builder y Salidas y acciones, ya que se abordan conceptos clave y técnicas que usaremos en este artículo.
También puede echar un vistazo a la sección Exponer clases o métodos de C# a Objective-C del documento sobre el funcionamiento interno de Xamarin.Mac, ya que en ella se explican los atributos Register
y Export
que se usan para conectar las clases de C# a objetos Objective-C y elementos de la interfaz de usuario.
Introducción a las listas de origen
Como se indicó anteriormente, una lista de origen es un tipo especial de vista esquema utilizada 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 orígenes es una vista esquema con estilo especial (NSOutlineView
), que es una subclase de la vista de tabla (NSTableView
) y, como tal, hereda gran parte de su comportamiento de su 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 control de estas características y puede configurar los parámetros de la lista de código fuente (ya sea en el código o en el Interface Builder) para permitir o denegar determinadas operaciones.
Una vista de tabla no almacena sus propios datos, sino que se basa en un origen de datos (NSOutlineViewDataSource
) para proporcionar las filas y columnas pertinentes según sea necesario.
El comportamiento de una lista de origen se puede personalizar proporcionando una subclase del delegado de vista de esquema (NSOutlineViewDelegate
) para admitir el tipo de esquema para seleccionar la funcionalidad, la selección de elementos y la edición, el seguimiento personalizado y las vistas personalizadas para elementos individuales.
Dado que una lista de orígenes comparte gran parte del comportamiento y la funcionalidad con una vista de tabla y una vista esquema, es posible que desee pasar por nuestra documentación de 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 utilizada 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 nuestra lista de orígenes en el Interface Builder, vamos a crear las clases de respaldo en Xamarin.Mac.
En primer lugar, vamos a crear una nueva SourceListItem
clase para almacenar los datos de nuestra lista de orígenes. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>Nuevo archivo.... Seleccione General>Clase vacía, escriba SourceListItem
para el Nombre y haga clic en el botón Nuevo:
Edite el archivo SourceListItem.cs
para que quede de la siguiente manera:
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 el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>Nuevo archivo.... Seleccione General>Clase vacía, escriba SourceListDataSource
para el Nombre y haga clic en el botón Nuevo. Edite el archivo SourceListDataSource.cs
para que quede de la siguiente manera:
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 orígenes.
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>Nuevo archivo.... Seleccione General>Clase vacía, escriba SourceListDelegate
para el Nombre y haga clic en el botón Nuevo. Edite el archivo SourceListDelegate.cs
para que quede de la siguiente manera:
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 orígenes.
En el Explorador de soluciones, haga clic con el botón derecho en el proyecto y seleccione Agregar>Nuevo archivo.... Seleccione General>Clase vacía, escriba SourceListView
para el Nombre y haga clic en el botón Nuevo. Edite el archivo SourceListView.cs
para que quede de la siguiente manera:
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 personalizada y reutilizable de NSOutlineView
(SourceListView
) que podemos usar para controlar la lista de origen en cualquier aplicación de Xamarin.Mac que realicemos.
Creación y mantenimiento de listas de origen en Xcode
Ahora, vamos a diseñar nuestra lista de orígenes en el Interface Builder. Haga doble clic en el Main.storyboard
archivo para abrirlo para editarlo en el Interface Builder y arrastre una vista dividida desde el Inspectorde biblioteca, agréguelo al controlador de vista y establézcalo para cambiar el tamaño con la vista en el Editor de restricciones:
A continuación, arrastre una lista de origen desde el Inspectorde biblioteca, agréguela al lado izquierdo de la vista dividida y establézcala para cambiar el tamaño con la vista en el Editor de restricciones:
A continuación, cambie a la Vista de identidad, seleccione la lista de origen y cambie su clase a SourceListView
:
Por último, cree una salida para nuestra lista de orígenes denominada SourceList
en el ViewController.h
archivo :
Guarde los cambios y vuelva a Visual Studio para Mac para sincronizarlo con Xcode.
Rellenar la lista de origen
Vamos a editar el RotationWindow.cs
archivo en Visual Studio para Mac y hacer que el método sea AwakeFromNib
similar 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 Initialize ()
necesario llamar al método en la salida de la lista de origen antes de que se agreguen elementos a él. Para cada grupo de elementos, creamos un elemento primario y, a continuación, agregamos los elementos secundarios a ese elemento de grupo. A continuación, cada grupo se agrega a la colección de la lista de origen SourceList.AddItem (...)
. Las dos últimas líneas cargan los datos de la lista de orígenes y expanden todos los grupos:
// Display side list
SourceList.ReloadData ();
SourceList.ExpandItem (null, true);
Edite el archivo AppDelegate.cs
y haga que el método DidFinishLaunching
se parezca a lo siguiente:
public override void DidFinishLaunching (NSNotification notification)
{
mainWindowController = new MainWindowController ();
mainWindowController.Window.MakeKeyAndOrderFront (this);
var rotation = new RotationWindowController ();
rotation.Window.MakeKeyAndOrderFront (this);
}
Si ejecutamos nuestra aplicación, aparecerá lo siguiente:
Resumen
En este artículo se ha echado un vistazo detallado al trabajo con listas de fuentes en una aplicación Xamarin.Mac. Hemos visto cómo crear y mantener listas de origen en el Interface Builder de Xcode y cómo trabajar con listas de origen en código de C#.
Vínculos relacionados
- Hello, Mac
- Vistas de tabla
- Vistas de esquema
- OS X Human Interface Guidelines (Directrices de interfaz humana de OS X)
- Introducción a las vistas de esquema
- NSOutlineView
- NSOutlineViewDataSource
- NSOutlineViewDelegate