Exibições de tabela no Xamarin.Mac

Este artigo aborda o trabalho com exibições de tabela em um aplicativo Xamarin.Mac. Ele descreve a criação de exibições de tabela no Xcode e no Construtor de Interfaces e a interação com eles no código.

Ao trabalhar com C# e .NET em um aplicativo Xamarin.Mac, você tem acesso às mesmas Exibições de Tabela em Objective-C que um desenvolvedor está trabalhando e o Xcode . Como o Xamarin.Mac se integra diretamente ao Xcode, você pode usar o Construtor de Interfaces do Xcode para criar e manter seus Modos de Exibição de Tabela (ou, opcionalmente, criá-los diretamente no código C#).

Um Modo de Exibição de Tabela exibe dados em um formato tabular que contém uma ou mais colunas de informações em várias linhas. Com base no tipo de Exibição de Tabela que está sendo criado, o usuário pode classificar por coluna, reorganizar colunas, adicionar colunas, remover colunas ou editar os dados contidos na tabela.

Uma tabela de exemplo

Neste artigo, abordaremos as noções básicas de como trabalhar com exibições de tabela em um aplicativo Xamarin.Mac. É altamente sugerido que você trabalhe primeiro no artigo Olá, Mac , especificamente nas seções Introdução ao Xcode e Construtor de Interfaces e Saídas e Ações , pois aborda os principais conceitos e técnicas que usaremos neste artigo.

Talvez você queira dar uma olhada na seção Expondo classes/métodos C# do Objective-C documento Interno do Xamarin.Mac , ele explica os Register comandos e Export usados para conectar suas classes C# a Objective-C objetos e elementos de interface do usuário.

Introdução aos modos de exibição de tabela

Um Modo de Exibição de Tabela exibe dados em um formato tabular que contém uma ou mais colunas de informações em várias linhas. Os Modos de Exibição de Tabela são exibidos dentro dos Modos de Exibição de Rolagem (NSScrollView) e, começando pelo macOS 10.7, você pode usar qualquer NSView um em vez de Células (NSCell) para exibir linhas e colunas. Dito isso, você ainda pode usar NSCell , no entanto, normalmente será subclasse NSTableCellView e criará suas linhas e colunas personalizadas.

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

O comportamento de um Modo de Exibição de Tabela pode ser personalizado fornecendo uma subclasse do Delegado de Exibição de Tabela (NSTableViewDelegate) para dar suporte ao gerenciamento de colunas de tabela, digite para selecionar funcionalidade, seleção e edição de linhas, acompanhamento personalizado e exibições personalizadas para colunas e linhas individuais.

Ao criar exibições de tabela, a Apple sugere o seguinte:

  • Permitir que o usuário classifique a tabela clicando em um Cabeçalhos de Coluna.
  • Crie cabeçalhos de coluna que são substantivos ou frases substantivas curtas que descrevem os dados que estão sendo mostrados nessa coluna.

Para obter mais informações, consulte a seção Exibições de conteúdo das Diretrizes de Interface Humana do OS X da Apple.

Criando e mantendo exibições de tabela no Xcode

Ao criar um novo aplicativo Xamarin.Mac Cocoa, você obtém uma janela padrão em branco, por padrão. Essas janelas são definidas em um .storyboard arquivo incluído automaticamente no projeto. Para editar o design do windows, no Gerenciador de Soluções, clique duas vezes no Main.storyboard arquivo:

Selecionando o storyboard main

Isso abrirá o design da janela no Construtor de Interfaces do Xcode:

Editando a interface do usuário no Xcode

Digite table na Caixa de Pesquisa do Inspetor de Biblioteca para facilitar a localização dos controles de Exibição de Tabela:

Selecionando um modo de exibição de tabela na biblioteca

Arraste um Modo de Exibição de Tabela para o Controlador de Exibição no Editor de Interface, faça com que ele preencha a área de conteúdo do Controlador de Exibição e defina-o como onde ele é reduzido e cresce com a janela no Editor de Restrições:

Editando restrições

Selecione o Modo de Exibição de Tabela na Hierarquia de Interface e as seguintes propriedades estão disponíveis no Inspetor de Atributos:

Captura de tela que mostra as propriedades disponíveis no Inspetor de Atributos.

  • Modo de Conteúdo – permite que você use Exibições (NSView) ou Células (NSCell) para exibir os dados nas linhas e colunas. A partir do macOS 10.7, você deve usar Exibições.
  • Floats Group Rows – Se true, o Modo de Exibição de Tabela desenhará células agrupadas como se estivessem flutuando.
  • Colunas – define o número de colunas exibidas.
  • Cabeçalhos – se true, as colunas terão Cabeçalhos.
  • Reordenação – se true, o usuário poderá arrastar novamente as colunas na tabela.
  • Redimensionamento – se true, o usuário poderá arrastar cabeçalhos de coluna para redimensionar colunas.
  • Dimensionamento de coluna – controla como a tabela dimensionará automaticamente as colunas.
  • Realce – controla o tipo de realce que a tabela usa quando uma célula é selecionada.
  • Linhas alternativas – se true, alguma outra linha terá uma cor de plano de fundo diferente.
  • Grade Horizontal – seleciona o tipo de borda desenhada entre as células horizontalmente.
  • Grade Vertical – seleciona o tipo de borda desenhada entre as células verticalmente.
  • Cor da Grade – define a cor da borda da célula.
  • Plano de fundo – define a cor da tela de fundo da célula.
  • Seleção – Permitir que você controle como o usuário pode selecionar células na tabela como:
    • Múltiplo – Se true, o usuário pode selecionar várias linhas e colunas.
    • Coluna – Se true, o usuário poderá selecionar colunas.
    • Digite Select – Se true, o usuário poderá digitar um caractere para selecionar uma linha.
    • Vazio – se true, o usuário não é obrigado a selecionar uma linha ou coluna, a tabela não permite nenhuma seleção.
  • Salvamento automático – o nome no qual o formato de tabelas é salvo automaticamente.
  • Informações da coluna – se true, a ordem e a largura das colunas serão salvas automaticamente.
  • Quebras de linha – selecione como a célula lida com quebras de linha.
  • Trunca última linha visível – se true, a célula será truncada nos dados não pode caber dentro dos limites.

Importante

A menos que você esteja mantendo um aplicativo Xamarin.Mac herdado, NSView as Exibições de Tabela baseadas devem ser usadas em modos NSCell de exibição de tabela baseados. NSCell é considerado herdado e pode não ter suporte daqui para frente.

Selecione uma coluna de tabela na Hierarquia de Interface e as seguintes propriedades estão disponíveis no Inspetor de Atributos:

A captura de tela mostra as propriedades disponíveis para uma Coluna de Tabela no Inspetor de Atributos.

  • Título – define o título da coluna.
  • Alinhamento – defina o alinhamento do texto dentro das células.
  • Fonte de Título – seleciona a fonte para o texto cabeçalho da célula.
  • Chave de Classificação – é a chave usada para classificar dados na coluna. Deixe em branco se o usuário não puder classificar esta coluna.
  • Seletor – é a Ação usada para executar a classificação. Deixe em branco se o usuário não puder classificar esta coluna.
  • Ordem – é a ordem de classificação para os dados das colunas.
  • Redimensionamento – seleciona o tipo de redimensionamento para a coluna.
  • Editável – se true, o usuário pode editar células em uma tabela baseada em célula.
  • Oculto – se true, a coluna fica oculta.

Você também pode redimensionar a coluna arrastando seu identificador (centralizado verticalmente no lado direito da coluna) à esquerda ou à direita.

Vamos selecionar cada Coluna em nosso Modo de Exibição de Tabela e dar à primeira coluna um Título de Product e o segundo Details.

Selecione um Modo de Exibição de Célula de Tabela (NSTableViewCell) na Hierarquia de Interface e as seguintes propriedades estão disponíveis no Inspetor de Atributos:

A captura de tela mostra as propriedades disponíveis para um Modo de Exibição de Célula de Tabela no Inspetor de Atributos.

Essas são todas as propriedades de uma Exibição padrão. Você também tem a opção de redimensionar as linhas desta coluna aqui.

Selecione uma célula de exibição de tabela (por padrão, esta é uma NSTextField) na Hierarquia de Interface e as seguintes propriedades estão disponíveis no Inspetor de Atributos:

A captura de tela mostra as propriedades disponíveis para uma célula de exibição de tabela no Inspetor de Atributos.

Você terá todas as propriedades de um campo de texto padrão para definir aqui. Por padrão, um campo de texto padrão é usado para exibir dados de uma célula em uma coluna.

Selecione um Modo de Exibição de Célula de Tabela (NSTableFieldCell) na Hierarquia de Interface e as seguintes propriedades estão disponíveis no Inspetor de Atributos:

A captura de tela mostra as propriedades disponíveis para uma célula de exibição de tabela diferente no Inspetor de Atributos.

As configurações mais importantes aqui são:

  • Layout – selecione como as células nesta coluna são dispostas.
  • Usa Modo de Linha Única – Se true, a célula é limitada a uma única linha.
  • Largura do Layout do Primeiro Runtime – se true, a célula preferirá o conjunto de largura para ela (manual ou automaticamente) quando for exibida na primeira vez em que o aplicativo for executado.
  • Ação – controla quando a Ação de Edição é enviada para a célula.
  • Comportamento – define se uma célula é selecionável ou editável.
  • Rich Text - Se true, a célula pode exibir texto formatado e estilizado.
  • Desfazer – se true, a célula assume a responsabilidade pelo comportamento de desfazer.

Selecione o Modo de Exibição de Célula de Tabela (NSTableFieldCell) na parte inferior de uma coluna de tabela na Hierarquia de Interface:

Selecionando o Modo de Exibição de Célula de Tabela

Isso permite editar o Modo de Exibição de Célula de Tabela usado como padrão base para todas as células criadas para a coluna fornecida.

Adicionando ações e saídas

Assim como qualquer outro controle de interface do usuário cocoa, precisamos expor nosso Modo de Exibição de Tabela e são colunas e células para o código C# usando Ações e Saídas (com base na funcionalidade necessária).

O processo é o mesmo para qualquer elemento table view que desejamos expor:

  1. Alterne para o Editor Assistente e verifique se o ViewController.h arquivo está selecionado:

    O Editor assistente

  2. Selecione o Modo de Exibição de Tabela na Hierarquia de Interface, clique com o controle e arraste para o ViewController.h arquivo.

  3. Crie uma saída para o Modo de Exibição de Tabela chamado ProductTable:

    A captura de tela mostra uma conexão de saída criada para o Modo de Exibição de Tabela chamado ProductTable.

  4. Crie saídas para as colunas de tabelas, bem como chamadas ProductColumn e DetailsColumn:

    A captura de tela mostra uma conexão de saída criada para outros modos de exibição de tabela.

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

Em seguida, gravaremos o código exibindo alguns dados para a tabela quando o aplicativo for executado.

Preenchendo o modo de exibição de tabela

Com nosso Modo de Exibição de Tabela projetado no Construtor de Interfaces e exposto por meio de uma Tomada, em seguida, precisamos criar o código C# para preenchê-lo.

Primeiro, vamos criar uma nova Product classe para manter as informações das linhas individuais. No Gerenciador de Soluções, clique com o botão direito do mouse no Projeto e selecione Adicionar>Novo Arquivo... SelecioneClasse VaziaGeral>, insira Product para o Nome e clique no botão Novo:

Criando uma classe vazia

Faça com que o Product.cs arquivo se pareça com o seguinte:

using System;

namespace MacTables
{
  public class Product
  {
    #region Computed Properties
    public string Title { get; set;} = "";
    public string Description { get; set;} = "";
    #endregion

    #region Constructors
    public Product ()
    {
    }

    public Product (string title, string description)
    {
      this.Title = title;
      this.Description = description;
    }
    #endregion
  }
}

Em seguida, precisamos criar uma subclasse de NSTableDataSource para fornecer os dados para nossa tabela conforme solicitado. No Gerenciador de Soluções, clique com o botão direito do mouse no Projeto e selecione Adicionar>Novo Arquivo... SelecioneClasse VaziaGeral>, insira ProductTableDataSource para o Nome e clique no botão Novo.

Edite o ProductTableDataSource.cs arquivo e faça com que ele se pareça com o seguinte:

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

namespace MacTables
{
  public class ProductTableDataSource : NSTableViewDataSource
  {
    #region Public Variables
    public List<Product> Products = new List<Product>();
    #endregion

    #region Constructors
    public ProductTableDataSource ()
    {
    }
    #endregion

    #region Override Methods
    public override nint GetRowCount (NSTableView tableView)
    {
      return Products.Count;
    }
    #endregion
  }
}

Essa classe tem armazenamento para os itens do Modo de Exibição de Tabela e substitui o GetRowCount para retornar o número de linhas na tabela.

Por fim, precisamos criar uma subclasse de NSTableDelegate para fornecer o comportamento para nossa tabela. No Gerenciador de Soluções, clique com o botão direito do mouse no Projeto e selecione Adicionar>Novo Arquivo... SelecioneClasse VaziaGeral>, insira ProductTableDelegate para o Nome e clique no botão Novo.

Edite o ProductTableDelegate.cs arquivo e faça com que ele se pareça com o seguinte:

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

namespace MacTables
{
  public class ProductTableDelegate: NSTableViewDelegate
  {
    #region Constants 
    private const string CellIdentifier = "ProdCell";
    #endregion

    #region Private Variables
    private ProductTableDataSource DataSource;
    #endregion

    #region Constructors
    public ProductTableDelegate (ProductTableDataSource datasource)
    {
      this.DataSource = datasource;
    }
    #endregion

    #region Override Methods
    public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
    {
      // This pattern allows you reuse existing views when they are no-longer in use.
      // If the returned view is null, you instance up a new view
      // If a non-null view is returned, you modify it enough to reflect the new data
      NSTextField view = (NSTextField)tableView.MakeView (CellIdentifier, this);
      if (view == null) {
        view = new NSTextField ();
        view.Identifier = CellIdentifier;
        view.BackgroundColor = NSColor.Clear;
        view.Bordered = false;
        view.Selectable = false;
        view.Editable = false;
      }

      // Setup view based on the column selected
      switch (tableColumn.Title) {
      case "Product":
        view.StringValue = DataSource.Products [(int)row].Title;
        break;
      case "Details":
        view.StringValue = DataSource.Products [(int)row].Description;
        break;
      }

      return view;
    }
    #endregion
  }
}

Quando criamos uma instância do ProductTableDelegate, também passamos uma instância do ProductTableDataSource que fornece os dados para a tabela. O GetViewForItem método é responsável por retornar uma exibição (dados) para exibir a célula de uma coluna e uma linha give. Se possível, um modo de exibição existente será reutilizado para exibir a célula, se não for necessário criar uma nova exibição.

Para preencher a tabela, vamos editar o ViewController.cs arquivo e fazer com que o AwakeFromNib método se pareça com o seguinte:

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

  // Create the Product Table Data Source and populate it
  var DataSource = new ProductTableDataSource ();
  DataSource.Products.Add (new Product ("Xamarin.iOS", "Allows you to develop native iOS Applications in C#"));
  DataSource.Products.Add (new Product ("Xamarin.Android", "Allows you to develop native Android Applications in C#"));
  DataSource.Products.Add (new Product ("Xamarin.Mac", "Allows you to develop Mac native Applications in C#"));

  // Populate the Product Table
  ProductTable.DataSource = DataSource;
  ProductTable.Delegate = new ProductTableDelegate (DataSource);
}

Se executarmos o aplicativo, o seguinte será exibido:

A captura de tela mostra uma janela chamada Tabela do Produto com três entradas.

Classificação por coluna

Vamos permitir que o usuário classifique os dados na tabela clicando em um Cabeçalho de Coluna. Primeiro, clique duas vezes no Main.storyboard arquivo para abri-lo para edição no Construtor de Interfaces. Selecione a Product coluna, insira Title para a Chave de Classificação, compare: para o Seletor e selecione Ascending para a Ordem:

A captura de tela mostra o Construtor de Interfaces em que você pode definir a chave de classificação para a coluna Produto.

Selecione a Details coluna, insira Description para a Chave de Classificação, compare: para o Seletor e selecione Ascending para a Ordem:

A captura de tela mostra o Construtor de Interfaces em que você pode definir a chave de classificação para a coluna Detalhes.

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

Agora, vamos editar o ProductTableDataSource.cs arquivo e adicionar os seguintes métodos:

public void Sort(string key, bool ascending) {

  // Take action based on key
  switch (key) {
  case "Title":
    if (ascending) {
      Products.Sort ((x, y) => x.Title.CompareTo (y.Title));
    } else {
      Products.Sort ((x, y) => -1 * x.Title.CompareTo (y.Title));
    }
    break;
  case "Description":
    if (ascending) {
      Products.Sort ((x, y) => x.Description.CompareTo (y.Description));
    } else {
      Products.Sort ((x, y) => -1 * x.Description.CompareTo (y.Description));
    }
    break;
  }

}

public override void SortDescriptorsChanged (NSTableView tableView, NSSortDescriptor[] oldDescriptors)
{
  // Sort the data
  if (oldDescriptors.Length > 0) {
    // Update sort
    Sort (oldDescriptors [0].Key, oldDescriptors [0].Ascending);
  } else {
    // Grab current descriptors and update sort
    NSSortDescriptor[] tbSort = tableView.SortDescriptors; 
    Sort (tbSort[0].Key, tbSort[0].Ascending); 
  }
      
  // Refresh table
  tableView.ReloadData ();
}

O Sort método nos permite classificar os dados na Fonte de Dados com base em um determinado Product campo de classe em ordem crescente ou decrescente. O método substituído SortDescriptorsChanged será chamado sempre que o uso clicar em um Título de Coluna. Ele será passado o valor chave que definimos no Construtor de Interfaces e a ordem de classificação para essa coluna.

Se executarmos o aplicativo e clicarmos nos Cabeçalhos de Coluna, as linhas serão classificadas por essa coluna:

Uma execução de aplicativo de exemplo

Seleção de Linha

Se você quiser permitir que o usuário selecione uma única linha, clique duas vezes no Main.storyboard arquivo para abri-lo para edição no Construtor de Interfaces. Selecione o Modo de Exibição de Tabela na Hierarquia de Interface e desmarque a caixa de seleção Múltiplos no Inspetor de Atributos:

Captura de tela que mostra o Construtor de Interfaces em que você pode selecionar Múltiplo no Inspetor de Atributos.

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

Em seguida, edite o ProductTableDelegate.cs arquivo e adicione o seguinte método:

public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
  return true;
}

Isso permitirá que o usuário selecione qualquer linha única no Modo de Exibição de Tabela. Retorne false para o ShouldSelectRow para qualquer linha que você não queira que o usuário possa selecionar ou false para cada linha se você não quiser que o usuário possa selecionar nenhuma linha.

O Modo de Exibição de Tabela (NSTableView) contém os seguintes métodos para trabalhar com a seleção de linha:

  • DeselectRow(nint) - Desmarca a linha especificada na tabela.
  • SelectRow(nint,bool) - Seleciona a linha fornecida. Passe false para o segundo parâmetro para selecionar apenas uma linha de cada vez.
  • SelectedRow – Retorna a linha atual selecionada na tabela.
  • IsRowSelected(nint) - Retornará true se a linha determinada estiver selecionada.

Seleção de várias linhas

Se você quiser permitir que o usuário selecione várias linhas, clique duas vezes no Main.storyboard arquivo para abri-lo para edição no Construtor de Interfaces. Selecione o Modo de Exibição de Tabela na Hierarquia de Interface e marcar a caixa de seleção Múltiplos no Inspetor de Atributos:

A captura de tela mostra o Construtor de Interfaces em que você pode selecionar Múltiplo para permitir a seleção de várias linhas.

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

Em seguida, edite o ProductTableDelegate.cs arquivo e adicione o seguinte método:

public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
  return true;
}

Isso permitirá que o usuário selecione qualquer linha única no Modo de Exibição de Tabela. Retorne false para o ShouldSelectRow para qualquer linha que você não queira que o usuário possa selecionar ou false para cada linha se você não quiser que o usuário possa selecionar nenhuma linha.

O Modo de Exibição de Tabela (NSTableView) contém os seguintes métodos para trabalhar com a seleção de linha:

  • DeselectAll(NSObject) - Desmarca todas as linhas na tabela. Use this para o primeiro parâmetro para enviar o objeto que está fazendo a seleção.
  • DeselectRow(nint) - Desmarca a linha especificada na tabela.
  • SelectAll(NSobject) - Seleciona todas as linhas na tabela. Use this para o primeiro parâmetro para enviar o objeto que está fazendo a seleção.
  • SelectRow(nint,bool) - Seleciona a linha fornecida. Passe false para o segundo parâmetro limpar a seleção e selecionar apenas uma única linha, passe true para estender a seleção e inclua essa linha.
  • SelectRows(NSIndexSet,bool) - Seleciona o conjunto de linhas especificado. Passe false para o segundo parâmetro limpar a seleção e selecionar apenas essas linhas, passar true para estender a seleção e incluir essas linhas.
  • SelectedRow – Retorna a linha atual selecionada na tabela.
  • SelectedRows – Retorna um NSIndexSet que contém os índices das linhas selecionadas.
  • SelectedRowCount – Retorna o número de linhas selecionadas.
  • IsRowSelected(nint) - Retornará true se a linha determinada estiver selecionada.

Digite para Selecionar Linha

Se você quiser permitir que o usuário digite um caractere com o Modo de Exibição de Tabela selecionado e selecione a primeira linha que tem esse caractere, clique duas vezes no Main.storyboard arquivo para abri-lo para edição no Construtor de Interfaces. Selecione o Modo de Exibição de Tabela na Hierarquia de Interface e marcar caixa de seleção Selecionar Tipo no Inspetor de Atributos:

Definindo o tipo de seleção

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

Agora vamos editar o ProductTableDelegate.cs arquivo e adicionar o seguinte método:

public override nint GetNextTypeSelectMatch (NSTableView tableView, nint startRow, nint endRow, string searchString)
{
  nint row = 0;
  foreach(Product product in DataSource.Products) {
    if (product.Title.Contains(searchString)) return row;

    // Increment row counter
    ++row;
  }

  // If not found select the first row
  return 0;
}

O GetNextTypeSelectMatch método usa o determinado searchString e retorna a linha do primeiro Product que tem essa cadeia de caracteres em que é Title.

Se executarmos o aplicativo e digitarmos um caractere, uma linha será selecionada:

Captura de tela que mostra o resultado da execução do aplicativo.

Reordenando colunas

Se você quiser permitir que o usuário arraste colunas de reordenação no Modo de Exibição de Tabela, clique duas vezes no Main.storyboard arquivo para abri-lo para edição no Construtor de Interfaces. Selecione o Modo de Exibição de Tabela na Hierarquia de Interface e marcar caixa de seleção Reordenação no Inspetor de Atributos:

A captura de tela mostra o Construtor de Interfaces em que você pode selecionar Reodering no Inspetor de Atributos.

Se fornecermos um valor para a propriedade Salvamento Automático e marcar campo Informações da Coluna, todas as alterações feitas no layout da tabela serão salvas automaticamente para nós e restauradas na próxima vez que o aplicativo for executado.

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

Agora vamos editar o ProductTableDelegate.cs arquivo e adicionar o seguinte método:

public override bool ShouldReorder (NSTableView tableView, nint columnIndex, nint newColumnIndex)
{
  return true;
}

O ShouldReorder método deve retornar true para qualquer coluna que deseje permitir que seja arrastado reordenado para o newColumnIndex, caso contrário, retornar false;

Se executarmos o aplicativo, poderemos arrastar cabeçalhos de coluna para reordenar nossas colunas:

Um exemplo das colunas reordenadas

Editando células

Se você quiser permitir que o usuário edite os valores de uma determinada célula, edite o ProductTableDelegate.cs arquivo e altere o GetViewForItem método da seguinte maneira:

public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{
  // This pattern allows you reuse existing views when they are no-longer in use.
  // If the returned view is null, you instance up a new view
  // If a non-null view is returned, you modify it enough to reflect the new data
  NSTextField view = (NSTextField)tableView.MakeView (tableColumn.Title, this);
  if (view == null) {
    view = new NSTextField ();
    view.Identifier = tableColumn.Title;
    view.BackgroundColor = NSColor.Clear;
    view.Bordered = false;
    view.Selectable = false;
    view.Editable = true;

    view.EditingEnded += (sender, e) => {
          
      // Take action based on type
      switch(view.Identifier) {
      case "Product":
        DataSource.Products [(int)view.Tag].Title = view.StringValue;
        break;
      case "Details":
        DataSource.Products [(int)view.Tag].Description = view.StringValue;
        break; 
      }
    };
  }

  // Tag view
  view.Tag = row;

  // Setup view based on the column selected
  switch (tableColumn.Title) {
  case "Product":
    view.StringValue = DataSource.Products [(int)row].Title;
    break;
  case "Details":
    view.StringValue = DataSource.Products [(int)row].Description;
    break;
  }

  return view;
}

Agora, se executarmos o aplicativo, o usuário poderá editar as células no Modo de Exibição de Tabela:

Um exemplo de edição de uma célula

Usando imagens em exibições de tabela

Para incluir uma imagem como parte da célula em um NSTableView, você precisará alterar como os dados são retornados pelo método do Modo de Exibição deGetViewForItemNSTableViewDelegate'sTabela para usar um NSTableCellView em vez do típico NSTextField. Por exemplo:

public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{

  // This pattern allows you reuse existing views when they are no-longer in use.
  // If the returned view is null, you instance up a new view
  // If a non-null view is returned, you modify it enough to reflect the new data
  NSTableCellView view = (NSTableCellView)tableView.MakeView (tableColumn.Title, this);
  if (view == null) {
    view = new NSTableCellView ();
    if (tableColumn.Title == "Product") {
      view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
      view.AddSubview (view.ImageView);
      view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
    } else {
      view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
    }
    view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
    view.AddSubview (view.TextField);
    view.Identifier = tableColumn.Title;
    view.TextField.BackgroundColor = NSColor.Clear;
    view.TextField.Bordered = false;
    view.TextField.Selectable = false;
    view.TextField.Editable = true;

    view.TextField.EditingEnded += (sender, e) => {

      // Take action based on type
      switch(view.Identifier) {
      case "Product":
        DataSource.Products [(int)view.TextField.Tag].Title = view.TextField.StringValue;
        break;
      case "Details":
        DataSource.Products [(int)view.TextField.Tag].Description = view.TextField.StringValue;
        break; 
      }
    };
  }

  // Tag view
  view.TextField.Tag = row;

  // Setup view based on the column selected
  switch (tableColumn.Title) {
  case "Product":
    view.ImageView.Image = NSImage.ImageNamed ("tags.png");
    view.TextField.StringValue = DataSource.Products [(int)row].Title;
    break;
  case "Details":
    view.TextField.StringValue = DataSource.Products [(int)row].Description;
    break;
  }

  return view;
}

Para obter mais informações, consulte a seção Usando imagens com exibições de tabela de nossa documentação Trabalhando com imagem .

Adicionando um botão Excluir a uma linha

Com base nos requisitos do seu aplicativo, pode haver ocasiões em que você precisa fornecer um botão de ação para cada linha na tabela. Como exemplo disso, vamos expandir o exemplo de Exibição de Tabela criado acima para incluir um botão Excluir em cada linha.

Primeiro, edite o Main.storyboard no Construtor de Interfaces do Xcode, selecione o Modo de Exibição de Tabela e aumente o número de colunas para três (3). Em seguida, altere o Título da nova coluna para Action:

Editando o nome da coluna

Salve as alterações no Storyboard e retorne ao Visual Studio para Mac para sincronizar as alterações.

Em seguida, edite o ViewController.cs arquivo e adicione o seguinte método público:

public void ReloadTable ()
{
  ProductTable.ReloadData ();
}

No mesmo arquivo, modifique a criação do novo Delegado de Exibição de Tabela dentro do método da ViewDidLoad seguinte maneira:

// Populate the Product Table
ProductTable.DataSource = DataSource;
ProductTable.Delegate = new ProductTableDelegate (this, DataSource);

Agora, edite o ProductTableDelegate.cs arquivo para incluir uma conexão privada com o Controlador de Exibição e para usar o controlador como um parâmetro ao criar uma nova instância do delegado:

#region Private Variables
private ProductTableDataSource DataSource;
private ViewController Controller;
#endregion

#region Constructors
public ProductTableDelegate (ViewController controller, ProductTableDataSource datasource)
{
  this.Controller = controller;
  this.DataSource = datasource;
}
#endregion

Em seguida, adicione o seguinte novo método privado à classe :

private void ConfigureTextField (NSTableCellView view, nint row)
{
  // Add to view
  view.TextField.AutoresizingMask = NSViewResizingMask.WidthSizable;
  view.AddSubview (view.TextField);

  // Configure
  view.TextField.BackgroundColor = NSColor.Clear;
  view.TextField.Bordered = false;
  view.TextField.Selectable = false;
  view.TextField.Editable = true;

  // Wireup events
  view.TextField.EditingEnded += (sender, e) => {

    // Take action based on type
    switch (view.Identifier) {
    case "Product":
      DataSource.Products [(int)view.TextField.Tag].Title = view.TextField.StringValue;
      break;
    case "Details":
      DataSource.Products [(int)view.TextField.Tag].Description = view.TextField.StringValue;
      break;
    }
  };

  // Tag view
  view.TextField.Tag = row;
}

Isso usa todas as configurações de Exibição de Texto que estavam sendo feitas anteriormente no método e as GetViewForItem coloca em um único local que pode ser chamado (já que a última coluna da tabela não inclui um Modo de Exibição de Texto, mas um Botão).

Por fim, edite o GetViewForItem método e faça com que ele se pareça com o seguinte:

public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{

  // This pattern allows you reuse existing views when they are no-longer in use.
  // If the returned view is null, you instance up a new view
  // If a non-null view is returned, you modify it enough to reflect the new data
  NSTableCellView view = (NSTableCellView)tableView.MakeView (tableColumn.Title, this);
  if (view == null) {
    view = new NSTableCellView ();

    // Configure the view
    view.Identifier = tableColumn.Title;

    // Take action based on title
    switch (tableColumn.Title) {
    case "Product":
      view.ImageView = new NSImageView (new CGRect (0, 0, 16, 16));
      view.AddSubview (view.ImageView);
      view.TextField = new NSTextField (new CGRect (20, 0, 400, 16));
      ConfigureTextField (view, row);
      break;
    case "Details":
      view.TextField = new NSTextField (new CGRect (0, 0, 400, 16));
      ConfigureTextField (view, row);
      break;
    case "Action":
      // Create new button
      var button = new NSButton (new CGRect (0, 0, 81, 16));
      button.SetButtonType (NSButtonType.MomentaryPushIn);
      button.Title = "Delete";
      button.Tag = row;

      // Wireup events
      button.Activated += (sender, e) => {
        // Get button and product
        var btn = sender as NSButton;
        var product = DataSource.Products [(int)btn.Tag];

        // Configure alert
        var alert = new NSAlert () {
          AlertStyle = NSAlertStyle.Informational,
          InformativeText = $"Are you sure you want to delete {product.Title}? This operation cannot be undone.",
          MessageText = $"Delete {product.Title}?",
        };
        alert.AddButton ("Cancel");
        alert.AddButton ("Delete");
        alert.BeginSheetForResponse (Controller.View.Window, (result) => {
          // Should we delete the requested row?
          if (result == 1001) {
            // Remove the given row from the dataset
            DataSource.Products.RemoveAt((int)btn.Tag);
            Controller.ReloadTable ();
          }
        });
      };

      // Add to view
      view.AddSubview (button);
      break;
    }

  }

  // Setup view based on the column selected
  switch (tableColumn.Title) {
  case "Product":
    view.ImageView.Image = NSImage.ImageNamed ("tag.png");
    view.TextField.StringValue = DataSource.Products [(int)row].Title;
    view.TextField.Tag = row;
    break;
  case "Details":
    view.TextField.StringValue = DataSource.Products [(int)row].Description;
    view.TextField.Tag = row;
    break;
  case "Action":
    foreach (NSView subview in view.Subviews) {
      var btn = subview as NSButton;
      if (btn != null) {
        btn.Tag = row;
      }
    }
    break;
  }

  return view;
}

Vamos examinar várias seções desse código mais detalhadamente. Primeiro, se uma nova NSTableViewCell ação estiver sendo criada, será executada com base no nome da Coluna. Para as duas primeiras colunas (Product e Details), o novo ConfigureTextField método é chamado.

Para a coluna Ação , um novo NSButton é criado e adicionado à célula como uma subexibição:

// Create new button
var button = new NSButton (new CGRect (0, 0, 81, 16));
button.SetButtonType (NSButtonType.MomentaryPushIn);
button.Title = "Delete";
button.Tag = row;
...

// Add to view
view.AddSubview (button);

A propriedade do Tag Botão é usada para conter o número da Linha que está sendo processada no momento. Esse número será usado posteriormente quando o usuário solicitar que uma linha seja excluída no evento do Activated Botão:

// Wireup events
button.Activated += (sender, e) => {
  // Get button and product
  var btn = sender as NSButton;
  var product = DataSource.Products [(int)btn.Tag];

  // Configure alert
  var alert = new NSAlert () {
    AlertStyle = NSAlertStyle.Informational,
    InformativeText = $"Are you sure you want to delete {product.Title}? This operation cannot be undone.",
    MessageText = $"Delete {product.Title}?",
  };
  alert.AddButton ("Cancel");
  alert.AddButton ("Delete");
  alert.BeginSheetForResponse (Controller.View.Window, (result) => {
    // Should we delete the requested row?
    if (result == 1001) {
      // Remove the given row from the dataset
      DataSource.Products.RemoveAt((int)btn.Tag);
      Controller.ReloadTable ();
    }
  });
};

No início do manipulador de eventos, obtemos o botão e o produto que está na linha de tabela fornecida. Em seguida, um Alerta é apresentado ao usuário confirmando a exclusão da linha. Se o usuário optar por excluir a linha, a linha fornecida será removida da Fonte de Dados e a tabela será recarregada:

// Remove the given row from the dataset
DataSource.Products.RemoveAt((int)btn.Tag);
Controller.ReloadTable ();

Por fim, se a Célula de Exibição de Tabela estiver sendo reutilizada em vez de ser criada novamente, o seguinte código a configurará com base na Coluna que está sendo processada:

// Setup view based on the column selected
switch (tableColumn.Title) {
case "Product":
  view.ImageView.Image = NSImage.ImageNamed ("tag.png");
  view.TextField.StringValue = DataSource.Products [(int)row].Title;
  view.TextField.Tag = row;
  break;
case "Details":
  view.TextField.StringValue = DataSource.Products [(int)row].Description;
  view.TextField.Tag = row;
  break;
case "Action":
  foreach (NSView subview in view.Subviews) {
    var btn = subview as NSButton;
    if (btn != null) {
      btn.Tag = row;
    }
  }
  break;
}

Para a coluna Ação , todas as Subexibições são verificadas até que o NSButton seja encontrado e, em seguida, sua Tag propriedade é atualizada para apontar para a Linha atual.

Com essas alterações em vigor, quando o aplicativo for executado, cada linha terá um botão Excluir :

O modo de exibição de tabela com botões de exclusão

Quando o usuário clicar em um botão Excluir , um alerta será exibido solicitando que ele exclua a linha especificada:

Um alerta de exclusão de linha

Se o usuário escolher excluir, a linha será removida e a tabela será redesenhada:

A tabela depois que a linha é excluída

Exibições de tabela de associação de dados

Usando Key-Value técnicas de Codificação e Associação de Dados em seu aplicativo Xamarin.Mac, você pode diminuir consideravelmente a quantidade de código que precisa escrever e manter para preencher e trabalhar com elementos da interface do usuário. Você também tem o benefício de desacoplar ainda mais seus dados de backup (Modelo de Dados) da interface do usuário front-end (Model-View-Controller), levando a um design de aplicativo mais fácil de manter e flexível.

Key-Value Coding (KVC) é um mecanismo para acessar indiretamente as propriedades de um objeto, usando chaves (cadeias de caracteres especialmente formatadas) para identificar propriedades em vez de acessá-las por meio de variáveis de instância ou métodos de acessador (get/set). Ao implementar Key-Value acessadores compatíveis com codificação em seu aplicativo Xamarin.Mac, você obtém acesso a outros recursos do macOS, como Key-Value Observing (KVO), Associação de Dados, Dados Principais, Associações cocoa e capacidade de script.

Para obter mais informações, consulte a seção Associação de dados de exibição de tabela de nossa documentação associação de dados e codificação de Key-Value .

Resumo

Este artigo analisou detalhadamente como trabalhar com exibições de tabela em um aplicativo Xamarin.Mac. Vimos os diferentes tipos e usos de exibições de tabela, como criar e manter exibições de tabela no Construtor de Interfaces do Xcode e como trabalhar com exibições de tabela no código C#.