Xamarin.Mac のテーブル ビュー

この記事では、Xamarin.Mac アプリケーションでのテーブル ビューの操作について説明します。 ここでは、Xcode と Interface Builder でテーブル ビューを作成し、それらをコードで操作する方法について説明します。

Xamarin.Mac アプリケーションで C# と .NET を使用する場合は、開発者と Xcode で作業しているのと同じテーブル ビューにObjective-Cアクセスできます。 Xamarin.Mac は Xcode と直接統合されるため、Xcode の Interface Builder を使用してテーブル ビューを作成および保守できます (または、必要に応じて C# コードで直接作成することもできます)。

テーブル ビューでは、1 つ以上の情報列を複数の行に含む表形式でデータが表示されます。 作成されるテーブル ビューの種類に基づいて、ユーザーは列による並べ替え、列の再構成、列の追加、列の削除、またはテーブルに含まれるデータの編集を行うことができます。

テーブルの例

この記事では、Xamarin.Mac アプリケーションでのテーブル ビューの操作の基本について説明します。 この記事で使用する主要な概念と手法については、「 Hello, Mac 」の記事、特 に「Xcode とインターフェイス ビルダーアウトレットとアクション の概要」セクションを最初に説明することを強くお勧めします。

Xamarin.Mac Internals ドキュメントの「C# クラス/メソッドを Xamarin.Mac Internals に公開するObjective-C」セクションも参照してください。C# クラスObjective-Cをオブジェクトと UI 要素に結び付けるために使用される コマンドと Export コマンドについても説明Registerしています。

テーブル ビューの概要

テーブル ビューでは、1 つ以上の情報列を複数の行に含む表形式でデータが表示されます。 テーブル ビューはスクロール ビュー (NSScrollView) 内に表示され、macOS 10.7 以降では、セル (NSCell) の代わりに任意NSViewの を使用して行と列の両方を表示できます。 ただし、使用することはできます NSCell が、通常はカスタムの行と列をサブクラス化 NSTableCellView して作成します。

テーブル ビューでは、独自のデータは格納されません。代わりに、データ ソース (NSTableViewDataSource) に依存して、必要に応じて必要な行と列の両方を提供します。

テーブル ビューの動作をカスタマイズするには、テーブル ビュー デリゲート (NSTableViewDelegate) のサブクラスを指定して、テーブル列の管理をサポートし、型を使用して機能を選択し、行の選択と編集、カスタム追跡、および個々の列と行のカスタム ビューを指定します。

テーブル ビューを作成する場合、Apple は次のことを提案します。

  • ユーザーが列ヘッダーをクリックしてテーブルを並べ替えることを許可します。
  • その列に表示されるデータを記述する名詞または短い名詞句である列ヘッダーを作成します。

詳細については、Apple の OS X ヒューマン インターフェイス ガイドライン「コンテンツ ビュー」セクションを参照してください。

Xcode でのテーブル ビューの作成と保守

新しい Xamarin.Mac Cocoa アプリケーションを作成すると、既定で標準の空白のウィンドウが表示されます。 このウィンドウは、プロジェクトに .storyboard 自動的に含まれるファイルで定義されます。 Windows デザインを編集するには、ソリューション エクスプローラーでファイルをMain.storyboardダブルクリックします。

メインストーリーボードの選択

これにより、Xcode のインターフェイス ビルダーでウィンドウ デザインが開きます。

Xcode での UI の編集

[ライブラリ インスペクター] の [検索] ボックスに「」と入力tableして、テーブル ビュー コントロールを見つけやすくします。

ライブラリからテーブル ビューを選択する

テーブル ビューを インターフェイス エディターのビュー コントローラーにドラッグし、ビュー コントローラーのコンテンツ領域を埋め、 制約エディターのウィンドウで縮小および拡大する場所に設定します。

制約の編集

インターフェイス階層でテーブル ビューを選択すると、属性インスペクターで次のプロパティを使用できます。

[属性インスペクター] で使用できるプロパティを示すスクリーンショット。

  • コンテンツ モード - ビュー () またはセル (NSViewNSCell) を使用して、行と列にデータを表示できます。 macOS 10.7 以降では、Views を使用する必要があります。
  • Floats Group Rows - の場合 true、テーブル ビューでは、グループ化されたセルがフローティングであるかのように描画されます。
  • - 表示される列の数を定義します。
  • ヘッダー - の場合 true、列にはヘッダーが含まれます。
  • 並べ替え - の場合 true、ユーザーはテーブル内の列の並べ替えをドラッグできます。
  • サイズ変更 - の場合 true、ユーザーは列ヘッダーをドラッグして列のサイズを変更できます。
  • 列のサイズ設定 - テーブルで列の自動サイズを設定する方法を制御します。
  • 強調表示 - セルを選択したときに使用するテーブルの強調表示の種類を制御します。
  • 代替行 - の場合 true、他の行の背景色が異なります。
  • [水平グリッド ] - セル間に水平方向に描画される罫線の種類を選択します。
  • [垂直グリッド ] - セル間に垂直方向に描画される罫線の種類を選択します。
  • [グリッドの色] - セルの境界線の色を設定します。
  • 背景 - セルの背景色を設定します。
  • 選択 - ユーザーがテーブル内のセルを次のように選択する方法を制御できます。
    • 複数 - の場合 true、ユーザーは複数の行と列を選択できます。
    • - の場合 true、ユーザーは列を選択できます。
    • 「選択 」 - の場合 true、ユーザーは文字を入力して行を選択できます。
    • - の場合 true、ユーザーは行または列を選択する必要はありません。テーブルでは、まったく選択できません。
  • 自動保存 - テーブル形式が自動的に保存される名前。
  • 列情報 - の場合 true、列の順序と幅が自動的に保存されます。
  • [改行] - セルが改行を処理する方法を選択します。
  • 最後に表示される行を切り捨てる - の場合 true、セルはデータ内で切り捨てられ、境界内に収まりません。

重要

従来の Xamarin.Mac アプリケーションを維持している場合を除き、 NSView ベースのテーブル ビューに対してベースのテーブル ビューを NSCell 使用する必要があります。 NSCell はレガシと見なされ、今後サポートされない可能性があります。

インターフェイス階層でテーブル列を選択すると、属性インスペクターで次のプロパティを使用できます。

[属性インスペクター] の [テーブル列] で使用できるプロパティを示すスクリーンショット。

  • Title - 列のタイトルを設定します。
  • [配置 ] - セル内のテキストの配置を設定します。
  • タイトル フォント - セルのヘッダー テキストのフォントを選択します。
  • 並べ替えキー - 列のデータを並べ替えるために使用されるキーです。 ユーザーがこの列を並べ替えることができない場合は、空白のままにします。
  • セレクター - 並べ替えを実行するために使用される アクション です。 ユーザーがこの列を並べ替えることができない場合は、空白のままにします。
  • Order - 列データの並べ替え順序です。
  • [サイズ変更] - 列のサイズ変更の種類を選択します。
  • 編集可能 - の場合 true、ユーザーはセルベースのテーブル内のセルを編集できます。
  • 非表示 - の場合 true、列は非表示になります。

また、ハンドル (列の右側の垂直方向の中央) を左または右にドラッグして、列のサイズを変更することもできます。

テーブル ビューで各列を選択し、最初の列に Title と 2 番目DetailsProduct列を指定します。

インターフェイス階層でテーブル セル ビュー (NSTableViewCell) を選択すると、属性インスペクターで次のプロパティを使用できます。

[属性インスペクター] のテーブル セル ビューで使用できるプロパティを示すスクリーンショット。

これらはすべて標準ビューのプロパティです。 この列の行のサイズを変更することもできます。

[インターフェイス階層] で [テーブル ビュー] セル (既定では これが NSTextFieldです) を選択すると、属性インスペクターで次のプロパティを使用できます。

[属性インスペクター] の [テーブル ビュー] セルで使用できるプロパティを示すスクリーンショット。

ここで設定する標準テキスト フィールドのすべてのプロパティがあります。 既定では、標準のテキスト フィールドを使用して、列内のセルのデータを表示します。

インターフェイス階層でテーブル セル ビュー (NSTableFieldCell) を選択すると、属性インスペクターで次のプロパティを使用できます。

[属性インスペクター] の別の [テーブル ビュー] セルで使用できるプロパティを示すスクリーンショット。

ここで最も重要な設定は次のとおりです。

  • レイアウト - この列のセルのレイアウト方法を選択します。
  • 単一行モードを使用 する - の場合 true、セルは 1 行に制限されます。
  • First Runtime Layout Width - の場合 true、アプリケーションの初回実行時にセルが表示されるときに、セルに設定された幅 (手動または自動) が優先されます。
  • Action - セルに対して編集 アクション が送信されるタイミングを制御します。
  • 動作 - セルが選択可能か編集可能か定義します。
  • リッチ テキスト - の場合 true、セルには書式設定されたテキストとスタイル設定されたテキストを表示できます。
  • 元に戻す - の場合 true、セルは元に戻す動作の責任を負います。

インターフェイス階層のテーブル列の下部にあるテーブル セル ビュー (NSTableFieldCell) を選択します。

テーブル セル ビューの選択

これにより、特定の列に対して作成されたすべてのセルの基本 パターン として使用されるテーブル セル ビューを編集できます。

アクションとアウトレットの追加

他の Cocoa UI コントロールと同様に、(必要な機能に基づいて) ActionsOutlet を 使用して、テーブル ビューと列とセルを C# コードに公開する必要があります。

このプロセスは、公開する Table View 要素と同じです。

  1. アシスタント エディターに切り替え、ファイルがViewController.h選択されていることを確認します。

    アシスタント エディター

  2. インターフェイス階層からテーブル ビューを選択し、control キーを押しながらクリックしてファイルにViewController.hドラッグします。

  3. という名前ProductTableのテーブル ビューのアウトレットを作成します。

    ProductTable という名前のテーブル ビューに対して作成されたアウトレット接続を示すスクリーンショット。

  4. と 呼ばれるProductColumnDetailsColumnテーブル列のアウトレットを作成します。

    他のテーブル ビューに対して作成されたアウトレット接続を示すスクリーンショット。

  5. 変更を保存し、Visual Studio for Macに戻って Xcode と同期します。

次に、アプリケーションの実行時にテーブルのデータを表示するコードを記述します。

テーブル ビューの設定

Interface Builder で設計され 、Outlet 経由で公開されたテーブル ビューを使用して、次に C# コードを作成して設定する必要があります。

まず、個々の行の情報を保持する新しい Product クラスを作成しましょう。 ソリューション エクスプローラーで[プロジェクト]を右クリックし、[新しいファイル追加>]を選択します。[全般>空のクラス] を選択し、[名前] に「」と入力Productし、[新規作成] ボタンをクリックします。

空のクラスの作成

ファイルを Product.cs 次のようにします。

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

次に、 の NSTableDataSource サブクラスを作成して、要求されたとおりにテーブルのデータを提供する必要があります。 ソリューション エクスプローラーで[プロジェクト]を右クリックし、[新しいファイル追加>]を選択します。[全般>空のクラス] を選択し、[名前] に「」と入力ProductTableDataSourceし、[新規作成] ボタンをクリックします。

ファイルを ProductTableDataSource.cs 編集し、次のようにします。

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

このクラスは、テーブル ビューの項目のストレージを持ち、 を GetRowCount オーバーライドしてテーブル内の行数を返します。

最後に、 のサブクラス NSTableDelegate を作成して、テーブルの動作を提供する必要があります。 ソリューション エクスプローラーで[プロジェクト]を右クリックし、[新しいファイル追加>]を選択します。[全般>空のクラス] を選択し、[名前] に「」と入力ProductTableDelegateし、[新規作成] ボタンをクリックします。

ファイルを ProductTableDelegate.cs 編集し、次のようにします。

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

ProductTableDelegateインスタンスを作成するとき、テーブルのデータを提供する の ProductTableDataSource インスタンスも渡します。 メソッドは GetViewForItem 、give 列と行のセルを表示するビュー (データ) を返す役割を担います。 可能であれば、新しいビューを作成する必要がない場合は、既存のビューを再利用してセルを表示します。

テーブルにデータを入力するには、ファイルを ViewController.cs 編集し、 メソッドを AwakeFromNib 次のようにします。

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

アプリケーションを実行すると、次のように表示されます。

3 つのエントリを含む Product Table という名前のウィンドウを示すスクリーンショット。

列による並べ替え

列ヘッダーをクリックして、テーブル内のデータを並べ替えることをユーザーに許可しましょう。 まず、ファイルを Main.storyboard ダブルクリックして、インターフェイス ビルダーで編集用に開きます。 列を選択しProduct並べ替えキーに「」と入力Titleし、[ compare:セレクター] に「Order」を選択Ascendingします

[Product] 列の並べ替えキーを設定できるインターフェイス ビルダーを示すスクリーンショット。

列を選択しDetails並べ替えキーに「」と入力Descriptionし、[ compare:セレクター] に「Order」を選択Ascendingします

[詳細] 列の並べ替えキーを設定できるインターフェイス ビルダーを示すスクリーンショット。

変更を保存し、Visual Studio for Macに戻って Xcode と同期します。

次に、ファイルを ProductTableDataSource.cs 編集し、次のメソッドを追加します。

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

Sortメソッドを使用すると、指定Productされたクラス フィールドに基づいて、データ ソース内のデータを昇順または降順で並べ替えることができます。 オーバーライドされた SortDescriptorsChanged メソッドは、使用が列見出しをクリックするたびに呼び出されます。 インターフェイス ビルダーで設定した Key 値と、その列の並べ替え順序が渡されます。

アプリケーションを実行して [列ヘッダー] をクリックすると、行はその列で並べ替えられます。

アプリの実行例

行の選択

ユーザーが 1 つの行を選択できるようにする場合は、ファイルを Main.storyboard ダブルクリックして、インターフェイス ビルダーで編集用に開きます。 [インターフェイス階層] で [テーブル ビュー] を選択し、[属性インスペクター] の [複数] チェック ボックスをオフにします。

[属性インスペクター] で [複数] を選択できるインターフェイス ビルダーを示すスクリーンショット。

変更を保存し、Visual Studio for Macに戻って Xcode と同期します。

次に、ファイルを ProductTableDelegate.cs 編集し、次のメソッドを追加します。

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

これにより、ユーザーはテーブル ビュー内の任意の行を選択できます。 ShouldSelectRowユーザーが選択できないようにする行の を返します。ユーザーfalseが行を選択できないようにする場合は、すべての行に対して を返falseします。

テーブル ビュー (NSTableView) には、行の選択を操作するための次のメソッドが含まれています。

  • DeselectRow(nint) - テーブル内の指定された行の選択を解除します。
  • SelectRow(nint,bool) - 指定された行を選択します。 2 番目のパラメーターに を渡 false して、一度に 1 つの行のみを選択します。
  • SelectedRow - テーブルで選択されている現在の行を返します。
  • IsRowSelected(nint) - 指定された true 行が選択されている場合は を返します。

複数行選択

ユーザーが複数の行を選択できるようにする場合は、ファイルを Main.storyboard ダブルクリックして、インターフェイス ビルダーで編集用に開きます。 [インターフェイス階層] の [テーブル ビュー] を選択し、[属性インスペクター] の [複数] チェックボックスをチェックします。

複数行の選択を許可する [複数] を選択できるインターフェイス ビルダーを示すスクリーンショット。

変更を保存し、Visual Studio for Macに戻って Xcode と同期します。

次に、ファイルを ProductTableDelegate.cs 編集し、次のメソッドを追加します。

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

これにより、ユーザーはテーブル ビュー内の任意の行を選択できます。 ShouldSelectRowユーザーが選択できないようにする行の を返します。ユーザーfalseが行を選択できないようにする場合は、すべての行に対して を返falseします。

テーブル ビュー (NSTableView) には、行の選択を操作するための次のメソッドが含まれています。

  • DeselectAll(NSObject) - テーブル内のすべての行の選択を解除します。 選択を行うオブジェクトで送信する最初のパラメーターに を使用 this します。
  • DeselectRow(nint) - テーブル内の指定された行の選択を解除します。
  • SelectAll(NSobject) - テーブル内のすべての行を選択します。 選択を行うオブジェクトで送信する最初のパラメーターに を使用 this します。
  • SelectRow(nint,bool) - 指定された行を選択します。 2 番目のパラメーターに渡すと false 、選択範囲がクリアされ、1 つの行のみを選択し、 を渡 true して選択範囲を拡張し、この行を含めます。
  • SelectRows(NSIndexSet,bool) - 指定された行セットを選択します。 2 番目のパラメーターに 渡す false 選択範囲をクリアし、これらの行のみを選択し、 を渡 true して選択範囲を拡張し、これらの行を含めます。
  • SelectedRow - テーブルで選択されている現在の行を返します。
  • SelectedRows - 選択した行のインデックスを含む を返 NSIndexSet します。
  • SelectedRowCount - 選択した行の数を返します。
  • IsRowSelected(nint) - 指定された true 行が選択されている場合は を返します。

行を選択するタイプ

ユーザーが [テーブル ビュー] を選択して文字を入力し、その文字を含む最初の行を選択できるようにする場合は、ファイルを Main.storyboard ダブルクリックしてインターフェイス ビルダーで編集用に開きます。 インターフェイス階層のテーブル ビューを選択し、[属性インスペクター] の [種類の選択] チェック ボックスをチェックします。

選択の種類を設定する

変更を保存し、Visual Studio for Macに戻って Xcode と同期します。

次に、ファイルを ProductTableDelegate.cs 編集し、次のメソッドを追加します。

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

メソッドはGetNextTypeSelectMatch、指定searchStringされた を受け取り、その文字列Titleが の最初Productの行を返します。

アプリケーションを実行して文字を入力すると、行が選択されます。

アプリケーションを実行した結果を示すスクリーンショット。

列の並べ替え

ユーザーがテーブル ビューで列の並べ替えをドラッグできるようにする場合は、ファイルを Main.storyboard ダブルクリックして、インターフェイス ビルダーで編集用に開きます。 インターフェイス階層のテーブル ビューを選択し、属性インスペクターの [並べ替え] チェックボックスをチェックします。

[属性インスペクター] で [Reodering] を選択できるインターフェイス ビルダーを示すスクリーンショット。

[自動保存] プロパティの値を指定し、[列情報] フィールドをチェックすると、テーブルのレイアウトに加えた変更はすべて自動的に保存され、次回アプリケーションが実行されるときに復元されます。

変更を保存し、Visual Studio for Macに戻って Xcode と同期します。

次に、ファイルを ProductTableDelegate.cs 編集し、次のメソッドを追加します。

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

メソッドは ShouldReorder 、 にドラッグ並べ替えを許可する列に対して を返 true す必要があります。それ以外の場合は を newColumnIndex返します false

アプリケーションを実行する場合は、列ヘッダーをドラッグして列の順序を変更できます。

並べ替えた列の例

セルの編集

ユーザーが特定のセルの値を編集できるようにする場合は、ファイルを ProductTableDelegate.cs 編集し、次のように メソッドを GetViewForItem 変更します。

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

アプリケーションを実行すると、ユーザーはテーブル ビューのセルを編集できます。

セルを編集する例

テーブル ビューでのイメージの使用

にセル NSTableViewの一部として画像を含めるには、テーブル ビュー NSTableViewDelegate'sGetViewForItem の メソッドによってデータが返される方法を変更して、一般的な ではなく を NSTableCellView 使用する必要があります NSTextField。 次に例を示します。

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

詳細については、画像の操作に関するドキュメントの「テーブル ビューでのイメージの使用」セクションを参照してください。

行への削除ボタンの追加

アプリの要件に基づいて、テーブルの各行にアクション ボタンを指定する必要がある場合があります。 この例として、上で作成したテーブル ビューの例を展開して、各行に [削除] ボタンを含めます。

まず、Xcode のインターフェイス ビルダーで を Main.storyboard 編集し、テーブル ビューを選択し、列の数を 3 に増やします。 次に、新しい列の タイトル を に Action変更します。

列名の編集

ストーリーボードに変更を保存し、Visual Studio for Macに戻って変更を同期します。

次に、ファイルを ViewController.cs 編集し、次のパブリック メソッドを追加します。

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

同じファイルで、 メソッド内での新しいテーブル ビュー デリゲートの作成を ViewDidLoad 次のように変更します。

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

次に ProductTableDelegate.cs 、ビュー コントローラーへのプライベート接続を含め、デリゲートの新しいインスタンスを作成するときにコントローラーをパラメーターとして受け取るようにファイルを編集します。

#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

次に、次の新しいプライベート メソッドを クラスに追加します。

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

これにより、メソッドで GetViewForItem 以前に実行されていたすべてのテキスト ビュー構成が取得され、1 つの呼び出し可能な場所に配置されます (テーブルの最後の列にはテキスト ビューではなくボタンが含まれるため)。

最後に、 メソッドを GetViewForItem 編集し、次のようになります。

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

このコードのいくつかのセクションを詳しく見てみましょう。 最初に、新 NSTableViewCell しいアクションが作成されている場合は、 Column の名前に基づいてアクションが実行されます。 最初の 2 つの列 (ProductDetails) では、新しい ConfigureTextField メソッドが呼び出されます。

[アクション] 列では、新NSButtonしい が作成され、サブ ビューとしてセルに追加されます。

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

Button の Tag プロパティは、現在処理中の Row の番号を保持するために使用されます。 この番号は、ユーザーが Button 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 ();
    }
  });
};

イベント ハンドラーの開始時に、ボタンと、指定されたテーブル行にある製品を取得します。 その後、行の削除を確認するアラートがユーザーに表示されます。 ユーザーが行の削除を選択すると、指定された行がデータ ソースから削除され、テーブルが再読み込みされます。

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

最後に、テーブル ビュー セルが新規作成されるのではなく再利用されている場合、次のコードは、処理される列に基づいて構成します。

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

[アクション] 列では、 が見つかるまでNSButtonすべてのサブビューがスキャンされ、現在のTag行を指すプロパティが更新されます。

これらの変更を行うと、アプリを実行すると、各行に [削除 ] ボタンが表示されます。

削除ボタンを含むテーブル ビュー

ユーザーが [削除 ] ボタンをクリックすると、指定された行を削除するように求めるアラートが表示されます。

行の削除アラート

ユーザーが削除を選択すると、行が削除され、テーブルが再描画されます。

行が削除された後のテーブル

データ バインディング テーブル ビュー

Xamarin.Mac アプリケーションでKey-Valueコーディングとデータ バインディングの手法を使用すると、UI 要素を設定して操作するために記述および保守する必要があるコードの量を大幅に減らすことができます。 また、バッキング データ (データ モデル) をフロントエンド ユーザー インターフェイス (Model-View-Controller) からさらに切り離すことで、保守が容易になり、アプリケーション設計が柔軟になります。

Key-Valueコーディング (KVC) は、インスタンス変数またはアクセサー メソッド () を介してプロパティにアクセスするのではなく、キー (特別に書式設定された文字列) を使用してプロパティを識別して、オブジェクトのプロパティに間接的にアクセスするためのメカニズムですget/set。 Xamarin.Mac アプリケーションKey-Valueコーディングに準拠したアクセサーを実装することで、Key-Value Observing (KVO)、データ バインディング、コア データ、Cocoa バインディング、スクリプト機能などの他の macOS 機能にアクセスできます。

詳細については、データ バインディングとコーディングに関するドキュメントの 「テーブル ビュー のデータ バインディングセクションKey-Value 参照してください。

まとめ

この記事では、Xamarin.Mac アプリケーションでのテーブル ビューの操作について詳しく説明しました。 テーブル ビューのさまざまな種類と使用方法、Xcode のインターフェイス ビルダーでテーブル ビューを作成および管理する方法、および C# コードでテーブル ビューを操作する方法について説明しました。