Zobrazení tabulek v Xamarin.Mac

Tento článek popisuje práci se zobrazeními tabulek v aplikaci Xamarin.Mac. Popisuje vytváření zobrazení tabulek v Xcode a Tvůrci rozhraní a interakci s nimi v kódu.

Při práci s C# a .NET v aplikaci Xamarin.Mac máte přístup ke stejným zobrazením tabulek, ve kterých vývojář pracuje Objective-C , a Xcode . Vzhledem k tomu, že Xamarin.Mac se integruje přímo s Xcode, můžete pomocí Tvůrce rozhraní Xcode vytvářet a udržovat zobrazení tabulek (nebo je volitelně vytvářet přímo v kódu jazyka C#).

Zobrazení tabulky zobrazuje data v tabulkovém formátu obsahujícím jeden nebo více sloupců informací v několika řádcích. Na základě typu vytvořeného zobrazení tabulky může uživatel řadit podle sloupců, změnit uspořádání sloupců, přidat sloupce, odebrat sloupce nebo upravit data obsažená v tabulce.

An example table

V tomto článku se podíváme na základy práce se zobrazeními tabulek v aplikaci Xamarin.Mac. Důrazně doporučujeme nejprve projít si článek Hello, Mac , konkrétně úvod do Xcode a Interface Builder aOutlets and Actions oddíly, protože se zabývá klíčovými koncepty a technikami, které budeme používat v tomto článku.

Můžete se také podívat na třídy / metody pro zveřejnění kódu jazyka C# v Objective-C části dokumentu Interní dokumenty Xamarin.Mac , vysvětluje Register a Export příkazy používané k převodu tříd jazyka C# na Objective-C objekty a prvky uživatelského rozhraní.

Úvod do zobrazení tabulek

Zobrazení tabulky zobrazuje data v tabulkovém formátu obsahujícím jeden nebo více sloupců informací v několika řádcích. Zobrazení tabulky se zobrazují uvnitř zobrazení posouvání (NSScrollView) a počínaje macOS 10.7 můžete místo buněk (NSCell) zobrazit NSView řádky i sloupce. To znamená, že stále můžete použít NSCell , ale obvykle podtřídu NSTableCellView a vytvoříte vlastní řádky a sloupce.

Zobrazení tabulky neukládá vlastní data, místo toho spoléhá na zdroj dat (NSTableViewDataSource) k poskytnutí požadovaných řádků i sloupců podle potřeby.

Chování zobrazení tabulky lze přizpůsobit tím, že poskytuje podtřídu delegáta zobrazení tabulky (NSTableViewDelegate) pro podporu správy sloupců tabulky, typ pro výběr funkcí, výběr řádků a úpravy, vlastní sledování a vlastní zobrazení pro jednotlivé sloupce a řádky.

Při vytváření zobrazení tabulek Apple navrhuje následující:

  • Umožňuje uživateli seřadit tabulku kliknutím na záhlaví sloupců.
  • Vytvořte záhlaví sloupců, která jsou jmenná jména nebo krátké fráze jmen, které popisují data zobrazená v tomto sloupci.

Další informace najdete v části Zobrazení obsahuv pokynech pro lidské rozhraní společnosti Apple oS X.

Vytváření a údržba zobrazení tabulek v Xcode

Když vytvoříte novou aplikaci Xamarin.Mac Cocoa, ve výchozím nastavení získáte standardní prázdné okno. Tato okna jsou definována .storyboard v souboru automaticky zahrnuté v projektu. Pokud chcete upravit návrh oken, poklikejte v Průzkumník řešení na Main.storyboard soubor:

Selecting the main storyboard

Tím se otevře návrh okna v Tvůrci rozhraní Xcode:

Editing the UI in Xcode

Zadejte table do vyhledávacího pole inspektoru knihovny , abyste usnadnili vyhledání ovládacích prvků Zobrazení tabulky:

Selecting a Table View from the Library

Přetáhněte zobrazení tabulky na kontroler zobrazení v editoru rozhraní, aby vyplnil oblast obsahu kontroleru zobrazení a nastavte ho na místo, kam se zmenší a zvětší s oknem v Editoru omezení:

Editing constraints

Vyberte zobrazení tabulky v hierarchii rozhraní a v inspektoru atributů jsou k dispozici následující vlastnosti:

Screenshot shows the properties available in the Attribute Inspector.

  • Režim obsahu – Umožňuje použít zobrazení () nebo buňky (NSViewNSCell) k zobrazení dat v řádcích a sloupcích. Počínaje macOS 10.7 byste měli používat zobrazení.
  • Floats Seskupuje řádky – Pokud true, bude zobrazení tabulky kreslit seskupené buňky, jako by byly plovoucí.
  • Sloupce – Definuje počet zobrazených sloupců.
  • Záhlaví – Pokud truesloupce budou obsahovat záhlaví.
  • Změna pořadí – Pokud true, uživatel bude moct přetáhnout sloupce v tabulce.
  • Změna velikosti – Pokud truebude uživatel moct přetáhnout záhlaví sloupců, aby změnil velikost sloupců.
  • Změna velikosti sloupců – Určuje, jak bude tabulka automaticky měnit velikost sloupců.
  • Zvýraznění – Určuje typ zvýraznění tabulky, která se používá při výběru buňky.
  • Alternativní řádky – Pokud true, někdy jiný řádek bude mít jinou barvu pozadí.
  • Vodorovná mřížka – vybere typ ohraničení nakresleného mezi buňkami vodorovně.
  • Svislá mřížka – vybere typ ohraničení nakresleného mezi buňkami svisle.
  • Barva mřížky – Nastaví barvu ohraničení buňky.
  • Pozadí – Nastaví barvu pozadí buňky.
  • Výběr – Umožňuje řídit, jak může uživatel vybrat buňky v tabulce jako:
    • Více – Pokud truemůže uživatel vybrat více řádků a sloupců.
    • Sloupec – Pokud trueuživatel může vybrat sloupce.
    • Typ Vybrat – Pokud truemůže uživatel zadat znak pro výběr řádku.
    • Prázdné – Pokud trueuživatel nemusí vybrat řádek nebo sloupec, tabulka neumožňuje vůbec žádný výběr.
  • Automatické ukládání – název, pod kterým se formát tabulek automaticky uloží.
  • Informace o sloupci – Pokud truese pořadí a šířka sloupců automaticky uloží.
  • Konce řádků – Vyberte, jak buňka zpracovává konce řádků.
  • Zkrátí poslední viditelnou čáru – pokud truese buňka zkrátí v datech, nemůže se vejít do hranic.

Důležité

Pokud nechováte starší verzi aplikace Xamarin.Mac, NSView měla by se zobrazení tabulek používat na NSCell základě zobrazení tabulek. NSCell je považováno za starší verzi a nemusí být podporováno v budoucnu.

Vyberte sloupec tabulky v hierarchii rozhraní a v inspektoru atributů jsou k dispozici následující vlastnosti:

Screenshot shows the properties available for a Table Column in the Attribute Inspector.

  • Název – Nastaví název sloupce.
  • Zarovnání – Nastavte zarovnání textu v buňkách.
  • Písmo nadpisu – Vybere písmo pro text záhlaví buňky.
  • Klíč řazení – je klíč použitý k řazení dat ve sloupci. Pokud uživatel nemůže tento sloupec seřadit, ponechejte prázdné.
  • Selektor – jedná se o akci použitou k provedení řazení. Pokud uživatel nemůže tento sloupec seřadit, ponechejte prázdné.
  • Pořadí – je pořadí řazení pro data sloupců.
  • Změna velikosti – Vybere typ změny velikosti sloupce.
  • Upravitelné – Pokud truemůže uživatel upravovat buňky v tabulce založené na buňce.
  • Skryté – Pokud trueje sloupec skrytý.

Můžete také změnit velikost sloupce přetažením úchytu (svisle na střed na pravé straně sloupce) doleva nebo doprava.

Pojďme vybrat každý sloupec v našem zobrazení tabulky a dát první sloupec názevProduct a druhý sloupec Details.

V hierarchii rozhraní vyberte zobrazení buněk tabulky (NSTableViewCell) a v inspektoru atributů jsou k dispozici následující vlastnosti:

Screenshot shows the properties available for a Table Cell View in the Attribute Inspector.

Jedná se o všechny vlastnosti standardního zobrazení. Tady máte také možnost změnit velikost řádků pro tento sloupec.

Vyberte buňku zobrazení tabulky (ve výchozím nastavení je to NSTextField) v hierarchii rozhraní a v inspektoru atributů jsou k dispozici následující vlastnosti:

Screenshot shows the properties available for a Table View Cell in the Attribute Inspector.

Budete mít všechny vlastnosti standardního textového pole, které se tady nastaví. Ve výchozím nastavení se standardní textové pole používá k zobrazení dat pro buňku ve sloupci.

V hierarchii rozhraní vyberte zobrazení buněk tabulky (NSTableFieldCell) a v inspektoru atributů jsou k dispozici následující vlastnosti:

Screenshot shows the properties available for a different Table View Cell in the Attribute Inspector.

Nejdůležitější nastavení jsou:

  • Rozložení – Vyberte způsob rozložení buněk v tomto sloupci.
  • Používá režim jednořádkového řádku – Pokud trueje buňka omezena na jeden řádek.
  • První šířka rozložení modulu runtime – Pokud truebude buňka upřednostňovat šířku nastavenou (buď ručně, nebo automaticky), když se zobrazí při prvním spuštění aplikace.
  • Akce – určuje, kdy se pro buňku odešle akce pro úpravy.
  • Chování – definuje, jestli je buňka vybratelná nebo upravitelná.
  • Formátovaný text – Pokud truemůže buňka zobrazit formátovaný a stylovaný text.
  • Zpět - Pokud truebuňka přebírá odpovědnost za chování zpět.

Vyberte zobrazení buněk tabulky (NSTableFieldCell) v dolní části sloupce tabulky v hierarchii rozhraní:

Selecting the Table Cell View

To umožňuje upravit zobrazení buňky tabulky použité jako základní vzor pro všechny buňky vytvořené pro daný sloupec.

Přidání akcí a zásuvek

Stejně jako jakýkoli jiný ovládací prvek uživatelského rozhraní Cocoa musíme vystavit zobrazení tabulky a je to sloupce a buňky kódu jazyka C# pomocí akcí a výstupů (na základě požadovaných funkcí).

Proces je stejný pro libovolný prvek Zobrazení tabulky, který chceme vystavit:

  1. Přepněte do Editoru asistentů a ujistěte se, že ViewController.h je soubor vybraný:

    The Assistant Editor

  2. V hierarchii rozhraní vyberte zobrazení tabulky, klikněte na něj a přetáhněte ho ViewController.h do souboru.

  3. Vytvořte zásuvku pro zobrazení tabulky s názvem ProductTable:

    Screenshot shows an Outlet connection created for the Table View named ProductTable.

  4. Vytvořte zásuvky pro sloupce tabulek a také volejte ProductColumn a DetailsColumn:

    Screenshot shows an Outlet connections created for other Table Views.

  5. Uložte změny a vraťte se do Visual Studio pro Mac pro synchronizaci s Xcode.

Dále napíšeme kód, který zobrazí některá data tabulky při spuštění aplikace.

Naplnění zobrazení tabulky

S naším zobrazením tabulky navrženým v Tvůrci rozhraní a vystaveným prostřednictvím zásuvky musíme vytvořit kód jazyka C#, který ho naplní.

Nejprve vytvoříme novou Product třídu, která bude obsahovat informace pro jednotlivé řádky. V Průzkumník řešení klikněte pravým tlačítkem na Project a vyberte AddNew>File... Vyberte Třídu GeneralEmpty>, zadejte Productnázev a klikněte na tlačítko Nový:

Creating an empty class

Udělejte soubor Product.cs takto:

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

Dále musíme vytvořit podtřídu, která NSTableDataSource bude poskytovat data pro naši tabulku, jak je požadována. V Průzkumník řešení klikněte pravým tlačítkem na Project a vyberte AddNew>File... Vyberte TříduGeneralEmpty>, zadejte ProductTableDataSourcenázev a klikněte na tlačítko Nový.

ProductTableDataSource.cs Upravte soubor a udělejte ho takto:

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

Tato třída má úložiště pro položky zobrazení tabulky a přepíše GetRowCount , aby vrátil počet řádků v tabulce.

Nakonec musíme vytvořit podtřídu NSTableDelegate , která bude poskytovat chování pro naši tabulku. V Průzkumník řešení klikněte pravým tlačítkem na Project a vyberte AddNew>File... Vyberte TříduGeneralEmpty>, zadejte ProductTableDelegatenázev a klikněte na tlačítko Nový.

ProductTableDelegate.cs Upravte soubor a udělejte ho takto:

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

Když vytvoříme instanci objektu ProductTableDelegate, předáme také instanci ProductTableDataSource , která poskytuje data pro tabulku. Tato GetViewForItem metoda zodpovídá za vrácení zobrazení (dat) k zobrazení buňky pro sloupec a řádek. Pokud je to možné, bude existující zobrazení znovu použito k zobrazení buňky, pokud není nové zobrazení nutné vytvořit.

Pokud chcete vyplnit tabulku, upravme ViewController.cs soubor a udělejte metodu AwakeFromNib takto:

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

Pokud spustíme aplikaci, zobrazí se následující:

Screenshot shows a window named Product Table with three entries.

Řazení podle sloupce

Umožníme uživateli řadit data v tabulce kliknutím na záhlaví sloupce. Nejprve poklikáním na Main.storyboard soubor otevřete soubor pro úpravy v Tvůrci rozhraní. Product Vyberte sloupec, zadejte Title klíč řazení, compare: pro selektor a vyberte Ascendingpořadí:

Screenshot shows the Interface Builder where you can set the sort key for the Product column.

Details Vyberte sloupec, zadejte Description klíč řazení, compare: pro selektor a vyberte Ascendingpořadí:

Screenshot shows the Interface Builder where you can set the sort key for the Details column.

Uložte změny a vraťte se do Visual Studio pro Mac pro synchronizaci s Xcode.

Teď soubor upravíme ProductTableDataSource.cs a přidáme následující metody:

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

Metoda Sort nám umožňuje seřadit data ve zdroji dat na základě daného Product pole třídy vzestupně nebo sestupně. Přepsaná SortDescriptorsChanged metoda bude volána při každém kliknutí na záhlaví sloupce. Předá se hodnota Klíč , kterou jsme nastavili v Tvůrci rozhraní a pořadí řazení pro daný sloupec.

Pokud spustíme aplikaci a klikneme na záhlaví sloupců, řádky se seřadí podle daného sloupce:

An example app run

Výběr řádku

Pokud chcete uživateli povolit výběr jednoho řádku, poklikejte na soubor a otevřete ho Main.storyboard pro úpravy v Tvůrci rozhraní. Vyberte zobrazení tabulky v hierarchii rozhraní a zrušte zaškrtnutí políčka Více v inspektoru atributů:

Screenshot shows the Interface Builder where you can select Multiple in the Attribute Inspector.

Uložte změny a vraťte se do Visual Studio pro Mac pro synchronizaci s Xcode.

Dále upravte ProductTableDelegate.cs soubor a přidejte následující metodu:

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

To uživateli umožní vybrat libovolný jeden řádek v zobrazení tabulky. Vraťte false se pro ShouldSelectRow libovolný řádek, který nechcete, aby uživatel mohl vybrat nebo false pro každý řádek, pokud nechcete, aby uživatel mohl vybrat žádné řádky.

Zobrazení tabulky (NSTableView) obsahuje následující metody pro práci s výběrem řádků:

  • DeselectRow(nint) – Zruší výběr daného řádku v tabulce.
  • SelectRow(nint,bool) - Vybere daný řádek. Předání false druhého parametru pro výběr pouze jednoho řádku najednou
  • SelectedRow - Vrátí aktuální řádek vybraný v tabulce.
  • IsRowSelected(nint) - Vrátí true , pokud je vybraný daný řádek.

Výběr více řádků

Pokud chcete uživateli povolit výběr více řádků, poklikejte na soubor a otevřete ho Main.storyboard pro úpravy v Tvůrci rozhraní. Vyberte zobrazení tabulky v hierarchii rozhraní a zaškrtněte políčko Více v inspektoru atributů:

Screenshot shows the Interface Builder where you can select Multiple to allow multiple row selection.

Uložte změny a vraťte se do Visual Studio pro Mac pro synchronizaci s Xcode.

Dále upravte ProductTableDelegate.cs soubor a přidejte následující metodu:

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

To uživateli umožní vybrat libovolný jeden řádek v zobrazení tabulky. Vraťte false se pro ShouldSelectRow libovolný řádek, který nechcete, aby uživatel mohl vybrat nebo false pro každý řádek, pokud nechcete, aby uživatel mohl vybrat žádné řádky.

Zobrazení tabulky (NSTableView) obsahuje následující metody pro práci s výběrem řádků:

  • DeselectAll(NSObject) – Zruší výběr všech řádků v tabulce. Slouží this k odeslání prvního parametru do objektu, který provádí výběr.
  • DeselectRow(nint) – Zruší výběr daného řádku v tabulce.
  • SelectAll(NSobject) – Vybere všechny řádky v tabulce. Slouží this k odeslání prvního parametru do objektu, který provádí výběr.
  • SelectRow(nint,bool) - Vybere daný řádek. Předání false druhého parametru vymaže výběr a vybere jenom jeden řádek, předejte true výběr a tento řádek zahrňte.
  • SelectRows(NSIndexSet,bool) - Vybere danou sadu řádků. Předejte false druhý parametr vymažte výběr a vyberte pouze tyto řádky, předejte výběr a zahrňte true tyto řádky.
  • SelectedRow - Vrátí aktuální řádek vybraný v tabulce.
  • SelectedRows - Vrátí NSIndexSet indexy vybraných řádků.
  • SelectedRowCount - Vrátí počet vybraných řádků.
  • IsRowSelected(nint) - Vrátí true , pokud je vybraný daný řádek.

Typ pro výběr řádku

Pokud chcete povolit uživateli zadat znak s vybraným zobrazením tabulky a vybrat první řádek s tímto znakem, poklikejte na Main.storyboard soubor a otevřete ho pro úpravy v Tvůrci rozhraní. Vyberte zobrazení tabulky v hierarchii rozhraní a zaškrtněte políčko Vybrat typ v inspektoru atributů:

Setting the selection type

Uložte změny a vraťte se do Visual Studio pro Mac pro synchronizaci s Xcode.

Teď soubor upravíme ProductTableDelegate.cs a přidáme následující metodu:

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

Metoda GetNextTypeSelectMatch vezme danou searchString a vrátí řádek prvního Product , který má tento řetězec v něm Title.

Pokud spustíme aplikaci a zadáme znak, vybere se řádek:

Screenshot shows the result of running the application.

Změna pořadí sloupců

Pokud chcete uživateli povolit přetahování sloupců v zobrazení tabulky, poklikejte na soubor a otevřete ho Main.storyboard pro úpravy v Tvůrci rozhraní. Vyberte zobrazení tabulky v hierarchii rozhraní a zaškrtněte políčko Změnit pořadí v Inspektoru atributů:

Screenshot shows the Interface Builder where you can select Reodering in the Attribute Inspector.

Pokud zadáme hodnotu vlastnosti automatického ukládání a zkontrolujeme pole Informace o sloupci , všechny změny provedené v rozložení tabulky se automaticky uloží pro nás a obnoví se při příštím spuštění aplikace.

Uložte změny a vraťte se do Visual Studio pro Mac pro synchronizaci s Xcode.

Teď soubor upravíme ProductTableDelegate.cs a přidáme následující metodu:

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

Metoda ShouldReorder by měla vrátit true pro libovolný sloupec, který chcete povolit přetáhnout newColumnIndexdo , jinak vrátit false;

Pokud spustíme aplikaci, můžeme přetáhnout záhlaví sloupců kolem a změnit pořadí sloupců:

An example of the reordered columns

Úpravy buněk

Pokud chcete uživateli povolit úpravu hodnot pro danou buňku, upravte ProductTableDelegate.cs soubor a změňte metodu GetViewForItem následujícím způsobem:

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

Když teď aplikaci spustíme, může uživatel upravit buňky v zobrazení tabulky:

An example of editing a cell

Použití obrázků v zobrazeních tabulek

Pokud chcete do buňky zahrnout obrázek jako součást buňkyNSTableView, musíte změnit způsob vrácení dat metodou zobrazeníGetViewForItemNSTableViewDelegate'stabulky tak, aby se místo typického objektu používala NSTableCellViewNSTextField. Příklad:

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

Další informace najdete v části Používání obrázků se zobrazeními tabulek v naší dokumentaci k práci s obrázkem .

Přidání tlačítka Odstranit na řádek

Na základě požadavků vaší aplikace můžou nastat situace, kdy potřebujete zadat tlačítko akce pro každý řádek v tabulce. V tomto příkladu rozbalme příklad zobrazení tabulky vytvořený výše, abychom do každého řádku zahrnuli tlačítko Odstranit .

Nejprve upravte Main.storyboard tvůrce rozhraní Xcode, vyberte zobrazení tabulky a zvyšte počet sloupců na tři (3). V dalším kroku změňte název nového sloupce na Action:

Editing the column name

Uložte změny do scénáře a vraťte se do Visual Studio pro Mac, aby se změny synchronizovaly.

Dále upravte ViewController.cs soubor a přidejte následující veřejnou metodu:

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

Ve stejném souboru upravte vytvoření nového delegáta zobrazení tabulky uvnitř ViewDidLoad metody následujícím způsobem:

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

Teď upravte ProductTableDelegate.cs soubor tak, aby zahrnoval privátní připojení k řadiči zobrazení a při vytváření nové instance delegáta převezměte kontroler jako parametr:

#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

Dále do třídy přidejte následující novou privátní metodu:

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

To přebírá všechny konfigurace zobrazení textu, které byly dříve provedeny v GetViewForItem metodě, a umístí je do jediného volatelného umístění (od posledního sloupce tabulky neobsahuje textové zobrazení, ale tlačítko).

Nakonec upravte metodu GetViewForItem a udělejte ji takto:

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

Pojďme se podrobněji podívat na několik částí tohoto kódu. Nejprve, pokud se vytvoří nová NSTableViewCell akce založená na názvu sloupce. Pro první dva sloupce (Product a Details) se volá nová ConfigureTextField metoda.

Pro sloupec Akce se vytvoří nový NSButton a přidá se do buňky jako dílčí zobrazení:

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

Vlastnost Button Tag se používá k uložení čísla řádku, který se právě zpracovává. Toto číslo se použije později, když uživatel požádá o odstranění řádku v události tlačítka Activated :

// 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 ();
    }
  });
};

Na začátku obslužné rutiny události získáme tlačítko a produkt, který je na daném řádku tabulky. Uživateli, který potvrdí odstranění řádku, se zobrazí výstraha. Pokud se uživatel rozhodne řádek odstranit, daný řádek se odebere ze zdroje dat a tabulka se znovu načte:

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

Pokud se buňka zobrazení tabulky místo vytvoření nové znovu používá, nakonfiguruje ji následující kód na základě zpracovávaného sloupce:

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

U sloupce Akce se zkontrolují všechna dílčí zobrazení, dokud se nenajde NSButton , a vlastnost Tag se aktualizuje tak, aby ukazovala na aktuální řádek.

Po provedení těchto změn bude mít aplikace při každém spuštění každého řádku tlačítko Odstranit :

The table view with deletion buttons

Když uživatel klikne na tlačítko Odstranit , zobrazí se upozornění s žádostí o odstranění daného řádku:

A delete row alert

Pokud uživatel zvolí odstranění, řádek se odebere a tabulka se překreslí:

The table after the row is deleted

Zobrazení tabulky datových vazeb

Pomocí technik kódování Key-Value kódování a datových vazeb v aplikaci Xamarin.Mac můžete výrazně snížit množství kódu, který musíte psát a udržovat pro naplnění a práci s prvky uživatelského rozhraní. Výhodou je také další oddělení backingových dat (datový model) od front-endového uživatelského rozhraní (Model-View-Controller), což usnadňuje údržbu a flexibilnější návrh aplikací.

Key-Value Kódování (KVC) je mechanismus pro přístup k vlastnostem objektu nepřímo pomocí klíčů (speciálně formátovaných řetězců) k identifikaci vlastností místo přístupu k nim prostřednictvím proměnných instancí nebo metod přístupových objektů (get/set). Implementací Key-Value přístupových objektů kompatibilních s kódováním v aplikaci Xamarin.Mac získáte přístup k dalším funkcím macOS, jako jsou Key-Value Observing (KVO), datové vazby, základní data, vazby Cocoa a skriptovatelnost.

Další informace najdete v části Datové vazby zobrazení tabulky v dokumentaci k datové vazbě a Key-Value kódování .

Souhrn

Tento článek se podrobně podíval na práci se zobrazeními tabulek v aplikaci Xamarin.Mac. Viděli jsme různé typy a použití zobrazení tabulek, jak vytvářet a udržovat zobrazení tabulek v Tvůrci rozhraní Xcode a jak pracovat se zobrazeními tabulek v kódu jazyka C#.