LoginSignup
1
0

More than 5 years have passed since last update.

[VS-Mac]VisualStudio for Mac (Cocoa)でDataTable(Qiita投稿一覧)をTableViewで表示

Posted at

 Xamarin - Developers - Guides - Mac - UserInterface - TableViews Using Table Views in a Xamarin.Mac applicationを参考にして、QiitaAPI GET /api/v2/itemsで投稿一覧を取得したDataTableをNSTableで表示してみた。

 なお、C#でJSONも扱えるようですが、うまく取得できなかったので正規表現で取得しています。

API

NSTableView

概要

  • 「GET /api/v2/items」で読み込んだQiita最新記事リストのデータテーブルをViewBasedのNSTableViewに表示します。
  • ArrayControllerは利用していません。
  • リスト取得後書類フォルダのsqliteデータベース(Qiita.db)にも保存します。
  • 2回目以降の起動時は、sqliteを読み込みます。
  • ファイルメニューの「New(Read API)」で最新記事を再読み込みします。

制限事項

匿名アクセスのため、時間60回以上Updateするとエラーになるかもしれません。

ソリューションの新規作成

省略

参照の追加

  • System.Data
  • Mono.Data.Sqlite

Classの追加とコード編集

  1. フォルダ「MyDatabase」を追加(右クリック新しいフォルダ)
  2. フォルダに新しいファイルを追加 「Qiita.cs」
  3. ArticleTableDataSource.cs追加
  4. ArticleTableDelegate.cs追加
  5. ViewController.csを編集

InterfaceBuilder

  1. ViewControllerにNSTableViewを追加
  2. Childの「ClipView」を展開して「TableView」を選択

    • AttributesInspectorのColumnsを「4」に変更Table05.png 画像は9だが4で列を作成
    • BorderedScrollViewを選択してSizeInspectorで、View-Arrangeのコンボボックスで「FillContainerHorizontal」と「FillContainerVertical」をクリックして最大化
    • AddNewConstraintsをクリックして、上下左右の赤いmarginIをアクティブにして「Add4Constraints」ボタンをクリックTable06.png
  3. さらに展開して、TableColumnを一つずつ選択して、AttributesInspectorのTitleをTitle,User,Created,Tagsにそれぞれ変更していく。

  4. AssistantEditorにViewController.hを表示してOutletを追加

    1. TableView: Connection:Outlet Name:ArticleTable
    2. 各TableColumn Connection:Outlet Name:Column[各Title]
  5. Menuの編集

    1. MenuのFile - Newを選択
    2. AttributesInspectorでTitleを「New(Read API)」に変更
    3. 選択したNewメニューをCtrlキーを押してFirstResponderにドラッグ
    4. Action選択の黒のポップアップが出現する
    5. ViewController.csに追加した「readApi:」を選択 Table11.png
    6. Save

実行画面

Table10.png

コード

Qiita.cs

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Data;
using Mono.Data.Sqlite;

namespace MyDatabase
{

    public class Qiita
    {
        private string _sqlitefile = Environment.GetFolderPath(Environment.SpecialFolder.Personal)
                                                + "/Documents/Qiita.db";
        private SqliteConnection _conn;
        private DataTable _tbl;
        private DataView _dv;
        public DataView Dv { get { return _dv; } }

        public Qiita()
        {
            CreateTable();
            if (!File.Exists(_sqlitefile))
                CreateSqlite();
            else
                LoadSqlite();
        }
        public int GetList(int page_count, int perpage)
        {
            int count = 0;
            if (perpage > 100)
                perpage = 100;
            using (WebClient wc = new WebClient())
            {
                int page = 1;
                do
                {
                    string url = $"https://qiita.com/api/v2/items?page={page}&per_page={perpage}";
                    Console.WriteLine(url);
                    wc.Encoding = Encoding.UTF8;
                    string source = wc.DownloadString(url);
                    int count1 = ReadJson(source);
                    if (count1 == 0)
                        break;
                    count += count1;
                    page++;
                } while (page <= Math.Min(page_count, 60));
            }
            if(count > 0)
                UpdateSqlite();

            return count;
        }
        private int ReadJson(string json)
        {
            int count = 0;
            string pattern = "\"rendered_body\".*?\"body\":\"(?<body>.*?)\".*?\"created_at\":\"(?<create>.*?)\"" +
                ".*?\"group\":(?<group>.*?),.*?\"id\":\"(?<id>.*?)\".*?\"tags\":\\[(?<tags>.*?)}\\]" +
                ".*?\"title\":\"(?<title>.*?)\".*?\"updated_at\":\"(?<update>.*?)\".*?\"url\":\"(?<url>.*?)\".*?\"id\":\"(?<userid>.*?)\"" +
                ".*?\"website_url";
            MatchCollection mc = Regex.Matches(json, pattern);
            Console.WriteLine(mc.Count);
            if (mc.Count > 0)
            {
                Dv.Sort = "id";
                foreach (Match m in mc)
                {
                    int idx = _dv.Find(m.Groups["id"].Value);
                    if (idx < 0)
                    {
                        DataRowView r = _dv.AddNew();
                        r.BeginEdit();
                        r["id"] = m.Groups["id"].Value;
                        r["title"] = m.Groups["title"].Value;
                        r["url"] = m.Groups["url"].Value;
                        r["user"] = m.Groups["userid"].Value;
                        r["body"] = m.Groups["body"].Value;
                        r["groups"] = m.Groups["group"].Value;
                        string pattern_tag = "\"name\":\"(?<name>.*?)\"";
                        MatchCollection mc_tag = Regex.Matches(m.Groups["tags"].Value, pattern_tag);
                        StringBuilder sb = new StringBuilder();
                        foreach (Match m2 in mc_tag)
                            sb.AppendFormat(" {0}", m2.Groups["name"].Value);
                        string tag = sb.ToString().Trim();
                        r["tags"] = tag;
                        string[] ss = m.Groups["create"].Value.Replace("T", " ").Split('+');
                        //r["created"] = DateTime.Parse(ss[0]).Add(TimeSpan.Parse(ss[1]));
                        r["created"] = DateTime.Parse(ss[0]);
                        ss = m.Groups["update"].Value.Replace("T", " ").Split('+');
                        r["updated"] = DateTime.Parse(ss[0]);
                        r.EndEdit();
                        count++;
                    }
                }
                Dv.Sort = "created desc";
            }
            else
            {
                Console.WriteLine("Error");
                System.Diagnostics.Debug.Print(json);
            }
            for (int i = 0; i < _tbl.Rows.Count; i++)
            {
                Console.WriteLine(_tbl.Rows[i]["title"].ToString());

                for (int j = 0; j < 7; j++)
                    System.Diagnostics.Debug.Write(_tbl.Rows[i][j].ToString() + "\t");
                System.Diagnostics.Debug.WriteLine(_tbl.Rows[i][8].ToString());

            }
            return count;
        }
        private void UpdateSqlite()
        {
            if (_conn == null)
                _conn = new SqliteConnection($"Data Source={_sqlitefile}");
            _conn.Open();
            string field_list = "@id, @title, @url, @user, @created, @updated, @tags, @body, @groups";
            string query = $"replace into qiita values ({field_list})";
            using (SqliteCommand command = new SqliteCommand(query, _conn))
            {
                string[] fields = field_list.Replace("@", "").Replace(" ", "").Split(',');
                for (int i = 0; i < fields.Length; i++)
                    command.Parameters.Add(fields[i], DbType.String);
                for (int i = 0; i < _tbl.Rows.Count; i++)
                {
                    for (int j = 0; j < 9; j++)
                        command.Parameters[fields[j]].Value = _tbl.Rows[i][j].ToString();
                    command.ExecuteNonQuery();
                }
            }
            _conn.Close();
        }
        private void CreateTable()
        {
            _tbl = new DataTable();
            _tbl.Columns.Add("id", typeof(string));
            _tbl.Columns.Add("title", typeof(string));
            _tbl.Columns.Add("url", typeof(string));
            _tbl.Columns.Add("user", typeof(string));
            _tbl.Columns.Add("created", typeof(DateTime));
            _tbl.Columns.Add("updated", typeof(DateTime));
            _tbl.Columns.Add("tags", typeof(string));
            _tbl.Columns.Add("body", typeof(string));
            _tbl.Columns.Add("groups", typeof(string));
            _dv = new DataView(_tbl, "", "created desc", DataViewRowState.CurrentRows);
        }

        private void LoadSqlite()
        {
            if (_conn == null)
                _conn = new SqliteConnection($"Data Source={_sqlitefile}");
            Console.WriteLine(_conn.DataSource);
            _conn.Open();
            string query = "select * from qiita";
            using(SqliteCommand command = new SqliteCommand(query, _conn)){
                using(SqliteDataReader reader = command.ExecuteReader()){
                    while(reader.Read())
                    {
                        DataRow r = _tbl.NewRow();
                        for (int i = 0; i < 9;i++)
                            r[i] = (string)reader[i];
                        _tbl.Rows.Add(r);
                    }
                }
            }
            _conn.Close();
        }
        private void CreateSqlite()
        {
            SqliteConnection.CreateFile(_sqlitefile);
            if (_conn == null)
                _conn = new SqliteConnection($"Data Source={_sqlitefile}");
            Console.WriteLine(_conn.DataSource);
            _conn.Open();
            string query = "create table qiita (id text primary key, title text, url text, user text" +
                ", created text, updated text, tags text, body text, groups text)";
            using (SqliteCommand command = new SqliteCommand(query, _conn))
            {
                command.ExecuteNonQuery();
            }
            _conn.Close();
            GetList(1, 100);
        }
    }
}

ArticleTableDataSource.cs

using System;
using AppKit;
using Foundation;
using System.Data;

namespace MyDatabase
{
    public class ArticleTableDataSource : NSTableViewDataSource
    {
        public DataView Dv { get; } = null;
        private Qiita _qiita;
        public ArticleTableDataSource()
        {
            _qiita = new Qiita();
            Dv = _qiita.Dv;
        }

        public int Update(int page, int per_page)
        {
            return _qiita.GetList(page, per_page);
        }
        public override nint GetRowCount(NSTableView tableView)
        {
            return Dv.Count;
        }
    }
}

ArticleTableDelegate.cs

using System;
using AppKit;
using Foundation;

namespace MyDatabase
{
    public class ArticleTableDelegate : NSTableViewDelegate
    {
        private const string CellIdentifier = "ArticleCell";
        private ArticleTableDataSource DataSource;

        public ArticleTableDelegate(ArticleTableDataSource datasource)
        {
            this.DataSource = datasource;
        }
        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
            string dbColname = tableColumn.Title.ToLower();
            if (DataSource.Dv[(int)row][dbColname] != DBNull.Value)
            {
                if (dbColname == "created" | dbColname == "updated")
                    view.StringValue = ((DateTime)DataSource.Dv[(int)row][dbColname]).ToString();
                else
                    view.StringValue = (string)DataSource.Dv[(int)row][dbColname];
            }
            else
                view.StringValue = "";
            return view;
        }
    }

}

ViewController.cs

using System;

using AppKit;
using Foundation;

namespace MyTutorial_TableQiita
{
    public partial class ViewController : NSViewController
    {
        private MyDatabase.ArticleTableDataSource dataSource;
        public ViewController(IntPtr handle) : base(handle)
        {
        }

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

            dataSource = new MyDatabase.ArticleTableDataSource();
            ArticleTable.DataSource = dataSource;
            ArticleTable.Delegate = new MyDatabase.ArticleTableDelegate(dataSource);
        }
        public override void ViewWillAppear()
        {
            base.ViewWillAppear();
            this.View.Window.Title = "MyTutorial NSTableView Qiita List";
        }
        public override void ViewWillDisappear()
        {
            base.ViewWillDisappear();
            //Windowを閉じる時にアプリも終了
            NSApplication.SharedApplication.Terminate(Self);
        }
        [Action("readApi:")]
        public void readApi(NSObject sender)
        {
            Console.WriteLine("Request to define readApi");
            var alert = new NSAlert()
            {
                AlertStyle = NSAlertStyle.Informational,
                InformativeText = "最新投稿リストを取得します",
                MessageText = "Qiita",
            };
            alert.AddButton("Ok");
            alert.AddButton("Cancel");
            nint res = 0;
            alert.BeginSheetForResponse(null, (result) =>
            {
                Console.WriteLine("Alert Result: {0}", result);
                res = result;
            });
            if (res == 1000)
            {
                int count = dataSource.Update(1, 50);    //1:page_count  2:per_page
                ArticleTable.ReloadData();
                var alert2 = new NSAlert()
                {
                    AlertStyle = NSAlertStyle.Informational,
                    MessageText = $"{count} タイトル取得しました。",
                };
                alert2.RunModal();
            }

        }

        public override NSObject RepresentedObject
        {
            get
            {
                return base.RepresentedObject;
            }
            set
            {
                base.RepresentedObject = value;
                // Update the view, if already loaded.
            }
        }
    }
}

Reference:

  1. Xamarin - Developers - Guides - Mac - UserInterface - TableViews Using Table Views in a Xamarin.Mac application
  2. Xamarin - Developers - Guides - Mac - UserInterface - Menus
  3. Xamarin - Developers - Guides - Mac - UserInterface - Alerts
  4. QiitaAPI GET /api/v2/items

GUに関する操作方法ならびに関連コードの著作権はXamarinに帰属します。
また、「ArticleTableDataSource.cs」はXamarinの「ProductTableDataSource.cs」を、「ArticleTableDelegate.cs」は「ProductTableDelegate.cs」を参考にデータベース用に改変したものです。

License:

Copyright (c) 2017 grayhead0603
Released under the MIT license

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0