Gtk3アプリ TreeViewとデータ各種処理

### TreeView関連のデータ各種処理

#### TreeViewにデーターを入れる

```cs
using System;
using Gtk;
using UI = Gtk.Builder.ObjectAttribute;

private Gtk.ListStore testStore;

private void _mkData(){

 testModel testModel1 = new testModel();
 testModel1.aId = 1;
 testModel1.testStr = "aaaaa";

 testStore = new Gtk.ListStore (typeof (testModel));
 testStore.AppendValues (testModel);

 treeView1.Model = testStore;
}

public class testModel
{
    public long aId { get; set; }
    public string testStr { get; set; }
}

```

TreeViewの表示の仕方について詳しく書いてあります。

https://www.mono-project.com/docs/gui/gtksharp/widgets/treeview-tutorial/

#### データストアのデータを取り出す

データストアにインサートしたデータをForeachで取り出せます。

```cs
private Gtk.ListStore testStore;

testStore.Foreach (delegate (ITreeModel model, TreePath path, TreeIter iter)  {
	testModel testModel1 = model.GetValue(iter, 0) as testModel;	
	return false;
});

```

```cs
    static public List<T> _getData<T>(this Gtk.ListStore listStore)
    {
        List<T> objectArray = new List<T>();

        listStore.Foreach((model, path, iter) =>{
            T object1 = (T)model.GetValue(iter, 0);
            objectArray.Add(object1);
            return false;
        });
        return objectArray;
    }
```
#### CombBoxの選択したデータを取り出す

```cs
private void on_comboBox1_changed(object sender , EventArgs e){

	TreeIter tree;
	comboBox1.GetActiveIter(out tree);
	testModel testModel1 = (testModel)testStore.GetValue (tree, 0);
}
```

#### CombBoxの選択したindexを取得設定する
```cs
private void on_comboBox1_changed(object sender , EventArgs e){

   int selectedIndex = ((Gtk.ComboBox)sender).Active;
}

comboBox1.Active = 0;
```

#### TreeViewのRowを選択しデータを取り出す


GtkTreeSelectionを選択しシグナルを追加する
<img width="500" alt="Screenshot from 2021-08-21 20-13-00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182324/7f1548ed-176c-2c1f-b465-a02dac4e68e3.png">

AutoConnectした状態にする。
コールバック関数を追加し、GetSelectedを呼び出す

```cs
private void on_selection1_Changed(object sender , EventArgs e){		
	
	ITreeModel model;
	TreeIter iter;
	if (treeView1.Selection.GetSelected(out model,out iter))
	{
		testModel testModel1 = (testModel)testStore.GetValue(iter, 0);
	}

}
```

```cs
static public object _getSelectionSelectedRow<T>(this Gtk.TreeView treeView,Gtk.ListStore listStore)
{
        TreeIter iter;
        ITreeModel model;
        if (treeView.Selection.GetSelected(out model,out iter))
        {
            T modelData = (T)listStore.GetValue(iter, 0);
            return modelData;
        }
        return null;
}
```
#### チェックボタンからデータを取り出す

```cs
private Gtk.ListStore testStore;

void _mkToggle()
{

    Gtk.TreeViewColumn DelColumn = new Gtk.TreeViewColumn ();
    Gtk.CellRendererToggle CellRendererToggle = new Gtk.CellRendererToggle();
    DelColumn.Title = "削除";
    CellRendererToggle.Width = 100;
    DelColumn.PackStart(CellRendererToggle, true);
    
    //イベントハンドラ１
    CellRendererToggle.Toggled += delegate(object o, ToggledArgs args) {
        TreeIter iter;
        if(testStore.GetIterFromString(out iter, args.Path)) {
            testModel testModel1 = (testModel)testStore.GetValue(iter, 0);
        }
    };
    //イベントハンドラ2
    CellRendererToggle.Toggled += new ToggledHandler(testToggled);
}

private void testToggled(object o, ToggledArgs args) {
    TreeIter iter;
    if(testStore.GetIterFromString(out iter, args.Path)) {
	    testModel testModel1 = (testModel)testStore.GetValue(iter, 0);
    }
}
```

```csharp
static public object _getToggledRow<T>(this Gtk.ListStore listStore, ToggledArgs args)
{
   TreeIter iter;
   if (listStore.GetIterFromString(out iter, args.Path))
   {
        T modelObj = (T)listStore.GetValue(iter, 0);
        return modelObj;
   }
   return null;
}
```

#### TreeViewのCellを編集できるようにする

```cs
Gtk.TreeViewColumn artistColumn = new Gtk.TreeViewColumn ();
artistColumn.Title = "Artist";
artistColumn.Expand = true;
artistColumn.Sizing = TreeViewColumnSizing.Autosize;	    
Gtk.CellRendererText artistCellRendererText = new Gtk.CellRendererText ();
artistCellRendererText.Editable = true;
artistCellRendererText.Edited += new EditedHandler(TextCellEdited);

private void TextCellEdited (object o, EditedArgs args)
{
    TreePath treePath1 = new TreePath (args.Path);
    TreeIter iter;
    testStore.GetIter (out iter, treePath1);
    testModel testModel1 = (testModel)testStore.GetValue(iter, 0);
    Console.WriteLine(args.NewText);
}
```

#### Listoreのイベントからデータを取り出す
```cs
listStore1 = new Gtk.ListStore (typeof (testModel));
listStore1.RowChanged += delegate(object o, RowChangedArgs args)
{
    ListStore testStore = ((ListStore)o);
    TreeIter iter;
    if (testStore.GetIter(out iter, args.Path))
    {
        testModel testModel1 = (testModel)testStore.GetValue(iter, 0);
    }

};
```

#### Listoreのイベントからデータを取り出すExtension
```cs
   static public List<T> _getData<T>(this Gtk.ListStore list)
    {
        List<T> objectArray = new List<T>();
        list.Foreach((model, path, iter) =>{
            T object1 = (T)model.GetValue(iter, 0);
            objectArray.Add(object1);
            return false;
        });
        return objectArray;
    }
```

#### ListStoreのデータソートDescのやり方

##### やり方1

###### 先頭に一件だけ追加する。

```cs
  Gtk.TreeIter iterFirst;
  //先頭のIterを取得する
  musicListStore.GetIterFirst(out iterFirst);
  //先頭の前のIterを取得する
  Gtk.TreeIter iter = musicListStore.InsertBefore(iterFirst);
  Song Song1 = new Song ("test test", "testtest");
  //Liststoreにセットする
  musicListStore.SetValue(iter, 0, Song1);
``` 

###### 最後の行の後に一件だけ追加する

```cs
 Gtk.TreeIter iter = musicListStore.Append();
 Song Song1 = new Song ("Daft Punk", "Technologic");
 musicListStore.SetValue(iter, 0, Song1);
```

##### やり方2 
Gtk.TreeModelSortを使ったやり方　（動作させるにはひと工夫必要）
modelの値を指定してDescさせるやり方。

TreeModelSortというクラスを使うと順番を変更できる。

```cs
    //ソート やり方2
    //TreeModelSortを使用するやり方
    Gtk.TreeModelSort sortable = new Gtk.TreeModelSort(TestListStore);
            
    sortable.SetSortFunc(0, delegate(
        ITreeModel model, 
        TreeIter iter, 
        TreeIter treeIter){
        TestView s1 = (TestView)model.GetValue(iter, 0);
        TestView s2 = (TestView)model.GetValue(treeIter, 0);
        return DateTime.Compare(s1.Create_date, s2.Create_date);
    });
            
    treeView1.Model = sortable;
```

##### ソート やり方１について　

###### ListStore.Clearを使う時の注意点

ListStore.Clear実行すると以下のエラーが出ることがる。
ITreeModelに登録されたiterがTreeModelの中で消えていないため？
iterはデータや何行目を元にハッシュにしてパスとして使っている？
データの配列の順番を替えたりすると、treeView1.ModelにListStoreを入れるエラーが出ることがある。

##### Modelのiterの設定を事前にリセットする

TreeView.ModelとListStoreがつながった状態でListStoreのAppendValuesにデータを入れる瞬間に、TreeViewでトリガーで何か関数が走る。iterの設定がおかしいとここでエラーがでる。
Modelにnullを入れることでModelの中のiterの設定をリセットしておく


```
Gtk-CRITICAL **: gtk_list_store_iter_next: assertion
'priv->stamp == iter->stamp' failed

Gtk-CRITICAL **: gtk_tree_path_to_string: assertion
'path != NULL' failed

Gtk-CRITICAL **: gtk_list_store_get_value: assertion
'iter_is_valid (iter, list_store)' failed
```

###### ポイント
Nullを入れてやる。その後ListStorをClearする


```cs
treeView1.Model = null;
ListStore.Clear();
```

OK
gtk_tree_path_to_string系のエラーはModelにnullを入れたら消えた。

```cs
Gtk-CRITICAL **:gtk_tree_path_to_string: assertion 'path != NULL' failed
treeView1.Model = null;
```

データーをがちゃがちゃいじりすぎるとClear時にエラーが起きます。
データーは一件ずつ先頭や最後に入れていくのがよいです。
Clearの後にはModelにnullを入れておいたほうがよい。

追記
UIの更新で注意点
Webなどの非同期処理の中で一緒にUIを更新するとクラッシュすることがありました。
UIの更新は同期のしたほうが安定していて確実です。


https://athenajc.gitbooks.io/python-gtk-3-api/content/gtk-group/gtkliststore.html

[Gtk3アプリのTreeViewの表示の簡略化 Part2](https://qiita.com/iota_11/items/f9d9ebbfb0660357cefb)に続く