Xamarin.MacのNSTableViewでNSTableViewSourceを使う

提供: MonoBook
ナビゲーションに移動 検索に移動

実装2:View Base[編集 | ソースを編集]

Xamarin.MacにはNSTableViewSourceというNSTableViewを簡単に扱うための便利クラスがあるようだ。

列となるデータを準備する[編集 | ソースを編集]

まずはソースの元(テーブルの1行)となるクラスを用意する。

    public class Study
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

行となるデータソースを準備する[編集 | ソースを編集]

次にデータソースを用意する。 NSTableViewSourceクラスを継承してGetRowCountメソッドとGetViewForItemメソッドをオーバーライドすることで利用可能な状態となる。 Cellベースでは「GetObjectValue」のところがViewベースでは「GetViewForItem」となっている。

この例ではNSTableColumnクラスのIdentifierプロパティの値と、前述のStudyクラスの各プロパティ名の簡易自動マッピングを行っている。NSTableColumnクラスのIdentifierプロパティはInterface Builderで設定した。

また、別途IList<>インターフェースを実装しておくとLINQで操作できたり色々便利だった。無くてもよい。

        public override NSView GetViewForItem(NSTableView tableView, NSTableColumn tableColumn, nint row)
        {
            if (tableColumn.Identifier == null)
            {
                return null;
            }

            var item = _items[(int)row];
            var type = typeof(Study);
            var prop = type.GetProperty(tableColumn.Identifier, BindingFlags.Public | BindingFlags.Instance);
            if (prop == null)
            {// そんな名前のプロパティはない
                return null;
            }

            var val = prop.GetValue(item);
            if (val == null)
            {// 空っぽですわ
                return null;
            }

            switch (tableColumn.Identifier)
            {

                // CellベースではNSStringを返せばよかったが、ViewベースではNSView派生クラスを返さねばならない。
                // NSTableCellViewを返すとCellベースと同じような使用感となる。
                // カスタムセルを使用する場合は分岐するとよい。
                default:
                    var cellView = (NSTableCellView)tableView.MakeView(tableColumn.Identifier, this);
                    cellView.TextField.StringValue = val.ToString();
                    return cellView;
            }

        }

あとはCellベースと同じ。

実装1:Cell Base[編集 | ソースを編集]

Xamarin.MacにはNSTableViewSourceというNSTableViewを簡単に扱うための便利クラスがあるようだ。

列となるデータを準備する[編集 | ソースを編集]

まずはソースの元(テーブルの1行)となるクラスを用意する。

    public class Study
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

行となるデータソースを準備する[編集 | ソースを編集]

次にデータソースを用意する。 NSTableViewSourceクラスを継承してGetRowCountメソッドとGetObjectValueメソッドをオーバーライドすることで利用可能な状態となる。

この例ではNSTableColumnクラスのIdentifierプロパティの値と、前述のStudyクラスの各プロパティ名でマッピングを行っている。 NSTableColumnクラスのIdentifierプロパティはInterface Builderで設定した。

また、別途IList<>インターフェースを実装しておくとLINQで操作できたり色々便利だった。無くてもよい。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Reflection;

    using Foundation;
    using AppKit;

    public class StudyTableViewSource : NSTableViewSource, IList<Study>
    {
        IList<Study> _items = new List<Study>();

        public override int GetRowCount(NSTableView tableView)
        {
            return _items.Count;
        }

        public override NSObject GetObjectValue(NSTableView tableView, NSTableColumn tableColumn, int row)
        {
            if (tableColumn.Identifier == null)
            {
                return null;
            }

            var item = _items[row];
            var type = typeof(Study);
            var prop = type.GetProperty(tableColumn.Identifier, BindingFlags.Public | BindingFlags.Instance);
            if (prop == null)
            {// そんな名前のプロパティはない
                return null;
            }

            var val = prop.GetValue(item);
            if (val == null)
            {// 空っぽですわ
                return null;
            }

            var ret = new NSString(val.ToString());
            return ret;
        }

        //  〜以下略〜
    }

使ってみる[編集 | ソースを編集]

早速使ってみる。

この例では

  • Interface BuilderでMainWindow.xibファイルを開き、
  • NSTableViewを貼り付け、
  • Content Modeを「Cell Based」に設定し
  • アウトレットは「_studyTableView」という名前にしておいた。

構造

  • NSTableView → この例では、_studyTableViewインスタンス
    • NSTableColumn → この例では、これのIdentityと、Studyクラスのプロパティが一致したものに文字列を代入している
    public partial class MainWindowController : NSWindowController
    {
        // 〜前略〜

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

            var studies = new StudyTableViewSource();
            studies.Add(new Study { Id = "A1", Name = "B1" });
            studies.Add(new Study { Id = "A2", Name = "B2" });
            studies.Add(new Study { Id = "A3", Name = "B3" });
            studies.Add(new Study { Id = "A4", Name = "B4" });
            _studyTableView.Source = studies;
        }
    }

なんと、この状態で実行するとテーブルの表示はされるが、クリックしても行選択ができない。

行選択できるようにする[編集 | ソースを編集]

これで良いのか知らんが以下で行選択が出来るようになった。

        public override void WindowDidLoad()
        {
            // 〜前略〜

            _studyTableView.Activated += (object sender, EventArgs e) => {
                var tableview = (NSTableView)sender;
                int clickedrow = (int)tableview.ClickedRow;
                tableview.SelectRow(clickedrow, false);
                tableview.ScrollRowToVisible(clickedrow);
            };
        }

セルを編集できるようにする[編集 | ソースを編集]

Interface Builderを開き、各カラムに「Editable」のチェックを入れる(最初から入っていると思う)。 設定対象はカラムでありセルじゃないよ(Identity Inspectorを開くとクラスがNSTableColumnとなってるやつ)。

多分こんな構造になっているはず

  • NSTableView
    • NSTableColumn ←これに「Editable」を設定する
      • NSTextFieldCell

次にデータソースの以下のShouldEditTableColumnメソッドをオーバーライドし「true」を返すようにする。 前述のEditableのチェックをしておくことでこのメソッドが呼ばれるようになり、このメソッドでtrueを返すと実際に編集が可能な状態となるようだ。

    public class StudyTableViewSource : NSTableViewSource, IList<Study>
    {
        // 〜〜〜省略〜〜〜

        public override bool ShouldEditTableColumn(NSTableView tableView, NSTableColumn tableColumn, int row)
        {
            return true;
        }
    }

上記のShouldEditTableColumnメソッドのみでは編集はできるが、編集終了後に値が反映されず、編集前の状態に戻ってしまう。 そこで以下のSetObjectValueメソッドもオーバーライドする。

    public class StudyTableViewSource : NSTableViewSource, IList<Study>
    {
        // 〜〜〜省略〜〜〜

         public override void SetObjectValue(NSTableView tableView, NSObject theObject, NSTableColumn tableColumn, int row)
        {
            var item = Items[row];
            var type = typeof(Study);
            var prop = type.GetProperty(tableColumn.Identifier, BindingFlags.Public | BindingFlags.Instance);
            if (prop == null)
            {
                return;
            }
            prop.SetValue(item, theObject.ToString());
        }
    }

セルのデザインを変える[編集 | ソースを編集]

セルのデザインをいじくるにはデータソースの以下のメソッドをオーバーライドしてその中でいじくりまわす。 セルは初期状態でDrawsBackgroundプロパティがfalseに設定されており背景描画が無効化されているようなので、trueに設定したのちに各種操作をしている。

    public class StudyTableViewSource : NSTableViewSource, IList<Study>
    {
        // 〜〜〜省略〜〜〜

        public override void WillDisplayCell(NSTableView tableView, NSObject cell, NSTableColumn tableColumn, int row)
        {
            // 編集不可カラムの背景色を少し暗くしてみる
            if (tableColumn.Editable == false)
            {
                if (cell is NSTextFieldCell)
                {
                    var textfield = cell as NSTextFieldCell;
                    textfield.DrawsBackground = true;
                    textfield.BackgroundColor = NSColor.FromSrgb(0.9f, 0.9f, 0.9f, 1.0f);
                }
            }
        }
    }

関連項目[編集 | ソースを編集]

参考文献[編集 | ソースを編集]