我已阅读"GtkSharp TreeView tutorial",其中作者描述了如何为底层ListStore设置和使用TreeModelFilter(在教程部分“过滤数据”下)。该技术似乎不适用于底层分层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", "stinky@hotmale.com");
contacts.AppendValues(treeiter, "Bee", "stingy@coolguy.com");
// second root
treeiter = contacts.AppendValues("RELATIVES");
// 3 children of second root
contacts.AppendValues(treeiter, "Mommy", "mother@family.com");
contacts.AppendValues(treeiter, "Daddy", "father@family.com");
contacts.AppendValues(treeiter, "tom", "cousin@family.com");
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;
}
}
[我在windows vista上使用单声道2.6.4 / monodevelop 2.4 / gtk-sharp 2.12。]
答案 0 :(得分:1)
似乎在过滤树模型中的行时,只有在其所有父项都可见时才会显示行。由于您的过滤器功能隐藏了父节点,因此即使文本匹配,它也不会显示子节点。我修改了你的代码来说明这个问题:
现在,其中一个父节点以'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", "stinky@hotmale.com");
contacts.AppendValues(treeiter, "testBee", "stingy@coolguy.com");
// second root
treeiter= contacts.AppendValues("RELATIVES");
// 3 children of second root
contacts.AppendValues (treeiter,"Mommy","mother@family.com");
contacts.AppendValues (treeiter,"Daddy", "father@family.com");
contacts.AppendValues (treeiter,"tom", "cousin@family.com");
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 Tutorial虽然暂时没有更新,但对于所有TreeView需求来说仍然是一个非常有用的资源。代码和示例都在C中,但大多数仍适用于GTK#。
答案 1 :(得分:1)
为了达到代码的正确功能,我建议您按以下方式修改它:
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));
}
通过此修改,检查每个节点是否有可能满足过滤条件的子节点。如果检测到任何一个,则不丢弃该节点。
答案 2 :(得分:0)
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;
}