[Gtk-sharp-list] DataGrid control, data binding, ObjectViews, Swf,
Gtk
Gennadiy Donchyts
don at env.com.ua
Sat Nov 19 18:57:56 EST 2005
Another thing I've found there is that grid takes only String values
... I've made a
small hack to fix it (change parameter for SetColumnValue string => object), but
still it is not everywhere. I have to make some draft version very
soon and then will
clean up and send a modified code. Probably in a few weeks or so.
On 11/19/05, Daniel Morgan <monodanmorg at yahoo.com> wrote:
> I really do wish someone would create a real-world enterprise-class DataGrid
> for gtk# with Data Binding and many other fancy features.
>
> And take this DataGrid and make it part of Gtk#. Maybe have another gtk#
> assembly for these extra widgets.
>
> Anyways, I have something I have worked on for awhile, maybe others can
> improve upon it and give back. If someone is interested, we could put it
> into subversion somewhere with other gtk# widgets. Chris and I have
> discussed this in the past, but there is only so much time.
>
> Here are the source files I included:
> DataGrid.cs - creates a DataGrid for Gtk# with data binding support.
> TestDataGrid.cs - tests the gtk# data grid
>
> Basically, you can take a pre-loaded DataTable, and then "data bind" it to
> the DataGrid. It may not be true data binding like SWF or webforms, but its
> an attempt.
>
> A change to the data in the DataGrid w! ill update the data in the
> DataTable.
> If you add a row or delete a row in the DataGrid, it will be reflected in
> the DataTable. If you modify a cell in the DataGrid, the row and column in
> the DataTable will be updated.
>
> In the test, there are four menu items:
> 1. Add Row - adds a row
> 2. Dump Table - dumps to the console what's in the DataTable
> 3. Editable - sets the DataGrid editable or not
> 4. Delete Selected Row - deletes the highlighted row
>
> One thing I would love to see added is an Input Mask and Format properties
> added to each column. Let's say you would like to format dates as a short
> date, you would put "Short Date" in the format property for any locale. Or
> you could set it to "MM/DD/YYYY" for US locale.. A numeric column you
> could have it format as "###,###,###.##" or just simply say "Currency" for
> any locale.
> An input mask could limit certain characters, such as, alpha only, nu! meric
> only, alphanumeric. Canadian postal code i believe is alpha, numeric,
> alpha, space, numeric, alpha, numeric. So your input mask would be: "ANA
> NAN".
>
> Anyways, please let me know if this is what you wanted.
>
>
> Christian Hergert <christian.hergert at gmail.com> wrote:
> The original version of the datagrid i put in monodevelop was taken
> from sqlsharpgtk. Daniel has since done some additions to the
> monodevelop version to support basic editing and deletion. It also had
> support for somewhat of a managed tree model. To be honest, im not
> sure if ive merged that support yet. Both versions are a hack
> regardless.
>
> Your example below will not work. First, you cant add an object to a
> gtk container that doesnt inherit from Gtk.Widget. Secondly, even if
> it did work, you would have just doubled your requirements on every
> platform to both gtk and swf.
>
> -- Christian
>
> On 11/17/05, Gennadiy Donchyts wrote:
> > Hi all,
> >
> > I'm wondering if there is some DataGrid-like control available for
> > Gtk# which has data-binding implemented. For now I've found 2
> > DataGirds in the (1) sqlsharpgtk and (2) MD, which one of these is the
> > latest and will be updated in the future?.
> >
> > Also there is implementation of the (3) SWF DataGrid ...quite complex
> > and still in development but looks very nice also.
> >
> > The question is if will be theoretically possible to use SWF DataGrid
> instead of
> > Gtk# controls, so to implement something like (Gtk <=> Swf bridge):
> >
> > using Swf = System.Windows.Forms;
> >
> > Swf.DataGrid grid = new Swf.DataGrid();
> > Gtk.Window w = new Gtk.Window();
> > window.Add(grid);
> >
> > Since I need everything to be cross! -platform and stable for my
> application the
> > Gtk# was chosen for gui development. After some play I managed to run a
> nice
> > test (see below), but then it came to the point when it is necessary
> > to extend it
> > quite a lot (add events, properties, etc.) , so my second question is
> > if someone is
> > already doing or plan to continue development of that class?
> >
> > Best regards,
> > --Gena
> >
> > -----------
> > Test using SqlSharp DataGrid
> > (NHibernate => IList => ObjectViews => (1, Gtk) DataGrid:
> >
> > ...
> > class Project
> > {
> > private int id;
> > private string name;
> >
> > public int ID { get ... }
> > public string Name { get ... }
> > }
> > ...
> >
> > // load from db into IList using NHibernate
> > IList projects = session.Select(typeof(Project), null);
> >
> > // create object view (ITypedList, IBindingList), see objectv! iews on
> sourceforge
> > ObjevtView view = new ObjectView(typeof(Project));
> > view.Columns.Add(new ObjectViewColumn("ID"));
> > view.Columns.Add(new ObjectViewColumn("Name"));
> > view.DataSource = projects;
> >
> > // create and fill project table
> > DataGrid grid = new DataGrid();
> > grid.DataSource = vew;
> >
> > which gives me a nice table with 2 columns :):
> >
> > ID Name
> > -----------------
> > 1 project1
> > 2 project2
> > _______________________________________________
> > Gtk-sharp-list maillist - Gtk-sharp-list at lists.ximian.com
> >
> http://lists.ximian.com/mailman/listinfo/gtk-sharp-list
> >
>
>
> --
> Christian Hergert
> Medsphere Systems Corporation
> Software Programmer
> C: 253 906 2115
> _______________________________________________
> Gtk-sharp-list maillist - Gtk-sharp-list at lists.ximian.com
> http://lists.ximian.com/mailman/listinfo/gtk-sharp-list
>
>
>
> ________________________________
> Yahoo! FareChase - Search multiple travel sites in one click.
>
>
> // DataGrid.cs
> namespace Mono.Data.GtkSharp
> {
> // notice System.Data is not references here
>
> using System;
> using System.Collections;
> using System.ComponentModel;
> using System.Reflection;
> using System.Runtime.InteropServices;
> using System.Text;
>
> using GLib;
> using Gtk;
>
> public class DataGridColumn
> {
> private string columnName = "";
> private TreeViewColumn treeViewColumn = null;
> public CellRendererText Renderer = null; // should be
> internal
>
> public string ColumnName {
> get {
> return columnName;
> }
> set {
> columnName = value;
> }
> }
>
> public TreeViewColumn TreeViewColumn {
> get {
> return treeViewColumn;
> }
> set {
> treeViewColumn = value;
> }
> }
> }
>
> public class DataGrid : VBox {
> private ListStore store;
> private TreeView treeView;
>
> public ArrayList gridColumns; // TODO: make Columns a
> collection
>
> public DataGrid () : base(false, 4) {
> ScrolledWindow sw = new ScrolledWindow ();
> this.PackStart (sw, true, true, 0);
>
> treeView = new TreeView (store);
> treeView.HeadersVisible = true;
> //treeView.ModifyFont
> (Pango.FontDescription.FromString ("courier new"));
>
> gridColumns = new ArrayList(0);
>
> sw.Add (treeView);
>
> store = new ListStore (GLib.GType.String);
>
> treeView.EnableSearch = true;
> treeView.HeadersClickable = true;
> dataMember = "";
> dataSource = null;
>
>
> }
>
> ArrayList bindrows = null;
> object resolvedDataSource = null;
>
> private bool editable = true;
>
> private object dataSource;
>
> private string dataMember;
>
> public int SelectedRow {
> get {
> TreeIter iter;
> TreeModel model;
> TreeSelection selection = treeView.Selection;
> if (selection.GetSelected (out model, out
> iter)) {
> TreePath[] path =
> selection.GetSelectedRows (out model);
> return path[0].Indices[0]; // return
> selected row
> }
> else
> return -1; // not selected
> }
> }
>
> public TreeIter SelectedIter {
> get {
> TreeIter iter;
> TreeModel model;
> TreeSelection selection = treeView.Selection;
> if (selection.GetSelected (out model, out
> iter))
> return iter; // return seelcted iter
> else
> return TreeIter.Zero; // not selected
> }
>
> }
>
> public TreeView View
> {
> get
> {
> return treeView;
> }
> }
>
> public object DataSource
> {
> get
> {
> return dataSource;
> }
> set
> {
> dataSource = value;
> }
> }
>
> public string DataMember
> {
> get
> {
> return dataMember;
> }
> set
> {
> dataMember = value;
> }
> }
>
> public ListStore Store
> {
> get
> {
> return store;
> }
> }
>
> public ArrayList Columns
> {
> get
> {
> return gridColumns;
> }
> }
>
> public bool Editable {
> get {
> return editable; // not a good way to see if
> its editable or not
> // because various columns could be editable
> and others non-editable
> }
>
> set {
> editable = value;
> if (value == true) {
> for(int c = 0; c < gridColumns.Count;
> c++) {
> DataGridColumn col =
> (DataGridColumn) gridColumns[c];
> col.TreeViewColumn.Clickable
> = true;
> col.Renderer.Mode =
> CellRendererMode.Editable;
> col.Renderer.Editable = true;
> }
> treeView.RulesHint = true;
> treeView.Selection.Mode =
> SelectionMode.Single;
> }
> else {
> for(int c = 0; c < gridColumns.Count;
> c++) {
> DataGridColumn col =
> (DataGridColumn) gridColumns[c];
> col.TreeViewColumn.Clickable
> = false;
> col.Renderer.Mode =
> CellRendererMode.Inert;
> col.Renderer.Editable =
> false;
> }
> treeView.RulesHint = false;
> treeView.Selection.Mode =
> SelectionMode.Single;
> }
> }
> }
>
> public int AddNew()
> {
> // TODO: need to check if resolved data source is not
> null
>
> IBindingList b = (IBindingList) resolvedDataSource;
>
> if (b.AllowNew) {
> object obj = b.AddNew();
> if (obj == null) {
> //Console.Error.WriteLine("obj is
> null");
> }
> else {
>
> //Console.Error.WriteLine("Type: " +
> obj.GetType().ToString());
> bindrows.Add(obj);
>
> TreeIter iter = NewRow();
> for(int i = 0; i < gridColumns.Count;
> i++)
> SetColumnValue (iter, i,
> String.Empty);
>
> return bindrows.Count - 1;
> }
> }
>
> return -1;
> }
>
> public int DeleteRow(int row)
> {
> if (row < 0)
> return -1; // should throw an exception - out
> of range
>
> TreeIter iter = TreeIter.Zero;
> if (store.IterNthChild(out iter, row) == false)
> return -1;
>
> IBindingList b = (IBindingList) resolvedDataSource;
> if (b.AllowRemove) {
> IList list = (IList) resolvedDataSource;
> bindrows.RemoveAt(row);
> store.Remove (ref iter);
> list.RemoveAt (row);
>
> return row;
> }
>
> return -1;
> }
>
> // sets the column count. beware, it clears
> // use this if you are going to load each column and row
> yourself
> // instead of using DataBind() or DataLoad()
> public void SetColumnCount (int columnCount)
> {
> Clear ();
> dataMember = "";
> dataSource = null;
>
> GLib.GType[] theTypes = new GLib.GType[columnCount];
> gridColumns = new ArrayList ();
> for (int col = 0; col < columnCount; col++)
> {
> theTypes[col] = GLib.GType.String;
> gridColumns.Add (new DataGridColumn ());
> }
> store.ColumnTypes = theTypes;
> }
>
> // load data from a data table or data set
> public long DataBind ()
> {
> long rowsRetrieved = 0;
>
> Clear ();
>
> System.Object o = null;
> o = GetResolvedDataSource (DataSource, DataMember);
> resolvedDataSource = o;
> IEnumerable ie = (IEnumerable) o;
> ITypedList tlist = (ITypedList) o;
> TreeIter iter = new TreeIter ();
>
> PropertyDescriptorCollection pdc =
> tlist.GetItemProperties (new PropertyDescriptor[0]);
> gridColumns = new ArrayList(pdc.Count);
>
> // define the columns in the treeview store
> // based on the schema of the result
> GLib.GType[] theTypes = new GLib.GType[pdc.Count];
> for (int col = 0; col < pdc.Count; col++) {
> theTypes[col] = GLib.GType.String;
> }
> store.ColumnTypes = theTypes;
>
> bindrows = new ArrayList();
>
> int colndx = -1;
> foreach (PropertyDescriptor pd in pdc) {
>
> colndx ++;
>
> DataGridColumn gridCol = new DataGridColumn
> ();
> gridCol.ColumnName = pd.Name;
> gridColumns.Add (gridCol);
> }
>
> bindrows = new ArrayList();
>
> foreach (System.Object obj in ie) {
> ICustomTypeDescriptor custom =
> (ICustomTypeDescriptor) obj;
> PropertyDescriptorCollection properties =
> custom.GetProperties ();
>
> bindrows.Add(obj);
>
> rowsRetrieved ++;
> iter = NewRow ();
> int cv = 0;
> foreach (PropertyDescriptor property in
> properties) {
> object oPropValue = property.GetValue
> (obj);
> string sPropValue = "";
> if (oPropValue.GetType ().ToString
> ().Equals("System.Byte[]")) {
> //sPropValue =
> SqlSharpGtk.GetHexString ((byte[]) oPropValue);
>
> Console.Error.WriteLine("Byte[] value");
> }
> else
> sPropValue =
> oPropValue.ToString ();
>
> SetColumnValue (iter, cv,
> sPropValue);
> cv++;
> }
> }
>
> treeView.Model = store;
> AutoCreateTreeViewColumns ();
> return rowsRetrieved;
> }
>
> // borrowed from Mono's System.Web implementation
> protected IEnumerable GetResolvedDataSource(object source,
> string member)
> {
> if (source != null && source is IListSource) {
> IListSource src = (IListSource) source;
> IList list = src.GetList ();
> if (!src.ContainsListCollection) {
> return list;
> }
> if (list != null && list is ITypedList) {
>
> ITypedList tlist = (ITypedList) list;
> PropertyDescriptorCollection pdc =
> tlist.GetItemProperties (new PropertyDescriptor[0]);
> if (pdc != null && pdc.Count > 0) {
> PropertyDescriptor pd = null;
> if (member != null &&
> member.Length > 0) {
> pd = pdc.Find
> (member, true);
> } else {
> pd = pdc[0];
> }
> if (pd != null) {
> object rv =
> pd.GetValue (list[0]);
> if (rv != null && rv
> is IEnumerable) {
> return
> (IEnumerable)rv;
> }
> }
> throw new Exception
> ("ListSource_Missing_DataMember");
> }
> throw new Exception
> ("ListSource_Without_DataMembers");
> }
> }
> if (source is IEnumerable) {
> return (IEnumerable)source;
> }
> return null;
> }
>
> public void Clear ()
> {
> if (store != null)
> {
> store.Clear ();
> store = null;
> store = new ListStore (GLib.GType.String);
> }
> else
> store = new ListStore (GLib.GType.String);
>
> if (gridColumns != null)
> {
> for (int c = 0; c < gridColumns.Count; c++)
> {
> DataGridColumn gridCol =
> (DataGridColumn) gridColumns[c];
> if (gridCol.TreeViewColumn != null)
> {
> treeView.RemoveColumn
> (gridCol.TreeViewColumn);
> gridCol.TreeViewColumn =
> null;
> }
> }
> gridColumns.Clear ();
> gridColumns = null;
> }
> }
>
> public TreeIter NewRow ()
> {
> return store.Append();
> }
>
> public void AddRow (object[] columnValues)
> {
> TreeIter iter = NewRow ();
> for(int col = 0; col < columnValues.Length; col++) {
> string cellValue = columnValues[col].ToString
> ();
> SetColumnValue (iter, col, cellValue);
> }
> }
>
> public void SetColumnValue (TreeIter iter, int column, string
> value)
> {
> GLib.Value cell = new GLib.Value (value);
> store.SetValue (iter, column, cell);
> }
>
> public void SetColumnValue (TreeIter iter, int column, byte[]
> value)
> {
> //string svalue = SqlSharpGtk.GetHexString (value);
> //SetColumnValue (iter, column, svalue);
> }
>
> private void AutoCreateTreeViewColumns ()
> {
> for(int col = 0; col < gridColumns.Count; col++) {
> // escape underscore _ because it is used
> // as the underline in menus and labels
> StringBuilder name = new StringBuilder ();
> foreach (char ch in ((DataGridColumn)
> gridColumns[col]).ColumnName) {
> if (ch == '_')
> name.Append ("__");
> else
> name.Append (ch);
> }
> TreeViewColumn tvc = CreateColumn (col,
> name.ToString ());
> AppendColumn (tvc);
> }
> }
>
> public int AppendColumn(TreeViewColumn tvc)
> {
> return treeView.AppendColumn (tvc);
> }
>
> private void TextCellEdited (object o, EditedArgs args)
> {
> IBindingList b = (IBindingList) resolvedDataSource;
> if (b.AllowEdit) {
> // ITypedList may help you figure out how to
> convert
> // between different data types
> // such as, how to display numbers, dates,
> etc...
>
> TreePath path = new TreePath (args.Path);
> TreeIter iter;
> store.GetIter (out iter, path);
> int i = path.Indices[0];
>
> TreePath cpath;
> string cellValue = args.NewText;
>
> TreeViewColumn tvcolumn;
> treeView.GetCursor (out cpath, out tvcolumn);
> int c = 0;
> for(c = 0; c < gridColumns.Count; c++) {
> TreeViewColumn tvc =
> ((DataGridColumn) gridColumns[c]).TreeViewColumn;
> if (tvcolumn == tvc) {
>
> Console.Error.WriteLine("Column clicked: Column Name: " + tvcolumn.Title);
> Console.Error.WriteLine("
> Ordinal: " + c.ToString());
> break;
> }
> }
>
> if (c >= gridColumns.Count)
> Console.Error.WriteLine("tv col not
> found");
>
> ICustomTypeDescriptor custom =
> (ICustomTypeDescriptor) bindrows[i];
> PropertyDescriptorCollection properties =
> custom.GetProperties ();
> PropertyDescriptor pd = properties[c];
> pd.SetValue(bindrows[i], cellValue);
>
> Console.Error.WriteLine(" Row number: " +
> path.Indices[0].ToString());
> Console.Error.WriteLine(" Cell Value: " +
> cellValue);
>
> ((IEditableObject) bindrows[i]).BeginEdit();
> SetColumnValue(iter, c, cellValue);
> ((IEditableObject) bindrows[i]).EndEdit();
> }
> }
>
> public TreeViewColumn CreateColumn (int columnNum, string
> columnName)
> {
> TreeViewColumn treeViewCol = new TreeViewColumn ();
> CellRendererText renderer = new CellRendererText ();
> treeViewCol.Clickable = true;
> treeView.RulesHint = true;
>
> treeView.Selection.Mode = SelectionMode.Single;
> //treeView.Selection.Mode = SelectionMode.Multiple;
>
> // Editable, Activatable, Inert
> renderer.Mode = CellRendererMode.Editable;
> //renderer.Family = "courier new";
> renderer.Editable = true;
> renderer.Edited += new EditedHandler
> (TextCellEdited);
> treeViewCol.Title = columnName;
> treeViewCol.PackStart (renderer, true);
> treeViewCol.AddAttribute (renderer, "text",
> columnNum);
>
> DataGridColumn gridCol = (DataGridColumn)
> gridColumns[columnNum];
> gridCol.Renderer = renderer;
> gridCol.TreeViewColumn = treeViewCol;
>
> return treeViewCol;
> }
> }
> }
>
>
>
> // $ mcs /out:TestDataGrid.exe TestDataGrid.cs DataGrid.cs
> -pkg:gtk-sharp-2.0 /r:System.Data.dll
> // TestDataGrid.cs
> namespace Mono.Data.GtkSharp
> {
> using System;
> using System.Collections;
> using System.Collections.Specialized;
> using System.Configuration;
> using System.Data;
> using System.Data.Common;
> using System.Data.SqlTypes;
> using System.Text;
> using System.IO;
> using System.Reflection;
> using System.Runtime.Remoting;
> using System.Runtime.InteropServices;
> using System.Diagnostics;
>
> using Gdk;
> using Gtk;
>
> public class TestDataGrid
> {
> private DataTable table;
> private Gtk.Window win;
> private DataGrid grid;
> //private TextView textView;
>
> public static readonly string ApplicationName = "Gtk#
> DataGrid Test";
>
> public TestDataGrid ()
> {
> CreateGui ();
> }
>
> public DataGrid Grid {
> get {
> return grid;
> }
> }
>
> public void Show ()
> {
> win.ShowAll ();
> }
>
> public void CreateGui ()
> {
> win = new Gtk.Window (ApplicationName);
> win.DeleteEvent += new Gtk.DeleteEventHandler
> (OnWindow_Delete);
> win.BorderWidth = 4;
> win.SetDefaultSize (600, 500);
>
> VBox vbox = new VBox (false, 4);
> win.Add (vbox);
>
> MenuBar mb = CreateMenuBar ();
> vbox.PackStart (mb, false, false, 0);
>
> VPaned vpaned = new VPaned ();
> vbox.PackStart (vpaned, true, true, 0);
> grid = CreateOutputResultsDataGrid();
> vpaned.Add1 (grid);
> }
>
> DataGrid CreateOutputResultsDataGrid ()
> {
> DataGrid grid = new DataGrid ();
>
> //grid.View.ButtonReleaseEvent +=
> // new Gtk.ButtonReleaseEventHandler
> (OnDataGridButtonRelease);
> //grid.Editable = false;
>
> return grid;
> }
>
> void OnWindow_Delete (object o, Gtk.DeleteEventArgs args)
> {
> QuitApplication();
> }
>
> void QuitApplication ()
> {
> Application.Quit ();
> }
>
> public static void DoEvents ()
> {
> while (Application.EventsPending ())
> Application.RunIteration ();
> }
>
> public DataTable BuildDataTableSample()
> {
> table = new DataTable();
>
> int maxColumns = 5;
> int maxRows = 3;
>
> for(int i = 0; i < maxColumns; i++) {
> string columnName =
> String.Format("Column{0}", i);
> table.Columns.Add(columnName);
> }
>
> for(int r = 0; r < maxRows; r++) {
> DataRow row = table.NewRow();
> for(int c = 0; c < table.Columns.Count; c++)
> {
> string cellValue =
> String.Format("(Row{0},Column{1})", r, c);
> row[c] = cellValue;
> }
> table.Rows.Add(row);
> }
>
> return table;
> }
>
> void OnMenu_DumpTable (object o, EventArgs args)
> {
> Console.Error.WriteLine("__=========== T a b l e
> D u m p =========__");
> Console.Error.WriteLine("Row Count: " +
> table.Rows.Count.ToString());
> for(int r = 0; r < table.Rows.Count; r++) {
> DataRow row = table.Rows[r];
> StringBuilder sb = new StringBuilder();
> for(int c = 0; c < table.Columns.Count; c++)
> {
> string s = row[c].ToString();
> sb.Append(s);
> sb.Append(" ");
> }
> Console.Error.WriteLine(sb.ToString());
> }
>
> Console.Error.WriteLine("=-----------------------------------------------=");
> }
>
> void OnMenu_Editable (object o, EventArgs args)
> {
> grid.Editable = !grid.Editable;
> }
>
> void OnMenu_AddNew (object o, EventArgs args)
> {
> AddNew();
> }
>
> void OnMenu_DeleteSelectedRow (object o, EventArgs args)
> {
> int selectedRow = 0;
> selectedRow = grid.SelectedRow;
> if (selectedRow >= 0)
> grid.DeleteRow (selectedRow);
> }
>
> void AddNew()
> {
> grid.AddNew();
> }
>
> public MenuBar CreateMenuBar () {
> MenuBar menuBar = new MenuBar ();
> Menu menu;
> MenuItem item;
> MenuItem barItem;
>
> menu = new Menu ();
>
> item = new MenuItem ("Add Row");
> item.Activated += new EventHandler (OnMenu_AddNew);
> menu.Append (item);
>
> item = new MenuItem ("Dump Table");
> item.Activated += new EventHandler
> (OnMenu_DumpTable);
> menu.Append (item);
>
> item = new MenuItem ("Editable");
> item.Activated += new EventHandler (OnMenu_Editable);
> menu.Append (item);
>
> item = new MenuItem ("Delete Selected Row");
> item.Activated += new EventHandler
> (OnMenu_DeleteSelectedRow);
> menu.Append (item);
>
> barItem = new MenuItem ("Test");
> barItem.Submenu = menu;
> menuBar.Append (barItem);
>
> return menuBar;
> }
>
> public static int Main (string[] args)
> {
> Application.Init ();
> TestDataGrid test = new TestDataGrid ();
> DataTable sampleTable = test.BuildDataTableSample();
> test.Grid.DataSource = sampleTable;
> test.Grid.DataBind();
> test.Show ();
> DoEvents ();
> //test.Grid.Editable = false;
> Application.Run ();
> return 0;
> }
> }
> }
>
>
>
>
More information about the Gtk-sharp-list
mailing list