如何设置 Gtk# TreeModelFilter 来过滤底层 TreeStore?

发布于 2024-09-15 18:20:46 字数 5020 浏览 7 评论 0原文

我已阅读 "GtkSharp TreeView 教程",其中作者描述了如何设置和使用 TreeModelFilter对于底层 ListStore(在教程“过滤数据”部分下)。该技术似乎不适用于底层分层 TreeStore。我想过滤多级 TreeStore 并在 TreeView 中显示结果。这让我很难过。有任何教程、示例或建议吗?

以下是代码。它与教程的代码基本相同,除了处理 TreeStore 而不是 ListStore 的构造和填充的更改之外。 {TreeStore 用于保存联系人的“姓名”和“电子邮件地址”,分为(并保存为)根“朋友”和“亲戚”的子级}

// compilation requires references to:
// gtk-sharp, atk-sharp and glib-sharp


using System;
using Gtk;

public class TreeViewExample
{
    public static void Main()
    {
        Gtk.Application.Init();
        new TreeViewExample();
        Gtk.Application.Run();
    }

    Gtk.Entry filterEntry;
    Gtk.TreeModelFilter filter;

    public TreeViewExample()
    {
        // Create a Window
        Gtk.Window window = new Gtk.Window("TreeView Example");
        window.SetSizeRequest(500, 200);
        window.DeleteEvent += delegate { Application.Quit(); };

        // Create an Entry used to filter the tree
        filterEntry = new Gtk.Entry();

        // Fire off an event when the text in the Entry changes
        filterEntry.Changed += OnFilterEntryTextChanged;

        // Create a nice label describing the Entry
        Gtk.Label filterLabel = new Gtk.Label("Search:");

        // Put them both into a little box so they show up side by side
        Gtk.HBox filterBox = new Gtk.HBox();
        filterBox.PackStart(filterLabel, false, false, 5);
        filterBox.PackStart(filterEntry, true, true, 5);

        // Create our TreeView
        Gtk.TreeView tv = new Gtk.TreeView();

        // Create a box to hold the Entry and Tree
        Gtk.VBox box = new Gtk.VBox();

        // Add the widgets to the box
        box.PackStart(filterBox, false, false, 5);
        box.PackStart(tv, true, true, 5);
        window.Add(box);

        //setting up columns and renderers
        Gtk.TreeViewColumn nameColumn = new Gtk.TreeViewColumn { Title = "Name" };
        Gtk.CellRendererText nameCell = new Gtk.CellRendererText();
        nameColumn.PackStart(nameCell, true);
        Gtk.TreeViewColumn emailColumn = new Gtk.TreeViewColumn { Title = "Email" };
        Gtk.CellRendererText emailCell = new Gtk.CellRendererText();
        emailColumn.PackStart(emailCell, true);

        // Add the columns to the TreeView
        tv.AppendColumn(nameColumn);
        tv.AppendColumn(emailColumn);

        // Tell the Cell Renderers which items in the model to display
        nameColumn.AddAttribute(nameCell, "text", 0);
        emailColumn.AddAttribute(emailCell, "text", 1);

        // Create a model that will hold two strings 
        Gtk.TreeStore contacts = new Gtk.TreeStore(typeof(string), typeof(string));

        // Add some hierarchical data
        Gtk.TreeIter treeiter;

        //first root
        treeiter = contacts.AppendValues("FRIENDS");

        // 2 children of first root
        contacts.AppendValues(treeiter, "Ogre", "[email protected]");
        contacts.AppendValues(treeiter, "Bee", "[email protected]");

        // second root
        treeiter = contacts.AppendValues("RELATIVES");

        // 3 children of second root
        contacts.AppendValues(treeiter, "Mommy", "[email protected]");
        contacts.AppendValues(treeiter, "Daddy", "[email protected]");
        contacts.AppendValues(treeiter, "tom", "[email protected]");

        filter = new Gtk.TreeModelFilter(contacts, null);
        // Specify the function that determines which rows to filter out and which ones to display
        filter.VisibleFunc = new Gtk.TreeModelFilterVisibleFunc(FilterTree);

        // Assign the filter as our treeview's model
        tv.Model = filter;

        // Show the window and everything on it
        window.ShowAll();
    }

    private void OnFilterEntryTextChanged(object o, System.EventArgs args)
    {
        // Since the filter text changed, tell the filter to re-determine which rows to display
        filter.Refilter();
    }

    private bool FilterTree(Gtk.TreeModel model, Gtk.TreeIter iter)
    {
        string contactname = model.GetValue(iter, 0).ToString();
        if (filterEntry.Text == "")
            return true;
        if (contactname.IndexOf(filterEntry.Text) > -1)
            return true;
        else
            return false;
    }
}

[我正在使用 mono 2.6.4 /monodevelop 2.4 / gtk -windows vista 上的锐利 2.12。]

I have read the "GtkSharp TreeView tutorial" wherein the author describes how to setup and use a TreeModelFilter for an underlying ListStore ( under the tutorial section "Filtering Data"). The technique doesn't seem to work for an underlying hierarchical TreeStore. I want to filter a multilevel TreeStore and show the results in a TreeView. Its giving me a real hard time. Are there any tutorials, samples, or suggestions for doing it ?

Following is the code. Its basically the same code as the tutorial except for changes to deal with construction and population of a TreeStore rather than a ListStore.
{The TreeStore is used to save "names" and "email addresses" of contacts , divided into (and saved as) children of the roots "friends" and "relatives" }

// compilation requires references to:
// gtk-sharp, atk-sharp and glib-sharp


using System;
using Gtk;

public class TreeViewExample
{
    public static void Main()
    {
        Gtk.Application.Init();
        new TreeViewExample();
        Gtk.Application.Run();
    }

    Gtk.Entry filterEntry;
    Gtk.TreeModelFilter filter;

    public TreeViewExample()
    {
        // Create a Window
        Gtk.Window window = new Gtk.Window("TreeView Example");
        window.SetSizeRequest(500, 200);
        window.DeleteEvent += delegate { Application.Quit(); };

        // Create an Entry used to filter the tree
        filterEntry = new Gtk.Entry();

        // Fire off an event when the text in the Entry changes
        filterEntry.Changed += OnFilterEntryTextChanged;

        // Create a nice label describing the Entry
        Gtk.Label filterLabel = new Gtk.Label("Search:");

        // Put them both into a little box so they show up side by side
        Gtk.HBox filterBox = new Gtk.HBox();
        filterBox.PackStart(filterLabel, false, false, 5);
        filterBox.PackStart(filterEntry, true, true, 5);

        // Create our TreeView
        Gtk.TreeView tv = new Gtk.TreeView();

        // Create a box to hold the Entry and Tree
        Gtk.VBox box = new Gtk.VBox();

        // Add the widgets to the box
        box.PackStart(filterBox, false, false, 5);
        box.PackStart(tv, true, true, 5);
        window.Add(box);

        //setting up columns and renderers
        Gtk.TreeViewColumn nameColumn = new Gtk.TreeViewColumn { Title = "Name" };
        Gtk.CellRendererText nameCell = new Gtk.CellRendererText();
        nameColumn.PackStart(nameCell, true);
        Gtk.TreeViewColumn emailColumn = new Gtk.TreeViewColumn { Title = "Email" };
        Gtk.CellRendererText emailCell = new Gtk.CellRendererText();
        emailColumn.PackStart(emailCell, true);

        // Add the columns to the TreeView
        tv.AppendColumn(nameColumn);
        tv.AppendColumn(emailColumn);

        // Tell the Cell Renderers which items in the model to display
        nameColumn.AddAttribute(nameCell, "text", 0);
        emailColumn.AddAttribute(emailCell, "text", 1);

        // Create a model that will hold two strings 
        Gtk.TreeStore contacts = new Gtk.TreeStore(typeof(string), typeof(string));

        // Add some hierarchical data
        Gtk.TreeIter treeiter;

        //first root
        treeiter = contacts.AppendValues("FRIENDS");

        // 2 children of first root
        contacts.AppendValues(treeiter, "Ogre", "[email protected]");
        contacts.AppendValues(treeiter, "Bee", "[email protected]");

        // second root
        treeiter = contacts.AppendValues("RELATIVES");

        // 3 children of second root
        contacts.AppendValues(treeiter, "Mommy", "[email protected]");
        contacts.AppendValues(treeiter, "Daddy", "[email protected]");
        contacts.AppendValues(treeiter, "tom", "[email protected]");

        filter = new Gtk.TreeModelFilter(contacts, null);
        // Specify the function that determines which rows to filter out and which ones to display
        filter.VisibleFunc = new Gtk.TreeModelFilterVisibleFunc(FilterTree);

        // Assign the filter as our treeview's model
        tv.Model = filter;

        // Show the window and everything on it
        window.ShowAll();
    }

    private void OnFilterEntryTextChanged(object o, System.EventArgs args)
    {
        // Since the filter text changed, tell the filter to re-determine which rows to display
        filter.Refilter();
    }

    private bool FilterTree(Gtk.TreeModel model, Gtk.TreeIter iter)
    {
        string contactname = model.GetValue(iter, 0).ToString();
        if (filterEntry.Text == "")
            return true;
        if (contactname.IndexOf(filterEntry.Text) > -1)
            return true;
        else
            return false;
    }
}

[I am using mono 2.6.4 /monodevelop 2.4 / gtk-sharp 2.12 on windows vista.]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

愿得七秒忆 2024-09-22 18:20:46

似乎在过滤树模型中的行时,只有当行的所有父行也可见时,该行才可见。由于您的过滤函数隐藏了父节点,因此即使文本匹配,它也不会显示子节点。我修改了您的代码来说明这个问题:

现在,父节点之一以“test”开头。如果您输入“test”,您将看到过滤工作正常。

using System;
using Gtk;

public class TreeViewExample
{

public static void Main ()

{

    Gtk.Application.Init ();

    new TreeViewExample ();

    Gtk.Application.Run ();

}


Gtk.Entry filterEntry;
Gtk.TreeModelFilter filter;



public TreeViewExample ()
{

    // Create a Window

    Gtk.Window window = new Gtk.Window ("TreeView Example");

    window.SetSizeRequest (500,200);

    window.DeleteEvent+=delegate {Application.Quit();};



    // Create an Entry used to filter the tree

    filterEntry = new Gtk.Entry ();



    // Fire off an event when the text in the Entry changes

    filterEntry.Changed += OnFilterEntryTextChanged;



    // Create a nice label describing the Entry

    Gtk.Label filterLabel = new Gtk.Label ("Search:");



    // Put them both into a little box so they show up side by side

    Gtk.HBox filterBox = new Gtk.HBox ();

    filterBox.PackStart (filterLabel, false, false, 5);

    filterBox.PackStart (filterEntry, true, true, 5);



    // Create our TreeView

    Gtk.TreeView tv = new Gtk.TreeView ();



    // Create a box to hold the Entry and Tree

    Gtk.VBox box = new Gtk.VBox ();



    // Add the widgets to the box

    box.PackStart (filterBox, false, false, 5);

    box.PackStart (tv, true, true, 5);



    window.Add (box);





    //setting up columns and renderers





    Gtk.TreeViewColumn nameColumn = new Gtk.TreeViewColumn{Title="Name"}; 

    Gtk.CellRendererText nameCell = new Gtk.CellRendererText ();        

    nameColumn.PackStart (nameCell, true);









    Gtk.TreeViewColumn emailColumn = new Gtk.TreeViewColumn {Title="Email"}; 

    Gtk.CellRendererText emailCell = new Gtk.CellRendererText ();

    emailColumn.PackStart (emailCell, true);







    // Add the columns to the TreeView

    tv.AppendColumn (nameColumn);

    tv.AppendColumn (emailColumn);







    // Tell the Cell Renderers which items in the model to display

    nameColumn.AddAttribute (nameCell, "text", 0);

    emailColumn.AddAttribute (emailCell, "text", 1);







    // Create a model that will hold two strings 

    Gtk.TreeStore contacts = new Gtk.TreeStore (typeof (string), typeof (string));





    // Add some hierarchical data



    Gtk.TreeIter treeiter;





    //first root

    treeiter= contacts.AppendValues("testFRIENDS"); 



        // 2 children of first root

        contacts.AppendValues(treeiter, "testOgre", "[email protected]");

        contacts.AppendValues(treeiter, "testBee", "[email protected]");







    // second root

    treeiter= contacts.AppendValues("RELATIVES"); 



        // 3 children of second root

        contacts.AppendValues (treeiter,"Mommy","[email protected]");

        contacts.AppendValues (treeiter,"Daddy", "[email protected]");

        contacts.AppendValues (treeiter,"tom", "[email protected]");









    filter = new Gtk.TreeModelFilter (contacts, null);



    // Specify the function that determines which rows to filter out and which ones to display

    filter.VisibleFunc = new Gtk.TreeModelFilterVisibleFunc (FilterTree);



    // Assign the filter as our treeview's model

    tv.Model = filter;



    // Show the window and everything on it

    window.ShowAll ();

}



private void OnFilterEntryTextChanged (object o, System.EventArgs args)

{

    // Since the filter text changed, tell the filter to re-determine which rows to display

    filter.Refilter ();

}



private bool FilterTree (Gtk.TreeModel model, Gtk.TreeIter iter)

{

    string contactname = model.GetValue (iter, 0).ToString ();



    if (filterEntry.Text == "")

        return true;



    if (contactname.IndexOf (filterEntry.Text) > -1)

        return true;

    else

        return false;

}

当前结构的最

简单的解决方案是让过滤器函数根据模型中隐藏列中的值始终为“容器”节点(朋友和亲戚)返回 TRUE。它看起来不会完全像你想要的那样,但它会起作用。

GTK+ Treeview 教程虽然已经有一段时间没有更新了,但仍然是一个非常有用的资源满足您所有的 TreeView 需求。代码和示例都是 C 语言,但大部分仍然适用于 GTK#。

It seems that when filtering rows in a tree model, a row is only visible if ALL its parents are visible too. Since your filter function hides the parent nodes, it will not display the child nodes even if the text matches. I have modified your code to illustrate this problem:

Now, one of the parent nodes begins with 'test'. If you type 'test' you'll see the filtering works correctly.

using System;
using Gtk;

public class TreeViewExample
{

public static void Main ()

{

    Gtk.Application.Init ();

    new TreeViewExample ();

    Gtk.Application.Run ();

}


Gtk.Entry filterEntry;
Gtk.TreeModelFilter filter;



public TreeViewExample ()
{

    // Create a Window

    Gtk.Window window = new Gtk.Window ("TreeView Example");

    window.SetSizeRequest (500,200);

    window.DeleteEvent+=delegate {Application.Quit();};



    // Create an Entry used to filter the tree

    filterEntry = new Gtk.Entry ();



    // Fire off an event when the text in the Entry changes

    filterEntry.Changed += OnFilterEntryTextChanged;



    // Create a nice label describing the Entry

    Gtk.Label filterLabel = new Gtk.Label ("Search:");



    // Put them both into a little box so they show up side by side

    Gtk.HBox filterBox = new Gtk.HBox ();

    filterBox.PackStart (filterLabel, false, false, 5);

    filterBox.PackStart (filterEntry, true, true, 5);



    // Create our TreeView

    Gtk.TreeView tv = new Gtk.TreeView ();



    // Create a box to hold the Entry and Tree

    Gtk.VBox box = new Gtk.VBox ();



    // Add the widgets to the box

    box.PackStart (filterBox, false, false, 5);

    box.PackStart (tv, true, true, 5);



    window.Add (box);





    //setting up columns and renderers





    Gtk.TreeViewColumn nameColumn = new Gtk.TreeViewColumn{Title="Name"}; 

    Gtk.CellRendererText nameCell = new Gtk.CellRendererText ();        

    nameColumn.PackStart (nameCell, true);









    Gtk.TreeViewColumn emailColumn = new Gtk.TreeViewColumn {Title="Email"}; 

    Gtk.CellRendererText emailCell = new Gtk.CellRendererText ();

    emailColumn.PackStart (emailCell, true);







    // Add the columns to the TreeView

    tv.AppendColumn (nameColumn);

    tv.AppendColumn (emailColumn);







    // Tell the Cell Renderers which items in the model to display

    nameColumn.AddAttribute (nameCell, "text", 0);

    emailColumn.AddAttribute (emailCell, "text", 1);







    // Create a model that will hold two strings 

    Gtk.TreeStore contacts = new Gtk.TreeStore (typeof (string), typeof (string));





    // Add some hierarchical data



    Gtk.TreeIter treeiter;





    //first root

    treeiter= contacts.AppendValues("testFRIENDS"); 



        // 2 children of first root

        contacts.AppendValues(treeiter, "testOgre", "[email protected]");

        contacts.AppendValues(treeiter, "testBee", "[email protected]");







    // second root

    treeiter= contacts.AppendValues("RELATIVES"); 



        // 3 children of second root

        contacts.AppendValues (treeiter,"Mommy","[email protected]");

        contacts.AppendValues (treeiter,"Daddy", "[email protected]");

        contacts.AppendValues (treeiter,"tom", "[email protected]");









    filter = new Gtk.TreeModelFilter (contacts, null);



    // Specify the function that determines which rows to filter out and which ones to display

    filter.VisibleFunc = new Gtk.TreeModelFilterVisibleFunc (FilterTree);



    // Assign the filter as our treeview's model

    tv.Model = filter;



    // Show the window and everything on it

    window.ShowAll ();

}



private void OnFilterEntryTextChanged (object o, System.EventArgs args)

{

    // Since the filter text changed, tell the filter to re-determine which rows to display

    filter.Refilter ();

}



private bool FilterTree (Gtk.TreeModel model, Gtk.TreeIter iter)

{

    string contactname = model.GetValue (iter, 0).ToString ();



    if (filterEntry.Text == "")

        return true;



    if (contactname.IndexOf (filterEntry.Text) > -1)

        return true;

    else

        return false;

}

}

The easiest solution with your current structure would be having the filter function always return TRUE for the 'container' nodes (Friends and Relatives), based upon a value in a hidden column in the model. It will not look exactly look the way you want, but it will work.

The GTK+ Treeview Tutorial, though not updated for some time,is still a VERY useful resource for all your TreeView needs. The code and examples are in C, but most of it still applies to GTK#.

山色无中 2024-09-22 18:20:46

为了达到代码的正确功能,我建议您按以下方式修改它:

1.向您的类添加新字段 private filterBool = false;

2.修改您的 FilterTree 方法到此状态:

private bool FilterTree (Gtk.TreeModel model, Gtk.TreeIter iter)
{
string contactname = model.GetValue (iter, 0).ToString ();

if (filterEntry.Text == "")
    return true;

if (contactname.IndexOf (filterEntry.Text) > -1)
    return true;

if (model.IterHasChild(iter)) 
{
    filerBool = false;
    investigateChildNodes(model, iter); //method checking if currently investigated
                               //node has any child fulfilling filter contitions
    return filerBool;
}
return false;
}

3.添加缺少的方法

 private void investigateChildNodes(TreeModel model, TreeIter iter) 
    {       
        TreeIter childIter;
        model.IterChildren(out childIter, iter); 
        do
        {
            if (model.GetValue(childIter, 0).ToString().IndexOf(filterEntry.Text) > -1)
                filerBool = true;

            if (model.IterHasChild(childIter))
                investigateChildNodes(model, childIter);

        } while (model.IterNext(ref childIter));
    }

通过此修改,将检查每个节点是否有可能满足过滤条件的子节点。如果检测到任何节点,则不会丢弃该节点。

In order to reach correct functionality of your code, I suggest you to modify it in following manner:

1.Add new field private filterBool = false; to your class

2.Modify your FilterTree method to this state:

private bool FilterTree (Gtk.TreeModel model, Gtk.TreeIter iter)
{
string contactname = model.GetValue (iter, 0).ToString ();

if (filterEntry.Text == "")
    return true;

if (contactname.IndexOf (filterEntry.Text) > -1)
    return true;

if (model.IterHasChild(iter)) 
{
    filerBool = false;
    investigateChildNodes(model, iter); //method checking if currently investigated
                               //node has any child fulfilling filter contitions
    return filerBool;
}
return false;
}

3.Add missing method

 private void investigateChildNodes(TreeModel model, TreeIter iter) 
    {       
        TreeIter childIter;
        model.IterChildren(out childIter, iter); 
        do
        {
            if (model.GetValue(childIter, 0).ToString().IndexOf(filterEntry.Text) > -1)
                filerBool = true;

            if (model.IterHasChild(childIter))
                investigateChildNodes(model, childIter);

        } while (model.IterNext(ref childIter));
    }

With this modification every node is checked for possible child nodes, which might fulfill filtering conditions. If any is detected, the node is not discarded.

祁梦 2024-09-22 18:20:46

Tinki 版本运行完美。我唯一不喜欢的是私有变量。这可以通过使用 InvestigateChildrenNodes 函数中的返回值来消除。

    private bool InvestigateChildNodes(TreeModel model, TreeIter iter)
    {
        TreeIter childIter;
        model.IterChildren(out childIter, iter);

        bool result = false;
        do
        {
            if (model.GetValue(childIter, 0).ToString().Contains(FilterEntry.Text))
            {
                result = true;
                break;
            }

            if (model.IterHasChild(childIter))
            {
                result = InvestigateChildNodes(model, childIter);
                if (result)
                    break;
            }

        } while (model.IterNext(ref childIter));
        return result;
    }

Tinki version works flawlessly. The only thing which I do not like is the private variable. This can be eliminated by using a return value in the InvestigateChildrenNodes function.

    private bool InvestigateChildNodes(TreeModel model, TreeIter iter)
    {
        TreeIter childIter;
        model.IterChildren(out childIter, iter);

        bool result = false;
        do
        {
            if (model.GetValue(childIter, 0).ToString().Contains(FilterEntry.Text))
            {
                result = true;
                break;
            }

            if (model.IterHasChild(childIter))
            {
                result = InvestigateChildNodes(model, childIter);
                if (result)
                    break;
            }

        } while (model.IterNext(ref childIter));
        return result;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文