Xamarin.Mac の Table View
この記事では、Xamarin.Mac アプリケーションでのテーブル ビューの使用について説明しています。 Xcode と Interface Builder でテーブル ビューを作成し、コードを使用して操作する方法について説明します。
Xamarin.Mac アプリケーションで C# と .NET を使用している場合、Objective-C や Xcode で開発者が使用しているのと同じ Table View にアクセスできます。 Xamarin.Mac は直接 Xcode と統合できるため、Xcode の Interface Builder を使用して、Table View を作成および管理できます (または、必要に応じて、C# コードで直接作成することもできます)。
Table View では、複数の行に 1 列以上の情報が含まれている表形式でデータが表示されます。 作成中の Table View の種類に基づいて、ユーザーは、列の並べ替え、列の再構成、列の追加、列の削除、またはテーブルに含まれているデータの編集を行うことができます。
この記事では、Xamarin.Mac アプリケーションでの Table View の使用の基本について説明します。 まずは、Hello Mac に関する記事を参照することを強くお勧めします。特に、この記事で使用する主要な概念と手法について説明している「Xcode と Interface Builder の概要」セクションと「Outlet と Action」セクションを参照してください。
Xamarin.Mac Internals に関するドキュメントの「Objective-C への C# クラスとメソッドの公開」セクションの参照もお勧めします。そこでは、C# クラスを Objective-C オブジェクトと UI 要素に接続するために使用する Register
コマンドと Export
コマンドについて説明しています。
Table View の概要
Table View では、複数の行に 1 列以上の情報が含まれている表形式でデータが表示されます。 Table View は、Scroll View (NSScrollView
) 内に表示されます。また、macOS 10.7 以降では、ユーザーは、Cell (NSCell
) ではなく、任意の NSView
を使用して、行と列の両方を表示できます。 ただし、NSCell
は引き続き使用できますが、通常は、NSTableCellView
をサブクラス化して、カスタムの行と列を作成します。
Table View は、独自のデータを格納するのではなく、必要に応じて必要な行と列の両方を提供する Data Source (NSTableViewDataSource
) に依存しています。
Table View の動作をカスタマイズするには、テーブル列の管理、機能選択の種類、行の選択と編集、カスタム追跡、個々の列と行のカスタム ビューをサポートする Table View Delegate (NSTableViewDelegate
) のサブクラスを指定します。
Table View を作成する場合、Apple は次を提案しています。
- ユーザーが Column Header をクリックしてテーブルを並べ替えられるようにする。
- その列に表示されるデータを記述する名詞または短い名詞句である Column Header を作成する。
詳細については、Apple の OS X ヒューマン インターフェイス ガイドラインのコンテンツ ビューに関するセクションを参照してください。
Xcode での Table View の作成と管理
新しい Xamarin.Mac Cocoa アプリケーションを作成すると、既定で標準の空白のウィンドウが表示されます。 このウィンドウは、プロジェクトに自動的に含まれる .storyboard
ファイルで定義されます。 Windows デザインを編集するには、ソリューション エクスプローラーで、Main.storyboard
ファイルをダブルクリックします:
これにより、Xcode の Interface Builder でウィンドウ デザインが開きます:
Library Inspector の検索ボックスに「table
」と入力すると、Table View コントロールを見つけやすくなります。
Table View を Interface Editor の View Controller にドラッグし、View Controller のコンテンツ領域を塗りつぶし、それを Constraint Editor のウィンドウで縮小拡大する場所に設定します。
Interface Hierarchy で Table View を選択すると、Attribute Inspector で次のプロパティを使用できます。
- Content Mode - ビュー (
NSView
) またはセル (NSCell
) を使用して、行や列にデータを表示できます。 macOS 10.7 以降では、ビューを使用する必要があります。 - Floats Group Rows -
true
の場合、テーブル ビューでは、グループ化されたセルが浮かんでいるように描画されます。 - Columns - 表示する列の数を定義します。
- Headers -
true
の場合、列にはヘッダーが含まれます。 - Reordering -
true
の場合、ユーザーは、テーブル内の列をドラッグして並べ替えることができます。 - Resizing -
true
の場合、ユーザーは、列のヘッダーをドラッグして列のサイズを変更できます。 - Column Sizing - テーブルで列のサイズを自動的に設定する方法を制御します。
- Highlight - セルを選択したときにテーブルで使用する強調表示の種類を制御します。
- Alternate Rows -
true
の場合、他の行は別の背景色になります。 - Horizontal Grid - セルの間で横に引く罫線の種類を選択します。
- Vertical Grid - セルの間で縦に引く罫線の種類を選択します。
- Grid Color - セルの罫線の色を設定します。
- Background - セルの背景色を設定します。
- Selection - ユーザーによるテーブル内のセルの選択方法を制御できます。
- Multiple -
true
の場合、ユーザーは複数の行と列を選択できます。 - Column -
true
の場合、ユーザーは列を選択できます。 - Type Select -
true
の場合、ユーザーは文字を入力して行を選択できます。 - Empty -
true
の場合、ユーザーは、行や列を選択する必要はなく、テーブルでは選択を行うことができません。
- Multiple -
- Autosave - 自動保存されるときのテーブル形式の名前。
- Column Information -
true
の場合、列の順序と幅が自動保存されます。 - Line Breaks - セルでの改行の処理方法を選択します。
- Truncates Last Visible Line -
true
の場合、セルの境界内に収まらないデータは切り取られます。
重要
従来の Xamarin.Mac アプリケーションを管理している場合を除き、NSCell
ベースの Table View ではなく、NSView
ベースの Table View を使用する必要があります。 NSCell
はレガシと見なされており、今後サポートされなくなる可能性があります。
Interface Hierarchy で Table Column を選択すると、Attribute Inspector で次のプロパティを使用できます。
- Title - 列のタイトルを設定します。
- Alignment - セル内のテキストの配置を設定します。
- Title Font - セルのヘッダー テキストのフォントを選択します。
- Sort Key - 列内のデータの並べ替えに使用するキーです。 ユーザーがこの列を並べ替えることができない場合は、空白のままにします。
- Selector - 並べ替えの実行に使用する Action です。 ユーザーがこの列を並べ替えることができない場合は、空白のままにします。
- Order - 列のデータの並べ替え順序です。
- Resizing - 列のサイズ変更の種類を選択します。
- Editable -
true
の場合、ユーザーはセル ベース テーブル内のセルを編集できます。 - Hidden -
true
の場合、列は非表示になります。
また、ハンドルを左または右 (列の右側の垂直方向の中心) にドラッグすると列のサイズを変更できます。
テーブル ビューで各列を選択して、最初の列のタイトルとして Product
を指定し、2 番目の列に Details
を指定します。
Interface Hierarchy で Table Cell View (NSTableViewCell
) を選択すると、Attribute Inspector で次のプロパティを使用できます。
これらはすべて、標準ビューのプロパティです。 この列の行のサイズを変更することもできます。
Interface Hierarchy で Table Cell View (これは既定で NSTextField
です) を選択すると、Attribute Inspector で次のプロパティを使用できます。
ここで設定する標準テキスト フィールドのプロパティはすべて用意されています。 既定では、標準のテキスト フィールドを使用して、列内のセルのデータが表示されます。
Interface Hierarchy で Table Cell View (NSTableFieldCell
) を選択すると、Attribute Inspector で次のプロパティを使用できます。
ここで最も重要な設定は、次のとおりです。
- Layout - この列のセルのレイアウト方法を選択します。
- Uses Single Line Mode -
true
の場合、セルは 1 行に制限されます。 - First Runtime Layout Width -
true
の場合、アプリケーションの初回実行時に設定 (手動または自動的に) されているセルの幅を使用します。 - Action - セルに対して Edit Action を送信するタイミングを制御します。
- Behavior - セルが選択可能または編集可能かどうかを定義します。
- Rich Text -
true
の場合、セルには書式設定およびスタイル設定されたテキストが表示されます。 - Undo -
true
の場合、セルは元に戻す動作を担います。
Interface Hierarchy の Table Column の下部にある Table Cell View (NSTableFieldCell
) を選択します。
この操作を行うと、特定の列に対して作成されたすべてのセルの基本 Pattern として使用されている Table Cell View を編集できます。
Action と Outlet の追加
他の Cocoa UI コントロールと同様に、Action と Outlet を使用して (必要な機能に基づく)、Table View とその列とセルを C# コードに公開する必要があります。
このプロセスは、公開する Table View 要素でも同じです。
Assistant Editor に切り替えて、
ViewController.h
ファイルが選択されていることを確認します。Interface Hierarchy から Table View を選択し、Ctrl キーを押しながらクリックして
ViewController.h
ファイルにドラッグします。ProductTable
という名前の Table View の Outlet を作成します。ProductColumn
とDetailsColumn
という名前のテーブル列の Outlet を作成します。変更内容を保存し、Visual Studio for Mac に戻って Xcode と同期します。
次に、アプリケーションの実行時にテーブルのデータの一部を表示するコードを記述します。
Table View の設定
次に、Interface Builder で設計されており、Outlet 経由で公開されている Table View で、それを設定するための C# コードを作成する必要があります。
まずは、個々の行の情報を保持するために、新しい Product
クラスを作成しましょう。 ソリューション エクスプローラーでプロジェクトを右クリックし、[Add]>[New File...] を選択します[General]>[Empty Class] を選択し、[Name] で「Product
」と入力して、[New] ボタンをクリックします。
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
のサブクラスを作成して、テーブルのデータを要求どおりに指定する必要があります。 ソリューション エクスプローラーでプロジェクトを右クリックし、[Add]>[New File...] を選択します[General]>[Empty Class] を選択し、[Name] に「ProductTableDataSource
」と入力して、[New] ボタンをクリックします。
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
}
}
このクラスでは、Table View の項目用のストレージがあり、テーブル内の行数を返すように GetRowCount
がオーバーライドされます。
最後に、テーブルの動作を指定する NSTableDelegate
のサブクラスを作成する必要があります。 ソリューション エクスプローラーでプロジェクトを右クリックし、[Add]>[New File...] を選択します[General]>[Empty Class] を選択し、[Name] に「ProductTableDelegate
」と入力して、[New] ボタンをクリックします。
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);
}
アプリケーションを実行すると、次が表示されます。
Column ごとの並べ替え
ユーザーが Column Header をクリックしてテーブルを並べ替えられるようにしましょう。 まず、Main.storyboard
ファイルをダブルクリックして、Interface Builder で編集用に開きます。 Product
列を選択し、[Sort Key] に「Title
」、[Selector] に「compare:
」と入力して、[Order] で Ascending
を選択します。
Details
列を選択し、[Sort Key] に「Description
」、[Selector] に「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
メソッドが、列見出しをクリックするたびに呼び出されます。 Interface Builder で設定したキー値と、その列の並べ替え順序が渡されます。
アプリケーションを実行して列ヘッダーをクリックすると、行がその列ごとに並べ替えられます。
行の選択
ユーザーが 1 つの行を選択できるようにする場合は、Main.storyboard
ファイルをダブルクリックして、Interface Builder で編集用に開きます。 Interface Hierarchy で Table View を選択し、Attribute Inspector の [Multiple] チェックボックスのチェックを外します。
変更内容を保存し、Visual Studio for Mac に戻って Xcode と同期します。
次に、ProductTableDelegate.cs
ファイルを編集し、次のメソッドを追加します。
public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
return true;
}
これにより、ユーザーは Table view のあらゆる行を選択できるようになります。 ユーザーが行を選択できないようにする場合は、ユーザーが選択または false
に設定できないようにしたい行で、ShouldSelectRow
に対して false
を返します。
Table View (NSTableView
) には、行の選択を操作するための次のメソッドが含まれています。
DeselectRow(nint)
- テーブル内の特定の行の選択を解除します。SelectRow(nint,bool)
- 特定の行を選択します。 2 番目のfalse
パラメータを渡して、一度に 1 行のみ選択します。SelectedRow
- テーブル内で選択している現在の表を返します。IsRowSelected(nint)
- 特定の行が選択されている場合はtrue
を返します。
複数行の選択
ユーザーが複数の行を選択できるようにする場合は、Main.storyboard
ファイルをダブルクリックして、Interface Builder で編集用に開きます。 Interface Hierarchy で Table View を選択し、Attribute Inspector の [Multiple] チェックボックスにチェックを入れます。
変更内容を保存し、Visual Studio for Mac に戻って Xcode と同期します。
次に、ProductTableDelegate.cs
ファイルを編集し、次のメソッドを追加します。
public override bool ShouldSelectRow (NSTableView tableView, nint row)
{
return true;
}
これにより、ユーザーは Table view のあらゆる行を選択できるようになります。 ユーザーが行を選択できないようにする場合は、ユーザーが選択または false
に設定できないようにしたい行で、ShouldSelectRow
に対して false
を返します。
Table View (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
を返します。
入力による行の選択
選択した Table View でユーザーが文字を入力して、その文字が含まれている最初の行を選択できるようにする場合は、Main.storyboard
ファイルをダブルクリックして、Interface Builder で編集用に開きます。 Interface Hierarchy で Table View を選択し、Attribute Inspector の [Type Select] チェックボックスにチェックを入れます。
変更内容を保存し、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
の行を返します。
アプリケーションを実行して文字を入力すると、行が選択されます。
列の並べ替え
ユーザーが Table View で並べ替える列をドラッグできるようにする場合は、Main.storyboard
ファイルをダブルクリックして、Interface Builder で編集用に開きます。 Interface Hierarchy で Table View を選択し、Attribute Inspector の [Reordering] チェックボックスにチェックを入れます。
[Autosave] プロパティに値を指定して Column Information フィールドにチェックを入れている場合、テーブルのレイアウトに加えた変更は自動保存され、次回アプリケーションが実行されたときに復元されます。
変更内容を保存し、Visual Studio for Mac に戻って Xcode と同期します。
ここで、ProductTableDelegate.cs
ファイルを編集し、次のメソッドを追加しましょう。
public override bool ShouldReorder (NSTableView tableView, nint columnIndex, nint newColumnIndex)
{
return true;
}
ShouldReorder
メソッドでは、newColumnIndex
にドラッグして並べ替える必要がある列の true
を返す必要があります。それ以外の場合は、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;
}
アプリケーションを実行するとユーザーはテーブル ビューのセルを編集できます。
Table View での画像の使用
NSTableView
のセルの一部として画像を含めるには、テーブル ビューの NSTableViewDelegate's
GetViewForItem
メソッドによってデータが返される方法を変更して、一般的なNSTextField
ではなくNSTableCellView
を使用する必要があります。 次に例を示します。
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;
}
詳細については、画像の使用に関するドキュメントの Table View での画像の使用に関するセクションを参照してください。
Row への [Delete] ボタンの追加
アプリの要件に基づいて、テーブルの各行にアクション ボタンの設定が必要となる場合があります。 この例として、上で作成した Table View の例を展開して、各行に [Delete] ボタンを含めましょう。
まず、Xcode の Interface Builder で、Main.storyboard
を編集し、Table View を選択して、列の数を 3 つに増やします。 次に、新しい列の Title を Action
に変更します。
Storyboard への変更を保存し、Visual Studio for Mac に戻って変更を同期します。
次に、ViewController.cs
ファイルを編集し、次のパブリック メソッドを追加します。
public void ReloadTable ()
{
ProductTable.ReloadData ();
}
同じファイルで、次のように、ViewDidLoad
メソッド内で新しい Table View Delegate の作成を変更します。
// Populate the Product Table
ProductTable.DataSource = DataSource;
ProductTable.Delegate = new ProductTableDelegate (this, DataSource);
次に、ProductTableDelegate.cs
ファイルを編集し、View Controller へのプライベート接続を含めて、デリゲートの新しいインスタンスを作成するときにコントローラーをパラメータとして受け取ります。
#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
メソッドで以前に実行されていたすべての Text View 構成が取得されて、呼び出し可能な 1 か所に配置されます (テーブルの最後の列には Text View ではなく Button が含まれているため)。
最後に、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 つの列 (Product と Details) に対して、新しい ConfigureTextField
メソッドが呼び出されます。
Action 列では、新しい NSButton
が作成されて、Sub View として Cell に追加されます。
// 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
プロパティは、現在処理中の行の数を保持するために使用されます。 この数は、後で、ユーザーが 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 ();
}
});
};
イベント ハンドラーの開始時に、特定のテーブル行にあるボタンとプロダクトを取得します。 その後、行の削除を確認するアラートがユーザーに表示されます。 ユーザーが行の削除を選択すると、特定の行が Data Source から削除され、テーブルの再読み込みが行われます。
// Remove the given row from the dataset
DataSource.Products.RemoveAt((int)btn.Tag);
Controller.ReloadTable ();
最後に、Table View Cell を新規作成するのではなく再利用する場合は、処理中の Column に基づいて、次のコードによって構成されます。
// 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;
}
Action 列では、NSButton
が見つかるまで、すべての Sub View がスキャンされます。その後、その Tag
プロパティは、現在の Row を指すように更新されます。
これらの変更を行うと、アプリを実行したときに、各行に [Delete] ボタンが表示されます。
ユーザーが [Delete] ボタンをクリックすると、特定の Row を削除するように求めるアラートが表示されます。
ユーザーが削除を選択すると、行が削除され、テーブルが再作成されます。
Data Binding Table View
Xamarin.Mac アプリケーションで Key-Value Coding と Data Binding の手法を使用すると、UI 要素を設定して操作するために記述および管理が必要なコードの量を大幅に減らすことができます。 また、バッキング データ (データ モデル) をフロントエンド ユーザー インターフェイス (Model-View-Controller) からさらに切り離すことで、アプリケーション デザインを管理しやすくなるという利点もあります。
キーと値のコーディング (KVC) は、インスタンス変数やアクセサー メソッド (get/set
) を介してアクセスするのではなく、キー (特別にフォーマットされた文字列) を使用してプロパティを識別し、オブジェクトのプロパティに間接的にアクセスするためのメカニズムです。 Xamarin.Mac アプリケーションで Key-Value Coding に準拠したアクセサーを実装すると、Key-Value Observing (KVO)、Data Binding、Core Data、Cocoa バインディング、スクリプト機能など、他の macOS 機能にアクセスできます。
詳細については、Data Binding と Key-Value Coding に関するドキュメントの「テーブル ビューのデータ バインディング」セクションを参照してください。
まとめ
この記事では、Xamarin.Mac アプリケーションでの Table View の使用について詳しく説明しました。 Table View のさまざまな種類と使用方法、Xcode の Interface Builder で Table View を作成および管理する方法、C# コードで Table View を操作する方法について確認しました。