Visualizações de coleção no Xamarin.Mac

Este artigo descreve como trabalhar com modos de exibição de coleção em um aplicativo Xamarin.Mac. Ele abrange a criação e manutenção de exibições de coleção no Xcode e no Interface Builder e o trabalho com elas programaticamente.

Ao trabalhar com C# e .NET em um aplicativo Xamarin.Mac, o desenvolvedor tem acesso aos mesmos controles AppKit Collection View que um desenvolvedor trabalhando e Objective-CXcode faz. Como o Xamarin.Mac se integra diretamente ao Xcode, o desenvolvedor usa o Construtor de Interfaces do Xcode para criar e manter Visualizações de Coleção.

A NSCollectionView exibe uma grade de subexibições organizadas usando um NSCollectionViewLayoutarquivo . Cada subexibição na grade é representada por um NSCollectionViewItem que gerencia o carregamento do conteúdo do modo de exibição de um .xib arquivo.

Uma execução de aplicativo de exemplo

Este artigo aborda os conceitos básicos de como trabalhar com Modos de Exibição de Coleção em um aplicativo Xamarin.Mac. É altamente recomendável que você trabalhe primeiro no artigo Hello, Mac, especificamente nas seções Introdução ao Xcode e ao Construtor de Interface e Outlets and Actions, pois ele aborda os principais conceitos e técnicas usados ao longo deste 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.

Sobre os modos de exibição de coleção

O objetivo principal de um Modo de Exibição de Coleção (NSCollectionView) é organizar visualmente um grupo de objetos de forma organizada usando um Layout de Modo de Exibição de Coleção (NSCollectionViewLayout), com cada objeto individual (NSCollectionViewItem) obtendo seu próprio Modo de Exibição na coleção maior. As Exibições de Coleta funcionam por meio de técnicas de Vinculação de Dados e Codificação de Chave-Valor e, como tal, você deve ler a documentação de Vinculação de Dados e Codificação de Chave-Valor antes de continuar com este artigo.

O Modo de Exibição de Coleção não tem um Item de Exibição de Coleção interno padrão (como um Modo de Exibição de Estrutura de Tópicos ou de Tabela), portanto, o desenvolvedor é responsável por projetar e implementar um Modo de Exibição de Protótipo usando outros controles do AppKit, como Campos de Imagem, Campos de Texto, Rótulos, etc. Essa Exibição de Protótipo será usada para exibir e trabalhar com cada item que está sendo gerenciado pela Exibição de Coleção e é armazenada em um .xib arquivo.

Como o desenvolvedor é responsável pela aparência de um Item de Exibição de Coleção, o Modo de Exibição de Coleção não tem suporte interno para realçar um item selecionado na grade. A implementação desse recurso será abordada neste artigo.

Definindo o modelo de dados

Antes de vincular dados a uma exibição de coleta no Construtor de Interfaces, uma classe compatível com KVC (Key-Value Coding - Codificação de Chave-Valor)/KVO (Key-Value Observando) deve ser definida no aplicativo Xamarin.Mac para atuar como o Modelo de Dados para a associação. O Modelo de Dados fornece todos os dados que serão exibidos na coleção e recebe quaisquer modificações nos dados que o usuário faz na interface do usuário durante a execução do aplicativo.

Veja o exemplo de um aplicativo que gerencia um grupo de funcionários, a seguinte classe pode ser usada para definir o Modelo de Dados:

using System;
using Foundation;
using AppKit;

namespace MacDatabinding
{
    [Register("PersonModel")]
    public class PersonModel : NSObject
    {
        #region Private Variables
        private string _name = "";
        private string _occupation = "";
        private bool _isManager = false;
        private NSMutableArray _people = new NSMutableArray();
        #endregion

        #region Computed Properties
        [Export("Name")]
        public string Name {
            get { return _name; }
            set {
                WillChangeValue ("Name");
                _name = value;
                DidChangeValue ("Name");
            }
        }

        [Export("Occupation")]
        public string Occupation {
            get { return _occupation; }
            set {
                WillChangeValue ("Occupation");
                _occupation = value;
                DidChangeValue ("Occupation");
            }
        }

        [Export("isManager")]
        public bool isManager {
            get { return _isManager; }
            set {
                WillChangeValue ("isManager");
                WillChangeValue ("Icon");
                _isManager = value;
                DidChangeValue ("isManager");
                DidChangeValue ("Icon");
            }
        }

        [Export("isEmployee")]
        public bool isEmployee {
            get { return (NumberOfEmployees == 0); }
        }

        [Export("Icon")]
        public NSImage Icon
        {
            get
            {
                if (isManager)
                {
                    return NSImage.ImageNamed("IconGroup");
                }
                else
                {
                    return NSImage.ImageNamed("IconUser");
                }
            }
        }

        [Export("personModelArray")]
        public NSArray People {
            get { return _people; }
        }

        [Export("NumberOfEmployees")]
        public nint NumberOfEmployees {
            get { return (nint)_people.Count; }
        }
        #endregion

        #region Constructors
        public PersonModel ()
        {
        }

        public PersonModel (string name, string occupation)
        {
            // Initialize
            this.Name = name;
            this.Occupation = occupation;
        }

        public PersonModel (string name, string occupation, bool manager)
        {
            // Initialize
            this.Name = name;
            this.Occupation = occupation;
            this.isManager = manager;
        }
        #endregion

        #region Array Controller Methods
        [Export("addObject:")]
        public void AddPerson(PersonModel person) {
            WillChangeValue ("personModelArray");
            isManager = true;
            _people.Add (person);
            DidChangeValue ("personModelArray");
        }

        [Export("insertObject:inPersonModelArrayAtIndex:")]
        public void InsertPerson(PersonModel person, nint index) {
            WillChangeValue ("personModelArray");
            _people.Insert (person, index);
            DidChangeValue ("personModelArray");
        }

        [Export("removeObjectFromPersonModelArrayAtIndex:")]
        public void RemovePerson(nint index) {
            WillChangeValue ("personModelArray");
            _people.RemoveObject (index);
            DidChangeValue ("personModelArray");
        }

        [Export("setPersonModelArray:")]
        public void SetPeople(NSMutableArray array) {
            WillChangeValue ("personModelArray");
            _people = array;
            DidChangeValue ("personModelArray");
        }
        #endregion
    }
}

O PersonModel Modelo de Dados será usado durante o restante deste artigo.

Trabalhando com um modo de exibição de coleção

A vinculação de dados com um Modo de Exibição de Coleção é muito semelhante à vinculação com um Modo de Exibição de Tabela, pois NSCollectionViewDataSource é usada para fornecer dados para a coleção. Como o modo de exibição de coleção não tem um formato de exibição predefinido, é necessário mais trabalho para fornecer feedback de interação do usuário e acompanhar a seleção do usuário.

Criando o protótipo de célula

Como o Modo de Exibição de Coleção não inclui um protótipo de célula padrão, o desenvolvedor precisará adicionar um ou mais .xib arquivos ao aplicativo Xamarin.Mac para definir o layout e o conteúdo das células individuais.

Faça o seguinte:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nome do projeto e selecione Adicionar>Novo Arquivo...

  2. Selecione Mac>View Controller, dê um nome a ele (como EmployeeItem neste exemplo) e clique no botão Novo para criar:

    Adicionando um novo controlador de exibição

    Isso adicionará um EmployeeItem.csarquivo e EmployeeItemController.xibEmployeeItemController.cs e à solução do projeto.

  3. Clique duas vezes no EmployeeItemController.xib arquivo para abri-lo para edição no Construtor de Interfaces do Xcode.

  4. Adicione um NSBoxe NSImageView dois NSLabel controles ao modo de exibição e esquema-os da seguinte maneira:

    Projetando o layout do protótipo de célula

  5. Abra o Editor Assistente e crie uma Tomada para o NSBox para que ele possa ser usado para indicar o estado de seleção de uma célula:

    Expondo o NSBox em um Outlet

  6. Retorne ao Editor Padrão e selecione a Exibição de Imagem.

  7. No Inspetor de Vinculação, selecione Vincular ao>proprietário do arquivo e insira um caminho de chave de modelo de:self.Person.Icon

    Vinculando o ícone

  8. Selecione o primeiro Rótulo e, no Inspetor de Vinculação, selecione Vincular ao>Proprietário do Arquivo e insira um Caminho de Chave de Modelo de:self.Person.Name

    Vinculando o nome

  9. Selecione o segundo Rótulo e, no Inspetor de Vinculação, selecione Vincular ao>Proprietário do Arquivo e insira um Caminho de Chave de Modelo de:self.Person.Occupation

    Vinculação da ocupação

  10. Salve as alterações no .xib arquivo e retorne ao Visual Studio para sincronizar as alterações.

Edite o EmployeeItemController.cs arquivo e faça com que ele tenha a seguinte aparência:

using System;
using System.Collections.Generic;
using System.Linq;
using Foundation;
using AppKit;

namespace MacCollectionNew
{
    /// <summary>
    /// The Employee item controller handles the display of the individual items that will
    /// be displayed in the collection view as defined in the associated .XIB file.
    /// </summary>
    public partial class EmployeeItemController : NSCollectionViewItem
    {
        #region Private Variables
        /// <summary>
        /// The person that will be displayed.
        /// </summary>
        private PersonModel _person;
        #endregion

        #region Computed Properties
        // strongly typed view accessor
        public new EmployeeItem View
        {
            get
            {
                return (EmployeeItem)base.View;
            }
        }

        /// <summary>
        /// Gets or sets the person.
        /// </summary>
        /// <value>The person that this item belongs to.</value>
        [Export("Person")]
        public PersonModel Person
        {
            get { return _person; }
            set
            {
                WillChangeValue("Person");
                _person = value;
                DidChangeValue("Person");
            }
        }

        /// <summary>
        /// Gets or sets the color of the background for the item.
        /// </summary>
        /// <value>The color of the background.</value>
        public NSColor BackgroundColor {
            get { return Background.FillColor; }
            set { Background.FillColor = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="T:MacCollectionNew.EmployeeItemController"/> is selected.
        /// </summary>
        /// <value><c>true</c> if selected; otherwise, <c>false</c>.</value>
        /// <remarks>This also changes the background color based on the selected state
        /// of the item.</remarks>
        public override bool Selected
        {
            get
            {
                return base.Selected;
            }
            set
            {
                base.Selected = value;

                // Set background color based on the selection state
                if (value) {
                    BackgroundColor = NSColor.DarkGray;
                } else {
                    BackgroundColor = NSColor.LightGray;
                }
            }
        }
        #endregion

        #region Constructors
        // Called when created from unmanaged code
        public EmployeeItemController(IntPtr handle) : base(handle)
        {
            Initialize();
        }

        // Called when created directly from a XIB file
        [Export("initWithCoder:")]
        public EmployeeItemController(NSCoder coder) : base(coder)
        {
            Initialize();
        }

        // Call to load from the XIB/NIB file
        public EmployeeItemController() : base("EmployeeItem", NSBundle.MainBundle)
        {
            Initialize();
        }

        // Added to support loading from XIB/NIB
        public EmployeeItemController(string nibName, NSBundle nibBundle) : base(nibName, nibBundle) {

            Initialize();
        }

        // Shared initialization code
        void Initialize()
        {
        }
        #endregion
    }
}

Examinando esse código em detalhes, a classe herda de NSCollectionViewItem para que possa atuar como um protótipo para uma célula de exibição de coleção. A Person propriedade expõe a classe que foi usada para vincular dados ao Modo de Exibição de Imagem e Rótulos no Xcode. Esta é uma instância do PersonModel criado acima.

A BackgroundColor propriedade é um atalho para os NSBox controles FillColor que serão usados para mostrar o status de seleção de uma célula. Substituindo a SelectedNSCollectionViewItempropriedade do , o código a seguir define ou limpa esse estado de seleção:

public override bool Selected
{
    get
    {
        return base.Selected;
    }
    set
    {
        base.Selected = value;

        // Set background color based on the selection state
        if (value) {
            BackgroundColor = NSColor.DarkGray;
        } else {
            BackgroundColor = NSColor.LightGray;
        }
    }
}

Criando a fonte de dados do Modo de Exibição de Coleção

Uma Fonte de Dados do Modo de Exibição de Coleção (NSCollectionViewDataSource) fornece todos os dados para um Modo de Exibição de Coleção e cria e preenche uma Célula de Exibição de Coleção (usando o .xib protótipo) conforme necessário para cada item da coleção.

Adicione uma nova classe ao projeto, chame-o CollectionViewDataSource e faça-o parecer com o seguinte:

using System;
using System.Collections.Generic;
using AppKit;
using Foundation;

namespace MacCollectionNew
{
    /// <summary>
    /// Collection view data source provides the data for the collection view.
    /// </summary>
    public class CollectionViewDataSource : NSCollectionViewDataSource
    {
        #region Computed Properties
        /// <summary>
        /// Gets or sets the parent collection view.
        /// </summary>
        /// <value>The parent collection view.</value>
        public NSCollectionView ParentCollectionView { get; set; }

        /// <summary>
        /// Gets or sets the data that will be displayed in the collection.
        /// </summary>
        /// <value>A collection of PersonModel objects.</value>
        public List<PersonModel> Data { get; set; } = new List<PersonModel>();
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="T:MacCollectionNew.CollectionViewDataSource"/> class.
        /// </summary>
        /// <param name="parent">The parent collection that this datasource will provide data for.</param>
        public CollectionViewDataSource(NSCollectionView parent)
        {
            // Initialize
            ParentCollectionView = parent;

            // Attach to collection view
            parent.DataSource = this;

        }
        #endregion

        #region Override Methods
        /// <summary>
        /// Gets the number of sections.
        /// </summary>
        /// <returns>The number of sections.</returns>
        /// <param name="collectionView">The parent Collection view.</param>
        public override nint GetNumberOfSections(NSCollectionView collectionView)
        {
            // There is only one section in this view
            return 1;
        }

        /// <summary>
        /// Gets the number of items in the given section.
        /// </summary>
        /// <returns>The number of items.</returns>
        /// <param name="collectionView">The parent Collection view.</param>
        /// <param name="section">The Section number to count items for.</param>
        public override nint GetNumberofItems(NSCollectionView collectionView, nint section)
        {
            // Return the number of items
            return Data.Count;
        }

        /// <summary>
        /// Gets the item for the give section and item index.
        /// </summary>
        /// <returns>The item.</returns>
        /// <param name="collectionView">The parent Collection view.</param>
        /// <param name="indexPath">Index path specifying the section and index.</param>
        public override NSCollectionViewItem GetItem(NSCollectionView collectionView, NSIndexPath indexPath)
        {
            var item = collectionView.MakeItem("EmployeeCell", indexPath) as EmployeeItemController;
            item.Person = Data[(int)indexPath.Item];

            return item;
        }
        #endregion
    }
}

Examinando esse código em detalhes, a classe herda de NSCollectionViewDataSource e expõe uma Lista de instâncias por meio de PersonModel sua Data propriedade.

Como essa coleção tem apenas uma seção, o código substitui o GetNumberOfSections método e sempre retorna 1. Além disso, o GetNumberofItems método é substituído em que retorna o número de itens na Data lista de propriedades.

O GetItem método é chamado sempre que uma nova célula é necessária e se parece com o seguinte:

public override NSCollectionViewItem GetItem(NSCollectionView collectionView, NSIndexPath indexPath)
{
    var item = collectionView.MakeItem("EmployeeCell", indexPath) as EmployeeItemController;
    item.Person = Data[(int)indexPath.Item];

    return item;
}

O MakeItem método do Modo de Exibição de Coleção é chamado para criar ou retornar uma instância reutilizável do EmployeeItemController e sua Person propriedade é definida como item sendo exibido na célula solicitada.

O EmployeeItemController deve ser registrado com o controlador de exibição de coleção de antemão usando o seguinte código:

EmployeeCollection.RegisterClassForItem(typeof(EmployeeItemController), "EmployeeCell");

O Identificador (EmployeeCell) usado na MakeItem chamada deve corresponder ao nome do Controlador de Exibição que foi registrado com o Modo de Exibição de Coleção. Esta etapa será abordada em detalhes a seguir.

Manipulação da seleção de item

Para lidar com a seleção e desseleção de itens na coleção, será necessário um NSCollectionViewDelegate . Como este exemplo usará o tipo de layout interno NSCollectionViewFlowLayout , uma NSCollectionViewDelegateFlowLayout versão específica desse delegado será necessária.

Adicione uma nova classe ao projeto, chame-a CollectionViewDelegate e faça-a parecer com a seguinte:

using System;
using Foundation;
using AppKit;

namespace MacCollectionNew
{
    /// <summary>
    /// Collection view delegate handles user interaction with the elements of the
    /// collection view for the Flow-Based layout type.
    /// </summary>
    public class CollectionViewDelegate : NSCollectionViewDelegateFlowLayout
    {
        #region Computed Properties
        /// <summary>
        /// Gets or sets the parent view controller.
        /// </summary>
        /// <value>The parent view controller.</value>
        public ViewController ParentViewController { get; set; }
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="T:MacCollectionNew.CollectionViewDelegate"/> class.
        /// </summary>
        /// <param name="parentViewController">Parent view controller.</param>
        public CollectionViewDelegate(ViewController parentViewController)
        {
            // Initialize
            ParentViewController = parentViewController;
        }
        #endregion

        #region Override Methods
        /// <summary>
        /// Handles one or more items being selected.
        /// </summary>
        /// <param name="collectionView">The parent Collection view.</param>
        /// <param name="indexPaths">The Index paths of the items being selected.</param>
        public override void ItemsSelected(NSCollectionView collectionView, NSSet indexPaths)
        {
            // Dereference path
            var paths = indexPaths.ToArray<NSIndexPath>();
            var index = (int)paths[0].Item;

            // Save the selected item
            ParentViewController.PersonSelected = ParentViewController.Datasource.Data[index];

        }

        /// <summary>
        /// Handles one or more items being deselected.
        /// </summary>
        /// <param name="collectionView">The parent Collection view.</param>
        /// <param name="indexPaths">The Index paths of the items being deselected.</param>
        public override void ItemsDeselected(NSCollectionView collectionView, NSSet indexPaths)
        {
            // Dereference path
            var paths = indexPaths.ToArray<NSIndexPath>();
            var index = paths[0].Item;

            // Clear selection
            ParentViewController.PersonSelected = null;
        }
        #endregion
    }
}

Os ItemsSelected métodos e ItemsDeselected são substituídos e usados para definir ou limpar a PersonSelected propriedade do View Controller que está manipulando o Modo de Exibição de Coleção quando o usuário seleciona ou desmarca um item. Isso será mostrado em detalhes a seguir.

Criando o modo de exibição de coleção no Construtor de Interfaces

Com todas as peças de suporte necessárias, o storyboard principal pode ser editado e uma Visualização de coleção adicionada a ele.

Faça o seguinte:

  1. Clique duas vezes no Main.Storyboard arquivo no Gerenciador de Soluções para abri-lo para edição no Construtor de Interfaces do Xcode.

  2. Arraste um Modo de Exibição de Coleção para o Modo de Exibição Principal e redimensione-o para preencher o Modo de Exibição:

    Adicionando um Modo de Exibição de Coleção ao layout

  3. Com o Modo de Exibição de Coleção selecionado, use o Editor de Restrições para fixá-lo no Modo de Exibição quando ele for redimensionado:

    A captura de tela mostra Adicionar novas restrições.

  4. Certifique-se de que a Vista de Coleção está selecionada na Superfície de Design (e não na Vista de Deslocamento Limitado ou na Vista de Clipe que a contém), mude para o Editor Assistente e crie uma Saída para a vista de coleção:

    A captura de tela mostra o Editor Assistente onde você pode criar uma Saída.

  5. Salve as alterações e retorne ao Visual Studio para sincronizar.

Juntando tudo

Todas as peças de suporte agora foram colocadas no lugar com uma classe para atuar como o modelo de dados (PersonModel), a NSCollectionViewDataSource foi adicionada para fornecer dados, a NSCollectionViewDelegateFlowLayout foi criada para lidar com a seleção de itens e a NSCollectionView foi adicionada ao Storyboard Principal e exposta como um Outlet (EmployeeCollection).

A etapa final é editar o View Controller que contém o Modo de Exibição de Coleção e reunir todas as peças para preencher a coleção e manipular a seleção de itens.

Edite o ViewController.cs arquivo e faça com que ele tenha a seguinte aparência:

using System;
using AppKit;
using Foundation;
using CoreGraphics;

namespace MacCollectionNew
{
    /// <summary>
    /// The View controller controls the main view that houses the Collection View.
    /// </summary>
    public partial class ViewController : NSViewController
    {
        #region Private Variables
        private PersonModel _personSelected;
        private bool shouldEdit = true;
        #endregion

        #region Computed Properties
        /// <summary>
        /// Gets or sets the datasource that provides the data to display in the
        /// Collection View.
        /// </summary>
        /// <value>The datasource.</value>
        public CollectionViewDataSource Datasource { get; set; }

        /// <summary>
        /// Gets or sets the person currently selected in the collection view.
        /// </summary>
        /// <value>The person selected or <c>null</c> if no person is selected.</value>
        [Export("PersonSelected")]
        public PersonModel PersonSelected
        {
            get { return _personSelected; }
            set
            {
                WillChangeValue("PersonSelected");
                _personSelected = value;
                DidChangeValue("PersonSelected");
                RaiseSelectionChanged();
            }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="T:MacCollectionNew.ViewController"/> class.
        /// </summary>
        /// <param name="handle">Handle.</param>
        public ViewController(IntPtr handle) : base(handle)
        {
        }
        #endregion

        #region Override Methods
        /// <summary>
        /// Called after the view has finished loading from the Storyboard to allow it to
        /// be configured before displaying to the user.
        /// </summary>
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Initialize Collection View
            ConfigureCollectionView();
            PopulateWithData();
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Configures the collection view.
        /// </summary>
        private void ConfigureCollectionView()
        {
            EmployeeCollection.RegisterClassForItem(typeof(EmployeeItemController), "EmployeeCell");

            // Create a flow layout
            var flowLayout = new NSCollectionViewFlowLayout()
            {
                ItemSize = new CGSize(150, 150),
                SectionInset = new NSEdgeInsets(10, 10, 10, 20),
                MinimumInteritemSpacing = 10,
                MinimumLineSpacing = 10
            };
            EmployeeCollection.WantsLayer = true;

            // Setup collection view
            EmployeeCollection.CollectionViewLayout = flowLayout;
            EmployeeCollection.Delegate = new CollectionViewDelegate(this);

        }

        /// <summary>
        /// Populates the Datasource with data and attaches it to the collection view.
        /// </summary>
        private void PopulateWithData()
        {
            // Make datasource
            Datasource = new CollectionViewDataSource(EmployeeCollection);

            // Build list of employees
            Datasource.Data.Add(new PersonModel("Craig Dunn", "Documentation Manager", true));
            Datasource.Data.Add(new PersonModel("Amy Burns", "Technical Writer"));
            Datasource.Data.Add(new PersonModel("Joel Martinez", "Web & Infrastructure"));
            Datasource.Data.Add(new PersonModel("Kevin Mullins", "Technical Writer"));
            Datasource.Data.Add(new PersonModel("Mark McLemore", "Technical Writer"));
            Datasource.Data.Add(new PersonModel("Tom Opgenorth", "Technical Writer"));
            Datasource.Data.Add(new PersonModel("Larry O'Brien", "API Docs Manager", true));
            Datasource.Data.Add(new PersonModel("Mike Norman", "API Documentor"));

            // Populate collection view
            EmployeeCollection.ReloadData();
        }
        #endregion

        #region Events
        /// <summary>
        /// Selection changed delegate.
        /// </summary>
        public delegate void SelectionChangedDelegate();

        /// <summary>
        /// Occurs when selection changed.
        /// </summary>
        public event SelectionChangedDelegate SelectionChanged;

        /// <summary>
        /// Raises the selection changed event.
        /// </summary>
        internal void RaiseSelectionChanged() {
            // Inform caller
            if (this.SelectionChanged != null) SelectionChanged();
        }
        #endregion
    }
}

Examinando esse código em detalhes, uma Datasource propriedade é definida para manter uma instância do CollectionViewDataSource que fornecerá os dados para o Modo de Exibição de Coleção. Uma PersonSelected propriedade é definida para manter a PersonModel representação do item selecionado no momento no Modo de Exibição de Coleção. Essa propriedade também gera o SelectionChanged evento quando a seleção é alterada.

A ConfigureCollectionView classe é usada para registrar o controlador de exibição que atua como o protótipo de célula com o modo de exibição de coleção usando a seguinte linha:

EmployeeCollection.RegisterClassForItem(typeof(EmployeeItemController), "EmployeeCell");

Observe que o Identificador (EmployeeCell) usado para registrar o protótipo corresponde ao chamado no GetItem método do CollectionViewDataSource definido acima:

var item = collectionView.MakeItem("EmployeeCell", indexPath) as EmployeeItemController;
...

Além disso, o tipo do controlador de exibição deve corresponder ao nome do .xib arquivo que define o protótipo exatamente. No caso deste exemplo, EmployeeItemController e EmployeeItemController.xib.

O layout real dos itens no Modo de Exibição de Coleção é controlado por uma classe de Layout do Modo de Exibição de Coleção e pode ser alterado dinamicamente em tempo de execução atribuindo uma nova instância à CollectionViewLayout propriedade. A alteração dessa propriedade atualiza a aparência do Modo de Exibição de Coleção sem animar a alteração.

A Apple envia dois tipos de layout integrados com o Modo de Exibição de Coleção que lidará com os usos mais típicos: NSCollectionViewFlowLayout e NSCollectionViewGridLayout. Se o desenvolvedor exigiu um formato personalizado, como o layout dos itens em um círculo, ele poderá criar uma instância personalizada e NSCollectionViewLayout substituir os métodos necessários para obter o efeito desejado.

Este exemplo usa o layout de fluxo padrão para criar uma instância da NSCollectionViewFlowLayout classe e configurá-la da seguinte maneira:

var flowLayout = new NSCollectionViewFlowLayout()
{
    ItemSize = new CGSize(150, 150),
    SectionInset = new NSEdgeInsets(10, 10, 10, 20),
    MinimumInteritemSpacing = 10,
    MinimumLineSpacing = 10
};

A ItemSize propriedade define o tamanho de cada célula individual na coleção. A SectionInset propriedade define as inserções da borda da coleção em que as células serão dispostas. MinimumInteritemSpacing Define o espaçamento mínimo entre itens e MinimumLineSpacing define o espaçamento mínimo entre linhas na coleção.

O layout é atribuído ao Modo de Exibição de Coleção e uma instância do é anexada para manipular a CollectionViewDelegate seleção de itens:

// Setup collection view
EmployeeCollection.CollectionViewLayout = flowLayout;
EmployeeCollection.Delegate = new CollectionViewDelegate(this);

O PopulateWithData método cria uma nova instância do , preenche-o CollectionViewDataSourcecom dados, anexa-o ao Modo de Exibição de Coleção e chama o ReloadData método para exibir os itens:

private void PopulateWithData()
{
    // Make datasource
    Datasource = new CollectionViewDataSource(EmployeeCollection);

    // Build list of employees
    Datasource.Data.Add(new PersonModel("Craig Dunn", "Documentation Manager", true));
    ...

    // Populate collection view
    EmployeeCollection.ReloadData();
}

O ViewDidLoad método é substituído e chama os ConfigureCollectionView métodos e PopulateWithData para exibir o Modo de Exibição de Coleção final para o usuário:

public override void ViewDidLoad()
{
    base.ViewDidLoad();

    // Initialize Collection View
    ConfigureCollectionView();
    PopulateWithData();
}

Resumo

Este artigo deu uma olhada detalhada no trabalho com Exibições de coleção em um aplicativo Xamarin.Mac. Primeiro, ele analisou a exposição de uma classe C# usando Objective-C KVC (Key-Value Coding - Codificação de Valor Chave) e KVO (Key-Value Observing - Observação de Valor Chave). Em seguida, ele mostrou como usar uma classe compatível com KVO e vinculá-la a exibições de coleção no Construtor de Interfaces do Xcode. Finalmente, ele mostrou como interagir com Exibições de Coleção no código C#.