我正在使用Mono和Gtk开发一个C#api,并且我在尝试使我的treeView单元格可编辑时遇到了麻烦。我在此找到了几个代码片段,但问题是所提供的解决方案都不允许编辑所有列。
这是我的功能
public void cellEdited (object o, Gtk.EditedArgs args)
{
TreePath path = new TreePath (args.Path);
TreeIter iter;
listStore.GetIter (out iter, path);
int numberOfSelectedRow_ = path.Indices[0];
listStore.SetValue (iter, COLUMN_NUMBER, args.NewText);
}
问题是我无法弄清楚如何捕获所选单元格的列号。 我读过this article说我们应该使用gtk.TreeView.get_path_at_pos,但我真的不明白究竟是怎么回事。任何代码片段都会非常感激!!!谢谢大家 !
答案 0 :(得分:0)
我遇到同样的问题,但找到了一个解决方案(以及其他一些问题)。 首先,我们必须对编辑发送者对象进行排队,因为取消编辑过程是不同的视图。第二个方法是无法获得有关单个单元格或列的任何信息,但您可以将needet信息存储在派生类中。
using System;
using Gtk;
using System.Windows.Forms;
using System.Xml.Linq;
using System.Xml;
using System.Collections.Generic;
using System.Collections;
using System.Data.Linq;
using System.Linq;
namespace TestWinForms
{
/// <summary>
/// New cell renterer text. derived from Gtk.CellRendererText
/// this was neccessary to host the column Index and a Name
/// ( in this sample the Attribute name, you can also define
/// an oibject a dataRow or a XElement ... to use it directly.)
/// </summary>
public class NewCellRentererText : CellRendererText
{
public int ColumnIndex{ get; set;}
public string XmlAttributeName{ get; set;}
}
/// <summary>
/// Main class.
/// </summary>
public class MainClass
{
/// <summary>Loaded XElement to show, edit and store </summary>
private XElement MainXMLElement ;
/// <summary>TreeStore used in several methodes</summary>
private Gtk.TreeStore musicListStore;
/// <summary>
/// The editing queue.to find the correct Row that have been edited this is neccessary
/// because the c# Integration of Gtk2 seems to be not thread safe
/// </summary>
private Queue<object> EditingQueue = new Queue<object>();
/// <summary>
/// The X element indexer.
/// host for all editable XElements in the Tree
/// using UserData results in an horrible Mix from managed and unmanaged code
/// this way is clean safe.
///
/// An other solution will be to store the datareferences in an additional
/// Property of NewCellRentererText
///
/// </summary>
public Dictionary<TreeIter,XElement> XElementIndexer = new Dictionary<TreeIter, XElement> ();
/// <summary>
/// Gets or sets the dynamic tree filter reg ex.
/// for filtering , this was only experimental, but it works fine.
/// So if it is needed ....
/// </summary>
/// <value>The dynamic tree filter reg ex.</value>
public System.Text.RegularExpressions.Regex DynamicTreeFilterRegEx { get; set;}
/// <summary>
/// Gets or sets the dynamic tree filter column.
/// for filtering , this was only experimental, but it works fine.
/// So if it is needed ....
/// </summary>
/// <value>The dynamic tree filter column.</value>
public Int32 DynamicTreeFilterColumn { get; set; }
/// <summary>
/// The entry point of the program, where the program control starts and ends.
/// </summary>
/// <param name="args">The command-line arguments.</param>
public static void Main (string[] args)
{
Gtk.Application.Init ();
new MainClass ();
Gtk.Application.Run ();
}
/// <summary>
/// Handles the window destroyed_callback event.
/// safe my work :-)
/// </summary>
/// <param name="o">O.</param>
/// <param name="args">Arguments.</param>
public void On_WindowDestroyed_callback(object o, EventArgs args)
{
MainXMLElement.Save(System.IO.Path.Combine("Properties","ProcessOut.xml"));
}
/// <summary>
/// Handles the editing canceled_callback event.
/// </summary>
/// <param name="o">O.</param>
/// <param name="args">Arguments.</param>
public void On_EditingCanceled_callback(object o, EventArgs args)
{
EditingQueue.Dequeue();
}
/// <summary>
/// Handles the editing started_callback event.
/// </summary>
/// <param name="o">O.</param>
/// <param name="args">Arguments.</param>
public void On_EditingStarted_callback(object o, EditingStartedArgs args)
{
EditingQueue.Enqueue (o);
}
/// <summary>
/// Handles the edited_callback event.
/// </summary>
/// <param name="o">O.</param>
/// <param name="args">Arguments.</param>
public void On_Edited_callback(object o, EditedArgs args)
{
object quedObject = EditingQueue.Dequeue ();
NewCellRentererText ncrt = quedObject as NewCellRentererText;
if (null == ncrt)
return;
TreeIter iter;
TreePath path = new TreePath (args.Path);
if (!musicListStore.GetIter (out iter, path))
return;
XElement dummy = XElementIndexer [iter];
if (dummy.Attribute (ncrt.XmlAttributeName) != null)
dummy.SetAttributeValue (ncrt.XmlAttributeName, args.NewText);
else
foreach (string attrKvp in args.NewText.Split(";".ToCharArray())) {
String[] attribute = attrKvp.Split (":".ToCharArray ());
if (attribute.Length > 1) // && dummy.Attribute (attribute [0].Trim ()) != null)
dummy.SetAttributeValue (attribute [0].Trim (), attribute [1].Trim ());
}
musicListStore.SetValue (iter, ncrt.ColumnIndex, args.NewText);
((NewCellRentererText)o).Text = args.NewText;
}
/// <summary>
/// Creates the new column.
/// set/reset the editable property
/// ans set name and Column Index in a new instance
/// of the new derived CellRendererText class
/// </summary>
/// <returns>The new column.</returns>
/// <param name="index">Index.</param>
/// <param name="name">Name.</param>
/// <param name="editable">If set to <c>true</c> editable.</param>
private Gtk.TreeViewColumn CreateNewColumn(int index, string name, bool editable)
{
NewCellRentererText renderer = new NewCellRentererText ();
renderer.ColumnIndex =index ;
renderer.XmlAttributeName=name;
renderer.Editable = editable;
if (editable) {
renderer.Edited += On_Edited_callback;
renderer.EditingStarted += On_EditingStarted_callback;
renderer.EditingCanceled += On_EditingCanceled_callback;
}
Gtk.TreeViewColumn ret = new Gtk.TreeViewColumn (renderer.XmlAttributeName, renderer);
ret.AddAttribute (ret.CellRenderers [0], "text", index);
return ret;
}
/// <summary>
/// Initializes a new instance of the <see cref="TestWinForms.MainClass"/> class.
/// </summary>
public MainClass ()
{
// using Styles ( OPTIONAL EXPERIMENTAL BUT WORKING )
Gtk.Rc.Parse (System.IO.Path.Combine("Properties","ProcessOut.xml"));
// Normal stuff to create a tree view
Gtk.Window window = new Gtk.Window ("TreeView Example");
window.SetSizeRequest (800, 600);
window.Destroyed += On_WindowDestroyed_callback;
Gtk.TreeView tree = new Gtk.TreeView ();
window.Add (tree);
// one Part of the Solution to host column index
tree.AppendColumn (CreateNewColumn(0, "name", false));
tree.AppendColumn (CreateNewColumn(1, "id", false));
tree.AppendColumn (CreateNewColumn(2, "type", true));
tree.AppendColumn (CreateNewColumn(3, "class", true));
tree.AppendColumn (CreateNewColumn(4, "parameter", true));
tree.AppendColumn (CreateNewColumn(5, "others", true));
Type[] TypeList = Enumerable.Repeat (typeof(string), tree.Columns.Length).ToArray();
musicListStore = new Gtk.TreeStore (TypeList);
// some XML stuff to make something usefull
MainXMLElement = XElement.Load ("Properties/Process.xml",LoadOptions.PreserveWhitespace);
XElement y = MainXMLElement.Element ("processes");
XAttribute title = y.Attribute ("name");
TreeIter iter = musicListStore.AppendValues(title == null ? y.Name.ToString() : title.Value);
XElementIndexer.Add (iter, y);
walkThroughXml (y, musicListStore, iter);
// using row filter ( OPTIONAL EXPERIMENTAL BUT WORKING )
Gtk.TreeModelFilter filter = new Gtk.TreeModelFilter (musicListStore, null);
filter.VisibleFunc = new Gtk.TreeModelFilterVisibleFunc (FilterTree);
tree.Model = filter;
// Now let us see
window.ShowAll ();
}
/// <summary>
/// Walks the through xml.
/// recursive TreView filling based on an XML Element
/// </summary>
/// <param name="xe">Xe.</param>
/// <param name="ts">Ts.</param>
/// <param name="ti">Ti.</param>
public void walkThroughXml(XElement xe, Gtk.TreeStore ts, Gtk.TreeIter ti )
{
if (!xe.HasElements)
return;
IEnumerable<XElement> elements = xe.Elements ();
foreach ( XElement element in elements )
{
// this part could be more generic, but it works for my requirements
String Xid = "";
String Xtype = "";
String Xclass = "";
String Xparameter = "";
System.Text.StringBuilder dataExpression = new System.Text.StringBuilder();
XAttribute title = element.Attribute ("name");
foreach (XAttribute data in element.Attributes()) {
switch (data.Name.ToString ()) {
case "name":
break;
case "id":
Xid = data.Value;
break;
case "type":
Xtype = data.Value;
break;
case "class":
Xclass = data.Value;
break;
case "parameter":
Xparameter = data.Value;
break;
default:
dataExpression.AppendFormat ("{0}: {1} ; ", data.Name.ToString (), data.Value);
break;
}
}
TreeIter iter = (TreeIter)ts.AppendValues (ti, title == null ? element.Name.ToString () : title.Value, Xid,Xtype,Xclass,Xparameter, dataExpression.ToString ());
walkThroughXml (element, ts, iter);
XElementIndexer.Add (iter,element);
}
}
/// <summary>
/// Filters the tree.
///
/// for filtering , this was only experimental, but it works fine.
/// So if it is needed ....
///
/// </summary>
/// <returns><c>true</c>, if tree was filtered, <c>false</c> otherwise.</returns>
/// <param name="model">Model.</param>
/// <param name="iter">Iter.</param>
private bool FilterTree (Gtk.TreeModel model, Gtk.TreeIter iter)
{
if (DynamicTreeFilterRegEx == null)
return true;
string searchName = model.GetValue (iter, DynamicTreeFilterColumn).ToString ();
return DynamicTreeFilterRegEx.IsMatch (searchName);
}
}
}
这里是测试XML文件
<?xml version="1.0" encoding="UTF-8" ?>
<document>
<enums>
</enums>
<processes>
<process name="main" id="p1" positionmeasurement="milliseconds" speed="4100" processlength="10000" maxitems="255" mingap="5">
<actuators>
<actuator name="actuator1" id="p1pa1" type="sensor" class="" parameter="" position="100" window="220" />
<actuator name="actuator2" id="p1pa2" type="sensor" class="" parameter="" position="1050" window="220" />
<actuator name="actuator3" id="p1pa3" type="receiver" class="" parameter="" position="9100" window="220" />
</actuators>
<processsteps>
<processstep name="step1" id="p1ps0" type="runningstate" class="" parameter="" order="1" retriggerable="true" />
<processstep name="step1" id="p1ps1" type="sync" class="" parameter="" order="2" retriggerable="false" />
<processstep name="step2" id="p1ps2" type="async" class="" parameter="" order="3" retriggerable="true" />
<processstep name="step3" id="p1ps3" type="timed" position="25000" window="1200" class="" parameter="" order="4" retriggerable="false" />
</processsteps>
<triggers>
<trigger name="trigger1" id="p1pt1" sourceid="p1pa2" sourcestate="start" targetid="p1ps2" triggertype="start" />
<trigger name="trigger2" id="p1pt2" sourceid="p1pa2" sourcestate="error" targetid="p1ps2" triggertype="stop" />
<trigger name="trigger3" id="p1pt3" sourceid="p1pa3" sourcestate="end" targetid="p1ps3" triggertype="start" />
</triggers>
<assertations>
<assertation name="assertation1" id="p1pas1" type="itemstate" rule="text" class="" parameter="" />
<assertation name="assertation2" id="p1pas2" type="itemstate" rule="text" class="" parameter="" />
<assertation name="assertation3" id="p1pas3" type="itemstate" rule="text" class="" parameter="" />
</assertations>
<exceptions>
<exception name="exception1" id="p1pex1" retriggerable="false">
<conditions logic="and">
<condition name="contition1" id="p1pex1c1" class="" paramter="" type="PLC" message="2001" />
<condition name="contition2" id="p1pex1c2" class="" paramter="" type="PLC" message="2005" />
</conditions>
<actions>
<action name="action1" id="p1pex1pac1" class="" parameter="" actiontype="stopfeeder" type="asynchron" order="1" />
<action name="action1" id="p1pex1pac1" class="" parameter="" actiontype="flushallitem" type="synchron" timeout="5000" order="2" />
<action name="action2" id="p1pex1pac2" class="" parameter="" actiontype="rundown" type="synchron" timeout="5000" order="3" />
</actions>
</exception>
</exceptions>
</process>
<process name="sub" id="p2">
</process>
<process name="sub2" id="p3">
<actuators>
<actuator name="actuator1" id="p3pa1" type="sensor" class="" parameter="" />
<actuator name="actuator2" id="p3pa2" type="sensor" class="" parameter="" />
<actuator name="actuator3" id="p3pa3" type="receiver" class="" parameter="" />
</actuators>
<processsteps>
<processstep name="step1" id="p3ps0" type="runningstate" class="" parameter="" />
<processstep name="step1" id="p3ps1" type="sync" class="" parameter="" />
<processstep name="step2" id="p3ps2" type="async" class="" parameter="" />
<processstep name="step3" id="p3ps3" type="timed" processtime="25000" window="1200" class="" parameter="" />
</processsteps>
<triggers>
<trigger name="trigger1" id="p3pt1" sourceid="p3pa2" sourcestate="start" targetid="p3ps2" triggertype="start" />
<trigger name="trigger2" id="p3pt2" sourceid="p3pa2" sourcestate="error" targetid="p3ps2" triggertype="stop" />
<trigger name="trigger3" id="p3pt3" sourceid="p3pa3" sourcestate="end" targetid="p3ps3" triggertype="start" />
</triggers>
<assertations>
<assertation name="assertation1" id="p3pas1" type="itemstate" rule="text" class="" parameter="" />
<assertation name="assertation2" id="p3pas2" type="itemstate" rule="text" class="" parameter="" />
<assertation name="assertation3" id="p3pas3" type="itemstate" rule="text" class="" parameter="" />
</assertations>
</process>
</processes>
</document>
将其保存在属性文件夹中并检查以复制