Listview GTK中的延迟加载列表视图#

Listview GTK中的延迟加载列表视图#,listview,lazy-loading,gtk#,Listview,Lazy Loading,Gtk#,我希望通过GTK中的列表视图显示大型数据集,而性能是这里的一个问题。我目前使用的是一个带有ListStore的TreeView,但是将我的所有数据添加到ListStore会花费很长时间。GTK中是否有某种列表视图小部件支持延迟加载数据?在Winforms中,您可以使用DataGridView的VirtualMode属性来处理此问题,但我没有看到GTK的任何类似情况。也许您可以使用不同的线程添加数据,以便当前应用程序不会“冻结”,而只是保持运行。它可能仍然需要相同的时间,但至少用户可以同时处理应用

我希望通过GTK中的列表视图显示大型数据集,而性能是这里的一个问题。我目前使用的是一个带有ListStore的TreeView,但是将我的所有数据添加到ListStore会花费很长时间。GTK中是否有某种列表视图小部件支持延迟加载数据?在Winforms中,您可以使用DataGridView的VirtualMode属性来处理此问题,但我没有看到GTK的任何类似情况。

也许您可以使用不同的线程添加数据,以便当前应用程序不会“冻结”,而只是保持运行。它可能仍然需要相同的时间,但至少用户可以同时处理应用程序的其余部分。

如果在模型连接到视图时将数据插入模型,或者在模型“脱机”时插入所有数据,并且仅在完成后将其连接到视图,则会产生巨大的差异。后者的速度要快得多,因为否则treeview必须随时重新生成其内部结构


如果插入不是主要问题,但从数据源中获取数据实际上是一个缓慢的过程,那么如果您能够快速检索模型的行数,则会有很大帮助。如果是这种情况,那么我建议首先创建一个liststore,其中所有行都分配了空内容,您可以将这些内容挂接到视图,然后从空闲回调或线程填充实际内容。不幸的是,没有批处理api来更新模型,因此可以一次更新几行。

据我所知,在Gtk中没有小部件可以执行您想要的操作,但是,您可以在最终结果中执行与TreeView中的VirtualMode属性类似的操作

TreeView控件的问题在于,它将提前从其模型中获取所有数据。如果不是这样的话,我会建议只使用模型的方法来解决这个问题,但不幸的是,TreeView在获取数据时非常贪婪,因此需要控制何时从视图加载数据,否则它将如何判断行何时可见,从而通知模型,或代理,以便在行变为可见时获取该行的数据

你需要三件事才能让它发挥作用

1) 要在树视图中使用的模型,它最初包含所有行,但在任何字段中都没有数据
2) 从您使用的任何数据库获取数据的方法
3) 一种确定为哪些行获取数据的方法

前两项可以在模型级别完成。确定要获取哪些行需要Treeview小部件以及确定要显示哪些行的方法。我在下面使用的方法不是最优的,但它确实有效,可以根据您的想法进行整理和/或调整

我使用一个代理类存储在模型中,用于获取特定于该行的数据。在我的示例中,它被称为ProxyClass。它获取并保存一行的数据,该行最初为空。在本例中,Fetch方法只创建并返回字符串“some data”+id

这将保存在MyNode的实例中,MyNode继承自TreeNode,表示一行数据。第一列返回代理中保存的数据,第二列(从未显示)保存代理类实例

然后创建NodeStore,即模型,用MyNode(id)的实例填充它,如下面的示例所示

从CellDataFunc控制加载数据的时间。这种方法是使其发挥作用的关键。CellDataFunc负责为传递给它的迭代器标识的行中的特定列设置CellRenderText中的文本。每次树视图显示一行时都会调用它,并且仅针对新显示的行调用它。因此,将仅获取在显示中呈现的单元格的数据。这为您提供了一种控制何时提取数据的方法,从而仅在需要时提取数据

通过使用TreeView Column.SetCellDataFunc将数据应用到其中一列,可以使TreeView使用CellDataFunc根据需要加载数据。您只需要对一列执行此操作,因为它可以获取整行的数据

要停止提取除可见行以外的所有行的数据,可以通过检查当前单元格是否在可见范围内来完成。为此,请调用TreeView.GetVisibleRange(out start,out end),然后查看传递给此函数的当前迭代器是否在start和end范围内,这两个范围都是树路径对象,因此需要先将它们更改为树路径。Model.GetIter(out iter\u start,start)。然后检查iter.UserData.ToInt32()>=iter\u start.UserData.ToInt32()是否小于iter\u end。如果当前的国际热核聚变实验堆落在从国际热核聚变实验堆开始到国际热核聚变实验堆结束的范围内,则获取数据,否则将其保留

这是我的例子

代理类

namespace LazyTree
{

    public class ProxyClass 
    {
      int id;
      string data;

      public ProxyClass (int id)
      {
        this.id = id;
        data = null;
      }


      public void Fetch()
      {
        data = "some data " + id;
      }


      public int Id
      {
        get { return id; }
      }

      public string Data
      {
        get {return data;}
      }
  }
}
自定义TreeNode实例

namespace LazyTree
{
    [Gtk.TreeNode (ListOnly=true)]
    public class MyNode : Gtk.TreeNode
    {
        protected ProxyClass proxy;

        public MyNode (int id)
        {
            proxy = new ProxyClass(id);
        }

        [Gtk.TreeNodeValue (Column=1)]
        public ProxyClass Proxy
        {
            get {return proxy;}
        }

        [Gtk.TreeNodeValue (Column=0)]
        public string Data
        {
            get { return proxy.Data; }
        }
    }
}
包括滚动窗口和树视图的窗口。这也是定义CellDataFunc的地方,尽管它可以放在任何地方

namespace LazyTree
{

    public class MyWindow : Gtk.Window
    {
        int NUMBER_COLUMNS = 10000;
        Gtk.NodeStore store;
        Gtk.NodeStore Store {
            get {
                if (store == null) {
                    store = new Gtk.NodeStore (typeof (MyNode));
                    for(int i = 0; i < NUMBER_COLUMNS; i++)
                    {
                        store.AddNode (new MyNode (i));
                    }
                }
                return store;
            }
        }


        protected void CellDataFunc(Gtk.TreeViewColumn column,
                                    Gtk.CellRenderer cell,
                                    Gtk.TreeModel model,
                                    Gtk.TreeIter iter)
        {
            try {
                string data = (string)model.GetValue(iter, 0);
                ProxyClass proxy = (ProxyClass)model.GetValue(iter, 1);
                Gtk.TreeView view = (Gtk.TreeView)column.TreeView;
                Gtk.TreePath start, end;
                bool go = view.GetVisibleRange(out start,out end);
                Gtk.TreeIter iter_start, iter_end;
                if(go)
                {
                    model.GetIter(out iter_start, start);
                    model.GetIter(out iter_end, end);
                }
                if (go &&
                    data == null && 
                    iter.UserData.ToInt32() >= iter_start.UserData.ToInt32() &&
                    iter.UserData.ToInt32() <= iter_end.UserData.ToInt32())
                {
                    Console.WriteLine("Lazy Loading " + proxy.Id + ", Visible: " + cell.Visible);
                    proxy.Fetch();
                }

                ((Gtk.CellRendererText)cell).Text = data;
            } catch(Exception e) {
                Console.WriteLine("error: " + e);
            }
        }


        public MyWindow () : base("Lazy Tree")
        {
            Gtk.NodeView view = new Gtk.NodeView(Store);

            Gtk.ScrolledWindow scroll = new Gtk.ScrolledWindow();
            scroll.Add(view);
            Add(scroll);
            Gtk.CellRendererText cell = new Gtk.CellRendererText ();
            view.AppendColumn ("Lazy Data", cell, "text", 0);

            Gtk.TreeViewColumn column = view.GetColumn(0);

            column.SetCellDataFunc(cell, CellDataFunc);
        }


        protected override bool OnDeleteEvent (Gdk.Event ev)
        {
            Gtk.Application.Quit ();
            return true;
        }

        public static void Main()
        {
            Gtk.Application.Init ();
                MyWindow win = new  MyWindow();
            win.SetDefaultSize(200, 200);
                    win.ShowAll ();
            Gtk.Application.Run ();
        }
    }


}
namespace LazyTree
{
公共类MyWindow:Gtk.Window
{
int NUMBER_列=10000;
Gtk.NodeStore商店;
Gtk.NodeStore商店{
得到{
if(store==null){
store=new Gtk.NodeStore(typeof(MyNode));
对于(int i=0;i