Compartilhar via


Listas de fontes no Xamarin.Mac

Este artigo aborda o trabalho com listas de código-fonte em um aplicativo Xamarin.Mac. Ele descreve a criação e manutenção de listas de fontes no Xcode e no Interface Builder e a interação com elas em código C#.

Ao trabalhar com C# e .NET em um aplicativo Xamarin.Mac, você tem acesso às mesmas Listas de Código-Fonte que um desenvolvedor trabalhando e Objective-Co Xcode . Como o Xamarin.Mac se integra diretamente ao Xcode, você pode usar o Construtor de Interfaces do Xcode para criar e manter suas Listas de Códigos-Fonte (ou, opcionalmente, criá-las diretamente no código C#).

Uma Lista de Fontes é um tipo especial de Modo de Exibição de Estrutura de Tópicos usado para mostrar a origem de uma ação, como a barra lateral no Finder ou no iTunes.

Um exemplo de lista de fontes

Neste artigo, abordaremos os conceitos básicos de como trabalhar com listas de código-fonte em um aplicativo Xamarin.Mac. É altamente recomendável que você trabalhe primeiro no artigo Olá, Mac, especificamente nas seções Introdução ao Xcode e ao Construtor de Interface e Saídas e Ações, pois ele aborda os principais conceitos e técnicas que usaremos neste artigo.

Você pode querer dar uma olhada na seção Expondo classes C# / métodos para Objective-C do documento Xamarin.Mac Internals também, ele explica os Register comandos e Export usados para conectar suas classes C# a Objective-C objetos e elementos da interface do usuário.

Introdução às listas de fontes

Como dito acima, uma Lista de Fontes é um tipo especial de Exibição de Estrutura de Tópicos usado para mostrar a origem de uma ação, como a barra lateral no Finder ou no iTunes. Uma Lista de Fontes é um tipo de Tabela que permite ao usuário expandir ou recolher linhas de dados hierárquicos. Ao contrário de um Modo de Exibição de Tabela, os itens em uma Lista de Origem não estão em uma lista simples, eles são organizados em uma hierarquia, como arquivos e pastas em um disco rígido. Se um item em uma Lista de Fontes contiver outros itens, ele poderá ser expandido ou recolhido pelo usuário.

A Lista de Códigos-Fonte é uma Exibição de Estrutura de Tópicos (NSOutlineView), especialmente estilizada, que é uma subclasse da Exibição de Tabela (NSTableView) e, como tal, herda grande parte de seu comportamento de sua classe pai. Como resultado, muitas operações suportadas por um Modo de Exibição de Estrutura de Tópicos também são suportadas por uma Lista de Origem. Um aplicativo Xamarin.Mac tem controle desses recursos e pode configurar os parâmetros da Lista de Códigos-Fonte (no código ou no Construtor de Interfaces) para permitir ou não determinadas operações.

Uma Lista de Fontes não armazena seus próprios dados, em vez disso, ela depende de uma Fonte de Dados (NSOutlineViewDataSource) para fornecer as linhas e colunas necessárias, conforme necessário.

O comportamento de uma Lista de Origem pode ser personalizado fornecendo uma subclasse do Delegado de Exibição de Estrutura de Tópicos (NSOutlineViewDelegate) para oferecer suporte ao tipo de Estrutura de Tópicos para selecionar funcionalidade, seleção e edição de itens, controle personalizado e modos de exibição personalizados para itens individuais.

Como uma Lista de Fontes compartilha muito de seu comportamento e funcionalidade com um Modo de Exibição de Tabela e um Modo de Exibição de Estrutura de Tópicos, convém examinar nossa documentação de Modos de Exibição de Tabela e Modos de Exibição de Estrutura de Tópicos antes de continuar com este artigo.

Trabalhando com listas de código-fonte

Uma Lista de Fontes é um tipo especial de Modo de Exibição de Estrutura de Tópicos usado para mostrar a origem de uma ação, como a barra lateral no Finder ou no iTunes. Ao contrário das Exibições de Estrutura de Tópicos, antes de definirmos nossa Lista de Códigos-Fonte no Construtor de Interfaces, vamos criar as classes de suporte no Xamarin.Mac.

Primeiro, vamos criar uma nova SourceListItem classe para armazenar os dados para nossa Lista de Fontes. No Gerenciador de Soluções, clique com o botão direito do mouse no Projeto e selecione Adicionar>Novo Arquivo... Selecione General>Empty Class, digite SourceListItem para o Nome e clique no botão Novo:

Adicionando uma classe vazia

Faça com que o SourceListItem.cs arquivo tenha a seguinte aparência:

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

No Gerenciador de Soluções, clique com o botão direito do mouse no Projeto e selecione Adicionar>Novo Arquivo... Selecione General>Empty Class, digite SourceListDataSource para o Nome e clique no botão Novo. Faça com que o SourceListDataSource.cs arquivo tenha a seguinte aparência:

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

Isso fornecerá os dados para nossa Lista de Fontes.

No Gerenciador de Soluções, clique com o botão direito do mouse no Projeto e selecione Adicionar>Novo Arquivo... Selecione General>Empty Class, digite SourceListDelegate para o Nome e clique no botão Novo. Faça com que o SourceListDelegate.cs arquivo tenha a seguinte aparência:

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

Isso fornecerá o comportamento de nossa Lista de Fontes.

Finalmente, no Gerenciador de Soluções, clique com o botão direito do mouse no Projeto e selecione Adicionar>Novo Arquivo... Selecione General>Empty Class, digite SourceListView para o Nome e clique no botão Novo. Faça com que o SourceListView.cs arquivo tenha a seguinte aparência:

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

Isso cria uma subclasse personalizada e reutilizável de (SourceListView) que podemos usar para conduzir a Lista de NSOutlineView Fontes em qualquer aplicativo Xamarin.Mac que criamos.

Criando e mantendo listas de código-fonte no Xcode

Agora, vamos criar nossa Lista de Códigos-Fonte no Construtor de Interfaces. Clique duas vezes no Main.storyboard arquivo para abri-lo para edição no Construtor de Interfaces e arraste um Modo de Exibição Dividido do Inspetor de Biblioteca, adicione-o ao Controlador de Exibição e defina-o para redimensioná-lo com o Modo de Exibição no Editor de Restrições:

Restrições de edição no Construtor de Interfaces.

Em seguida, arraste uma Lista de Fontes do Inspetor de Biblioteca, adicione-a ao lado esquerdo do Modo de Exibição Dividido e defina-a para redimensionar com a Exibição no Editor de Restrições:

Restrições de edição arrastando uma Lista de Fontes para o Modo de Exibição Dividido.

Em seguida, alterne para o Modo de Exibição de Identidade, selecione a Lista de Origem e altere sua Classe para SourceListView:

Definindo o nome da classe

Finalmente, crie uma saída para nossa lista de fontes chamada SourceList no ViewController.h arquivo:

Configurando uma tomada

Salve suas alterações e retorne ao Visual Studio para Mac para sincronizar com o Xcode.

Preenchendo a lista de fontes

Vamos editar o RotationWindow.cs arquivo no Visual Studio para Mac e fazer com que seu AwakeFromNib método tenha a seguinte aparência:

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

}

O Initialize () método precisa ser chamado contra o Outlet da nossa Lista de Fontesantes que qualquer item seja adicionado a ele. Para cada grupo de itens, criamos um item pai e, em seguida, adicionamos os subitens a esse item de grupo. Cada grupo é então adicionado à coleção SourceList.AddItem (...)da Lista de Códigos-Fonte. As duas últimas linhas carregam os dados para a Lista de Fontes e expandem todos os grupos:

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

Finalmente, edite o AppDelegate.cs arquivo e faça com que o DidFinishLaunching método tenha a seguinte aparência:

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

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

Se executarmos nosso aplicativo, o seguinte será exibido:

Uma execução de aplicativo de exemplo

Resumo

Este artigo deu uma olhada detalhada no trabalho com listas de código-fonte em um aplicativo Xamarin.Mac. Vimos como criar e manter Listas de Códigos-Fonte no Construtor de Interfaces do Xcode e como trabalhar com Listas de Códigos-Fonte em código C#.