Introduction
This article is focused on the two widgets XrwList
and XrwTree
from the Roma Widget Set (Xrw). The Roma Widget Set is a zero dependency GUI application framework for X11 (it requires only assemblies of the free Mono standard installation and libraries of the free X11 distribution; it doesn't particularly require GNOME, KDE or commercial libraries) and is implemented entirely in C#.
Please find the concepts and general descriptions in Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - part 1, Basics. It should always be the preferred source.
The reference description of simple widgets (this is where XrwList
, XrwTree
and XrwViewportGridViewHeader
belong to) is to find in Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - part 2, Simple widgets.
The reference description of composite widgets (this is where XrwViewport
belongs to) is to find in Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - part 3, Composite widgets.
Background
The XrwList
and XrwTree
widgets deliver their full performance only in cooperation with the XrwViewport
and XrwViewportGridViewHeader
widgets. How to apply this widget combination is, where this article goes beyond the reference description of the single widgets. This covers among other things:
- text view, large icon view, small icon view, list view and advanced (multi column) view for
XrwList
- list view item selection notification for
XrwList
- automatic detection of the demand to show scrollbar(s), demand independend always visible or always hidden scrollbars for
XrwViewport
with XrwList
and XrwList
- singel and multi selection for
XrwList
- text view and advanced (multi column) view for
XrwTree
- column header display for
XrwList
and XrwTree
using XrwViewportGridViewHeader
inside XrwViewport
- column resizing and reordering for
XrwList
and XrwTree
using XrwViewportGridViewHeader
inside XrwViewport
- in-place editing for
XrwList
and XrwTree
(including information about the editor invocation)
Using the code
The sample application was written with Mono Develop 2.4.1 for Mono 2.8.1 on OPEN SUSE 11.3 Linux 32 bit EN and GNOME desktop. Neither the port to any older nor to any newer version should be a problem. The sample application's solution consists of one project containing all the necessary source code.
The sample application is also tested with Mono Develop 3.0.6 for Mono 3.0.4 on OPEN SUSE 12.3 Linux 64 bit DE and GNOME desktop, IceWM, TWM und Xfce.
The Xlib/X11 window handling is based on the X11Wrapper assembly version 0.5, that defines the function prototypes, structures and types for Xlib/X11 calls to the libX11.so. It has been developed for the Programming Xlib with Mono Develop - Part 1: Low-level (proof of concept) project and has been advanced during the Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - Part 1, Basics project.
The sample application implements several levels of complexity and functionality for XrwList
and XrwTree
widgets.
To play with the executables start either /bin/Debug/32/UnleshListAndTree.exe on 32 bit systems or /bin/Debug/64/UnleshListAndTree.exe on 64 bit systems.
To load the project use either the UnleshListAndTree32.sln on 32 bit systems or the UnleshListAndTree.sln on 64 bit systems.
Let's start with some XrwList
samples and continue with XrwTree
later on.
XrwList samples
Just text, no scroll
The most simple implementation of XrwList
is to use it without XrwViewport
and XrwViewportGridViewHeader
and to display static text only. This approach, to omit a surrounding XrwViewport
, is not recommended - except the minimum size of the XrwList
guarantees the complete display of all list items.
The sample code shows how to define the list widget (with the minimum of recommended calls) and to connect the EntrySelectionChanged
event's anonymous delegate.
XrwList list1 = XrwList.NewListWidget (parent);
list1.ExpandToAvailableHeight = true;
list1.ExpandToAvailableWidth = true;
parent.AddChild (list1);
list1.EntrySelectionChanged += delegate(XrwRectObj source, XrwList.ListItem entry)
{
IGridViewElement item = list1.SelectedItem ();
string selection = "Selected item(s): ";
int index = -1;
if (item != null && (index = list1.IndexOf (item)) >= 0)
selection += index.ToString ();
else
selection += "-";
labelList1Feedback.Label = selection;
labelList1Feedback.InvokeRedraw ();
};
The (invisible) default column of the XrwList
widget is created automatically during construction and the appropriate MeasureCell
, MeasureCellEditorOffset
and DrawCell
handler are connected to their fallback delegate implementations.
The labelList1Feedback
gadget is just to display the selection result in order to check the EntrySelectionChanged
delegate functionality. The gadget displays the text "Selected item(s):" followed by the selected item's index.
The next sample code shows how to create list items displaying static text only. The static text (first parameter of the XrwList.ListItem
's constructor) is used to initialize the XrwList.ListItem
's Data
property directly. All XrwList.ListItem
's are added to the XrwList
's Items
collection.
XrwList.ListItem ln1A = new XrwList.ListItem ("Item one List-item with a simple text only.",
null, false, null, false);
list1.Items.Add (ln1A);
XrwList.ListItem ln1B = new XrwList.ListItem ("Item two List-item with a simple text only.",
null, false, null, false);
list1.Items.Add (ln1B);
XrwList.ListItem ln1C = new XrwList.ListItem ("Item three List-item with a simple text only.",
null, false, null, false);
list1.Items.Add (ln1C);
XrwList.ListItem ln1D = new XrwList.ListItem ("Item four List-item with a simple text only.",
null, false, null, false);
list1.Items.Add (ln1D);
XrwList.ListItem ln1E = new XrwList.ListItem ("Item fife List-item with a simple text only.",
null, false, null, false);
list1.Items.Add (ln1E);
XrwList.ListItem ln1F = new XrwList.ListItem ("Item six List-item with a simple text only.",
null, false, null, false);
list1.Items.Add (ln1F);
...
That's all to show a simple list like this:
Large icon view, small icon view and list view with scroll
The next more advanved implementation of XrwList
is to use it with surrounding XrwViewport
and to display icon and static text per list item.
The next sample code shows how to define the viewport and list widgets (with the minimum of recommended calls) for a large icon view and to connect the EntrySelectionChanged
event's anonymous delegate. The delegate now supports single and multi selection.
XrwViewport viewportList2 = XrwViewport.NewViewportGadget (parent);
viewportList2.ExpandToAvailableWidth = true;
viewportList2.ExpandToAvailableHeight = true;
viewportList2.ForceBars = false;
parent.AddChild (viewportList2);
XrwList list2 = XrwList.NewListWidget (viewportList2);
list2.ExpandToAvailableHeight = true;
list2.ExpandToAvailableWidth = true;
list2.View = ViewType.LargeIcon;
viewportList2.AddChild (list2);
list2.EntrySelectionChanged += delegate(XrwRectObj source, XrwList.ListItem entry)
{
if (list2.MultiSelect == true)
{
List<IGridViewElement> items = list2.SelectedItems ();
string selection = "Selected item(s): ";
bool firstMatch = true;
foreach (IGridViewElement item in items)
{
int index = list2.IndexOf (item);
if (index >= 0)
{
selection += (firstMatch ? index.ToString () : ", " + index.ToString ());
firstMatch = false;
}
}
if (items.Count == 0)
selection += "-";
labelList2Feedback.Label = selection;
labelList2Feedback.InvokeRedraw ();
}
else
{
IGridViewElement item = list2.SelectedItem ();
string selection = "Selected item(s): ";
int index = -1;
if (item != null && (index = list2.IndexOf (item)) >= 0)
selection += index.ToString ();
else
selection += "-";
labelList2Feedback.Label = selection;
labelList2Feedback.InvokeRedraw ();
}
};
The listView2.View = ViewType.LargeIcon
sets the view type to large icon view.
The labelList2Feedback
gadget is just to display the selection result in order to check the EntrySelectionChanged
delegate functionality. The gadget displays the text "Selected item(s):" followed by the list if all selected item's indices, delimited by ",".
The next sample code shows how to manipulate the viewport and list behaviour. The anonymous delegates are connected to three radio buttons of one radio group and one toggle button. They realize the switch between auto displayed scroll bars, always displayed scroll bars and alwasy hidden scroll bars as well as between single selection and multi selection.
radioList2ScrollAuto.SwitchedOn += delegate(XrwRectObj source)
{
viewportList2.ForceBars = false;
viewportList2.AllowHoriz = true;
viewportList2.AllowVert = true;
viewportList2.CalculateChildLayout (viewportList2.AssignedPosition, viewportList2.AssignedSize);
};
radioList2ScrollAlways.SwitchedOn += delegate(XrwRectObj source)
{
viewportList2.ForceBars = true;
viewportList2.AllowHoriz = true;
viewportList2.AllowVert = true;
viewportList2.CalculateChildLayout (viewportList2.AssignedPosition, viewportList2.AssignedSize);
};
radioList2ScrollOff.SwitchedOn += delegate(XrwRectObj source)
{
viewportList2.ForceBars = false;
viewportList2.AllowHoriz = false;
viewportList2.AllowVert = false;
viewportList2.CalculateChildLayout (viewportList2.AssignedPosition, viewportList2.AssignedSize);
};
toggleMultiselect2.SwitchedOn += delegate(XrwRectObj source)
{
list2.MultiSelect = true;
};
toggleMultiselect2.SwitchedOff += delegate(XrwRectObj source)
{
list2.MultiSelect = false;
};
If multi selection capability is switched on, the selected list items must be determined using the XrwList.SelectedItems()
method instead of the XrwList.SelectedItem()
. To distinguish whether the multi selection capability is switched on, XrwList.MultiSelect
property can be used.
The next sample code shows how to create list items displaying large icon and static text per list item. The static text (first parameter of the XrwList.ListItem
's constructor) is used to initialize the XrwList.ListItem
's Data
property directly.
X11Graphic informationLargeGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
X11Graphic.StockIcon.Information32);
X11Graphic questionLargeGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
X11Graphic.StockIcon.Question32);
XrwList.ListItem ln2A = new XrwList.ListItem ("Item one", informationLargeGraphic,
true, null, false);
list2.Items.Add (ln2A);
XrwList.ListItem ln2B = new XrwList.ListItem ("Item two", questionLargeGraphic,
true, null, false);
list2.Items.Add (ln2B);
XrwList.ListItem ln2C = new XrwList.ListItem ("Item three", informationLargeGraphic,
true, null, false);
list2.Items.Add (ln2C);
XrwList.ListItem ln2D = new XrwList.ListItem ("Item four", questionLargeGraphic,
true, null, false);
list2.Items.Add (ln2D);
XrwList.ListItem ln2E = new XrwList.ListItem ("Item fife", informationLargeGraphic,
true, null, false);
list2.Items.Add (ln2E);
XrwList.ListItem ln2F = new XrwList.ListItem ("Item six", questionLargeGraphic,
true, null, false);
list2.Items.Add (ln2F);
...
This is what the more advanved large icon view list looks like:
The code to define the viewport and list widgets for a small icon view and to connect the EntrySelectionChanged
event's anonymous delegate is very similar to that one for large icon view.
The only difference is that XrwList.View
property is set to ViewType.SmallIcon
.
The code to manipulate the viewport behaviour is very similar to that one for large icon view and omitted here.
The next sample code shows how to create list items displaying small icon and static text per list item. The static text (first parameter of the XrwList.ListItem
's constructor) is used to initialize the XrwList.ListItem
's Data
property directly.
X11Graphic informationSmallGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
X11Graphic.StockIcon.Information16);
X11Graphic questionSmallGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
X11Graphic.StockIcon.Question16);
XrwList.ListItem ln3A = new XrwList.ListItem ("Item one", null, false,
informationSmallGraphic, true);
list3.Items.Add (ln3A);
XrwList.ListItem ln3B = new XrwList.ListItem ("Item two", null, false,
questionSmallGraphic, true);
list3.Items.Add (ln3B);
XrwList.ListItem ln3C = new XrwList.ListItem ("Item three", null, false,
informationSmallGraphic, true);
list3.Items.Add (ln3C);
XrwList.ListItem ln3D = new XrwList.ListItem ("Item four", null, false,
questionSmallGraphic, true);
list3.Items.Add (ln3D);
XrwList.ListItem ln3E = new XrwList.ListItem ("Item fife", null, false,
informationSmallGraphic, true);
list3.Items.Add (ln3E);
XrwList.ListItem ln3F = new XrwList.ListItem ("Item six", null, false,
questionSmallGraphic, true);
list3.Items.Add (ln3F);
...
This is what the more advanved small icon view list looks like:
The main difference between the small icon view and list view display of the list is the listView4.View = ViewType.List
property value.
And this is what themore advanved large list view list looks like:
Viewport scroll and single / multi selection
The large icon view, small icon view and list view samples provide a radio group to switch the XrwList
's surrounding XrwViewport
scroll capabilities between automatic scrollbar display, always forced scrollbar display and always hidden scroll bars. The code to connect the SwitchedOn
event's anonymous delegate has already been shown for the large icon sample:
radioList2ScrollAuto.SwitchedOn += delegate(XrwRectObj source)
{
viewportList2.ForceBars = false;
viewportList2.AllowHoriz = true;
viewportList2.AllowVert = true;
viewportList2.CalculateChildLayout (viewportList2.AssignedPosition, viewportList2.AssignedSize);
};
radioList2ScrollAlways.SwitchedOn += delegate(XrwRectObj source)
{
viewportList2.ForceBars = true;
viewportList2.AllowHoriz = true;
viewportList2.AllowVert = true;
viewportList2.CalculateChildLayout (viewportList2.AssignedPosition, viewportList2.AssignedSize);
};
radioList2ScrollOff.SwitchedOn += delegate(XrwRectObj source)
{
viewportList2.ForceBars = false;
viewportList2.AllowHoriz = false;
viewportList2.AllowVert = false;
viewportList2.CalculateChildLayout (viewportList2.AssignedPosition, viewportList2.AssignedSize);
};
The large icon view, small icon view and list view samples provide as well a toggle to switch the XrwList
's multi selection capability on or off. The code to connect the SwitchedOn
and SwitchedOff
event's anonymous delegate has already been shown for the large icon sample:
toggleMultiselect2.SwitchedOn += delegate(XrwRectObj source)
{
list2.MultiSelect = true;
};
toggleMultiselect2.SwitchedOff += delegate(XrwRectObj source)
{
list2.MultiSelect = false;
};
Column resizing and reordering
Multi column list views, that display column headers, can resize and reorder their columns. To use these features, the XrwViewport
's HeaderVisibility
property must be set to Visibility.Visible
and the XrwViewport
's HeaderPreferredHeight
property must be set to an appropriate value. Column resizing is always enabled. To enable column reordering, the XrwViewport
contained XrwViewportGridViewHeader
's ColumnDrag
property must be set to either ColumnDragType.HeaderAnimated
or ColumnDragType.FullAnimated
.
The sample code shows how to create list items for the multi column list. Now the first parameter of the XrwList.ListItem
's constructor is not a static text but a GridViewNodeData
instance. The GridViewNodeData
is a sample implementation of a data object, that is prepared for the use with XrwList
/XrwTree
. The main aspect of the preparation is the provision of public properties, that can be bound to XrwList
/XrwTree
columns.
XrwList.ListItem ln1A = new XrwList.ListItem (new GridViewNodeData ("Item one. Multi-column with a small " +
"icon.", true, ThreeState.on, TArrowOrientation.Up, "Item one."),
null, false, informationSmallGraphic, true);
ln1A.Editable = true;
list1.Items.Add (ln1A);
XrwList.ListItem ln1B = new XrwList.ListItem (new GridViewNodeData ("Item two. Multi-column with a small " +
"icon.", false, ThreeState.off, TArrowOrientation.Down, "Item two."),
null, false, questionSmallGraphic, true);
ln1B.Editable = true;
list1.Items.Add (ln1B);
XrwList.ListItem ln1C = new XrwList.ListItem (new GridViewNodeData ("Item three. Multi-column with a small " +
"icon.", true, ThreeState.unset, TArrowOrientation.Left, "Item three."),
null, false, informationSmallGraphic, true);
ln1C.Editable = true;
list1.Items.Add (ln1C);
XrwList.ListItem ln1D = new XrwList.ListItem (new GridViewNodeData ("Item four. Multi-column with a small " +
"icon.", false, ThreeState.on, TArrowOrientation.Right, "Item four."),
null, false, questionSmallGraphic, true);
ln1D.Editable = true;
list1.Items.Add (ln1D);
XrwList.ListItem ln1E = new XrwList.ListItem (new GridViewNodeData ("Item fife. Multi-column with a small " +
"icon.", true, ThreeState.off, TArrowOrientation.Up, "Item fife."),
null, false, informationSmallGraphic, true);
ln1E.Editable = true;
list1.Items.Add (ln1E);
XrwList.ListItem ln1F = new XrwList.ListItem (new GridViewNodeData ("Item six. Multi-column with a small " +
"icon.", false, ThreeState.unset, TArrowOrientation.Down, "Item six."),
null, false, questionSmallGraphic, true);
ln1F.Editable = true;
list1.Items.Add (ln1F);
XrwList.ListItem ln1G = new XrwList.ListItem (new GridViewNodeData ("Item seven. Multi-column with a small " +
"icon.", true, ThreeState.on, TArrowOrientation.Left, "Item seven."),
null, false, informationSmallGraphic, true);
ln1G.Editable = true;
...
The next sample code shows how to adopt the default column (Columns[0]
) and to define further columns suitable for the data types used by the list. The column data binding is done according to GridViewNodeData
's properties Name
, Col2
, Col3
, Col4
and Col5
.
list1.Columns[0].DisplayMemberBinding = new XrwBinding ("Name");
list1.Columns[0].Header = new XrwGridViewColumnHeader ("Name");
list1.Columns[0].Editable = true;
XrwGridViewColumn col2 = new XrwGridViewColumn (XrwList.MeasureCommonCell,
XrwList.MeasureCommonCellEditorOffset, XrwList.DrawCommonCell);
col2.DisplayMemberBinding = new XrwBinding ("Col2");
col2.Header = new XrwGridViewColumnHeader ("Boolean");
col2.Header.TextColor = list1.TextColor;
col2.Editable = true;
list1.Columns.Add (col2);
XrwGridViewColumn col3 = new XrwGridViewColumn (XrwList.MeasureCommonCell,
XrwList.MeasureCommonCellEditorOffset, XrwList.DrawCommonCell);
col3.DisplayMemberBinding = new XrwBinding ("Col3");
col3.Header = new XrwGridViewColumnHeader ("ThreeState");
col3.Header.TextColor = list1.TextColor;
col3.Editable = true;
list1.Columns.Add (col3);
XrwGridViewColumn col4 = new XrwGridViewColumn (XrwList.MeasureCommonCell,
XrwList.MeasureCommonCellEditorOffset, XrwList.DrawCommonCell);
col4.DisplayMemberBinding = new XrwBinding ("Col4");
col4.Header = new XrwGridViewColumnHeader ("Enum");
col4.Header.TextColor = list1.TextColor;
col4.Editable = true;
list1.Columns.Add (col4);
XrwGridViewColumn col5 = new XrwGridViewColumn (XrwList.MeasureCommonCell,
XrwList.MeasureCommonCellEditorOffset, XrwList.DrawCommonCell);
col5.DisplayMemberBinding = new XrwBinding ("Col5");
col5.Header = new XrwGridViewColumnHeader ("Text");
col5.Header.TextColor = list1.TextColor;
col5.Editable = true;
list1.Columns.Add (col5);
A column creation always registers three handler,
- one to calculate a cell's required size,
- one to calculate a cell's editor offset and
- one to draw the cell's content.
The default list column (Columns[0]
) is always created registering the XrwList.MeasureCommonCell()
, the XrwList.MeasureCommonCellEditorOffset()
and the XrwList.DrawCommonCell()
delegates to the handler. Because the columns 2 to 5 use data types, that have built-in support (boolean, tree state, enum, and text) for cell measurement, editor offset measurement and drawing, all of them register the XrwList.MeasureCommonCell()
, the XrwList.MeasureCommonCellEditorOffset()
and the XrwList.DrawCommonCell()
delegates to the handler as well.
If there are inidividual data types used for the data object's properties, bound to columns, it might be required to register individual delegats to do cell measurement, editor offset measurement and drawing.
The next sample code shows how to manipulate the viewport and list behaviour. The anonymous delegates are connected to three radio buttons of one radio group and one toggle button. They realize the switch between disabled column dragging, header animated column dragging and full animated header dragging as well as between single selection and multi selection.
radioList1DragNever.SwitchedOn += delegate(XrwRectObj source)
{
if (viewportList1.Header != null)
viewportList1.Header.ColumnDrag = ColumnDragType.Never;
};
radioList1DragHeaderUpdate.SwitchedOn += delegate(XrwRectObj source)
{
if (viewportList1.Header != null)
viewportList1.Header.ColumnDrag = ColumnDragType.HeaderAninated;
};
radioList1DragFullUpdate.SwitchedOn += delegate(XrwRectObj source)
{
if (viewportList1.Header != null)
viewportList1.Header.ColumnDrag = ColumnDragType.FullAnimated;
};
toggleMultiselect1.SwitchedOn += delegate(XrwRectObj source)
{
list1.MultiSelect = true;
};
toggleMultiselect1.SwitchedOff += delegate(XrwRectObj source)
{
list1.MultiSelect = false;
};
This is what the most advanved multi column detail view list looks like:
To use column resizing, the XrwViewport
contained XrwViewportGridViewHeader
widget must be shown (XrwViewpor.HeaderVisibility
is set to Visibility.Visible
).
The readyness to resize a column is indicated visually to the user by a cursor change to a horizontal double arrow at every column's right delimiter.
Currently the sensitive area to resize a column is in the range of 5 pixels left and right to a column's right delimiter.
To resize a column, the horizontal double arrow cursor must be dragged left or right and dropped at the target position.
To use column reordering, a multi column list is required and the XrwViewport
contained XrwViewportGridViewHeader
widget's ColumnDrag
property must be set to either ColumnDragType.HeaderAnimated
or ColumnDragType.FullAnimated
.
If the XrwViewport
contained XrwViewportGridViewHeader
widget's ColumnDrag
property is set to ColumnDragType.Never
, outside the sensitive area to resize a column the cursor changes to an arrow.
If the XrwViewport
contained XrwViewportGridViewHeader
widget's ColumnDrag
property is set to either ColumnDragType.HeaderAnimated
or ColumnDragType.FullAnimated
, outside the sensitive area to resize a column the cursor changes to hand 2 and indicates it's readyness to reorder a column visually to the user.
Currently the sensitive area to place a column to reorder is in the range of 5 to 15 pixels left and right to its column delimiters.
To reorder a column, the hand 2 cursor must be dragged left or right and dropped at the target position. If the hand 2 cursor is moved into the sensitive area to reorder a column, either the column header moves to the new order position (ColumnDragType.HeaderAnimated
is set) or column header and list column content move to the new order position (ColumnDragType.FullAnimated
is set).
The radio buttons no drag, header update and full update switch between the ColumnDragType.Never
, ColumnDragType.HeaderAnimated
and ColumnDragType.FullAnimated
behaviour.
XrwTree samples
Just text, no scroll
The most simple implementation of XrwTree
is to use it without surrounding XrwViewport
and to display static text only. This approach, to omit a surrounding XrwViewport
, is not recommended - except the minimum size of the XrwTree
guarantees the complete display of all tree nodes.
The sample code shows how to define the tree widget (with the minimum of recommended calls) and to connect the EntrySelectionChanged
event's anonymous delegate.
XrwTree tree1 = XrwTree.NewTreeWidget (parent);
tree1.ExpandToAvailableHeight = true;
tree1.ExpandToAvailableWidth = true;
parent.AddChild (tree1);
tree1.EntrySelectionChanged += delegate(XrwRectObj source, List<IGridViewElement> oldEntries,
IGridViewElement newEntry)
{
IGridViewElement node = tree1.SelectedNode ();
string selection = "Selected node path: ";
if (node != null)
selection += tree1.PathOf (node);
else
selection += "-";
labelTree1Feedback.Label = selection;
labelTree1Feedback.InvokeRedraw ();
};
The (invisible) default column of the XrwTree
widget is created automatically during construction and the appropriate MeasureCell
, MeasureCellEditorOffset
and DrawCell
handler are connected to their fallback implementation delegates.
The labelTree1Feedback
gadget is just to display the selection result in order to check the EntrySelectionChanged
delegate functionality. The gadget displays the text "Selected node path:" followed by the path of the selected node. The path of a node is the concatenation of the involved node's indices starting with the root node and ending with the leaf node, delimited by ":".
The next sample code shows how to create tree nodes displaying static text only. The static text (first parameter of the XrwTree.TreeNode
's constructor) is used to initialize the XrwTree.TreeNode
's Data
property directly. Some tree nodes have child nodes. All root nodes are added to the XrwTree
's RootNodes
collection. All child nodes are added to their parent XrwTree.TreeNode
's Nodes
collection.
XrwTree.TreeNode tn1A = new XrwTree.TreeNode ("Tree-node 1 with simple text only. Has two detail levels.",
null, false, null, false);
tree1.RootNodes.Add (tn1A);
XrwTree.TreeNode tn1A1 = new XrwTree.TreeNode ("Tree-node 1.1 with simple text only. Has one detail level.",
null, false, null, false);
tn1A.Nodes.Add (tn1A1);
XrwTree.TreeNode tn1A11 = new XrwTree.TreeNode ("Tree-node 1.1.1 with simple text only. Has no detail level.",
null, false, null, false);
tn1A1.Nodes.Add (tn1A11);
XrwTree.TreeNode tn1A2 = new XrwTree.TreeNode ("Tree-node 1.2 with simple text only. Has one detail level.",
null, false, null, false);
tn1A.Nodes.Add (tn1A2);
XrwTree.TreeNode tn1A21 = new XrwTree.TreeNode ("Tree-node 1.2.1 with simple text only. Has no detail level.",
null, false, null, false);
tn1A2.Nodes.Add (tn1A21);
XrwTree.TreeNode tn1B = new XrwTree.TreeNode ("Tree-node 2 with simple text only. Has one detail level.",
null, false, null, false);
tree1.RootNodes.Add (tn1B);
XrwTree.TreeNode tn1B1 = new XrwTree.TreeNode ("Tree-node 2.1 with simple text only. Has no detail level.",
null, false, null, false);
tn1B.Nodes.Add (tn1B1);
XrwTree.TreeNode tn1B2 = new XrwTree.TreeNode ("Tree-node 2.2 with simple text only. Has no detail level.",
null, false, null, false);
tn1B.Nodes.Add (tn1B2);
XrwTree.TreeNode tn1C = new XrwTree.TreeNode ("Tree-node 3 with simple text only. Has one detail level.",
null, false, null, false);
tree1.RootNodes.Add (tn1C);
...
That's all to show a simple tree like this:
Icon and expander with scroll
The next more advanved implementation of XrwTree
is to use it with surrounding XrwViewport
and to display icon and expander with static text per tree node.
The next sample code shows how to define the viewport and tree widget (with the minimum of recommended calls) to display a small icon and expander. The code to connect the EntrySelectionChanged
delegate is the same as in the previous sample.
XrwViewport viewportTree2 = XrwViewport.NewViewportGadget (parent);
viewportTree2.ExpandToAvailableWidth = true;
viewportTree2.ExpandToAvailableHeight = true;
viewportTree2.ForceBars = false;
parent.AddChild (viewportTree2);
XrwTree tree2 = XrwTree.NewTreeWidget (viewportTree2);
tree2.ExpandToAvailableHeight = true;
tree2.ExpandToAvailableWidth = true;
tree2.ShowExpander = true;
viewportTree2.AddChild (tree2);
tree2.EntrySelectionChanged += delegate(XrwRectObj source, List<IGridViewElement> oldEntries,
IGridViewElement newEntry)
{
IGridViewElement item = tree2.SelectedNode ();
string selection = "Selected item path: ";
if (item != null)
selection += tree2.PathOf (item);
else
selection += "-";
labelTree2Feedback.Label = selection;
labelTree2Feedback.InvokeRedraw ();
};
The labelTree2Feedback
gadget is just to display the selection result in order to check the EntrySelectionChanged
delegate functionality. The gadget displays the text "Selected node path:" followed by the path of the selected node. The path of a node is the concatenation of the involved node's indices starting with the root node and ending with the selected node, delimited by ":".
The next sample code shows how to manipulate the viewport and tree behaviour. The delegates are connected to three radio buttons of one radio group and one toggle button. They realize the switch between auto displayed scroll bars, always displayed scroll bars and alwasy hidden scroll bars as well as between displayed expander and hidden expander.
radioTree2ScrollAuto.SwitchedOn += delegate(XrwRectObj source)
{
viewportTree2.ForceBars = false;
viewportTree2.AllowHoriz = true;
viewportTree2.AllowVert = true;
viewportTree2.CalculateChildLayout (viewportTree2.AssignedPosition, viewportTree2.AssignedSize);
};
radioTree2ScrollAlways.SwitchedOn += delegate(XrwRectObj source)
{
viewportTree2.ForceBars = true;
viewportTree2.AllowHoriz = true;
viewportTree2.AllowVert = true;
viewportTree2.CalculateChildLayout (viewportTree2.AssignedPosition, viewportTree2.AssignedSize);
};
radioTree2ScrollOff.SwitchedOn += delegate(XrwRectObj source)
{
viewportTree2.ForceBars = false;
viewportTree2.AllowHoriz = false;
viewportTree2.AllowVert = false;
viewportTree2.CalculateChildLayout (viewportTree2.AssignedPosition, viewportTree2.AssignedSize);
};
toggleTree2ShowExpander.SwitchedOn += delegate(XrwRectObj source)
{
tree2.ShowExpander = true;
tree2.InvokeRedraw ();
};
toggleTree2ShowExpander.SwitchedOff += delegate(XrwRectObj source)
{
tree2.ShowExpander = false;
tree2.InvokeRedraw ();
};
The next sample code shows how to create tree nodes displaying small icon and static text per tree node. The static text (first parameter of the XrwTree.TreeNode
's constructor) is used to initialize the XrwTree.TreeNode
's Data
property directly. Again some tree nodes have child nodes. All root nodes are added to the XrwTree
's RootNodes
collection. All child nodes are added to their parent XrwTree.TreeNode
's Nodes
collection.
X11Graphic informationSmallGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
X11Graphic.StockIcon.Information16);
X11Graphic questionSmallGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
X11Graphic.StockIcon.Question16);
X11Graphic attentionSmallGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
X11Graphic.StockIcon.Attention16);
XrwTree.TreeNode tn2A = new XrwTree.TreeNode ("Tree-node 1.", null, false, informationSmallGraphic, true);
tn2A.Editable = true;
tree2.RootNodes.Add (tn2A);
XrwTree.TreeNode tn2A1 = new XrwTree.TreeNode ("Tree-node 1.1 .", null, false, questionSmallGraphic, true);
tn2A1.Editable = true;
tn2A.Nodes.Add (tn2A1);
XrwTree.TreeNode tn2A11 = new XrwTree.TreeNode ("Tree-node 1.1.1.", null, false, attentionSmallGraphic, true);
tn2A11.Editable = true;
tn2A1.Nodes.Add (tn2A11);
XrwTree.TreeNode tn2A2 = new XrwTree.TreeNode ("Tree-node 1.2 .", null, false, questionSmallGraphic, true);
tn2A2.Editable = true;
tn2A.Nodes.Add (tn2A2);
XrwTree.TreeNode tn2A21 = new XrwTree.TreeNode ("Tree-node 1.2.1.", null, false, attentionSmallGraphic, true);
tn2A21.Editable = true;
tn2A2.Nodes.Add (tn2A21);
XrwTree.TreeNode tn2B = new XrwTree.TreeNode ("Tree-node 2.", null, false, informationSmallGraphic, true);
tn2B.Editable = true;
tree2.RootNodes.Add (tn2B);
XrwTree.TreeNode tn2B1 = new XrwTree.TreeNode ("Tree-node 2.1.", null, false, questionSmallGraphic, true);
tn2B1.Editable = true;
tn2B.Nodes.Add (tn2B1);
XrwTree.TreeNode tn2B2 = new XrwTree.TreeNode ("Tree-node 2.2.", null, false, questionSmallGraphic, true);
tn2B2.Editable = true;
tn2B.Nodes.Add (tn2B2);
XrwTree.TreeNode tn2C = new XrwTree.TreeNode ("Tree-node 3 .", null, false, informationSmallGraphic, true);
tn2C.Editable = true;
tree2.RootNodes.Add (tn2C);
...
This is what the more advanved icon and expander tree looks like:
Column resizing and reordering
Multi column tree views, that display column headers, can resize and reorder their columns. To use these features, the XrwViewport
's HeaderVisibility
property must be set to Visibility.Visible
and the XrwViewport
's HeaderPreferredHeight
property must be set to an appropriate value. Column resizing is always enabled. To enable column reordering, the XrwViewport
contained XrwViewportGridViewHeader
's ColumnDrag
property must be set to either ColumnDragType.HeaderAnimated
or ColumnDragType.FullAnimated
.
The sample code shows how to create treee nodes for the multi column tree. Now the first parameter of the XrwTree.TreeNode
's constructor is not a static text but a GridViewNodeData
instance. The GridViewNodeData
is a sample implementation of a data object, that is prepared for the use with XrwList
/XrwTree
. The main aspect of the preparation is the provision of public properties, that can be bound to XrwList
/XrwTree
columns.
XrwTree.TreeNode tn3A = new XrwTree.TreeNode (new GridViewNodeData ("Tree-node 1 with multiple columns. " +
"Has two detail levels.", true, ThreeState.on,
TArrowOrientation.Up,
"Node 1."), null, false, informationSmallGraphic, true);
tn3A.Editable = true;
tree3.RootNodes.Add (tn3A);
XrwTree.TreeNode tn3A1 = new XrwTree.TreeNode (new GridViewNodeData ("Tree-node 1.1 with multiple columns. " +
"Has one detail level.", false, ThreeState.off,
TArrowOrientation.Down,
"Node 1.1."), null, false, questionSmallGraphic, true);
tn3A1.Editable = true;
tn3A.Nodes.Add (tn3A1);
XrwTree.TreeNode tn3A11 = new XrwTree.TreeNode (new GridViewNodeData ("Tree-node 1.1.1 with multiple columns. " +
"Has no detail level.", true, ThreeState.unset,
TArrowOrientation.Left,
"Node 1.1.1."), null, false, attentionSmallGraphic, true);
tn3A11.Editable = true;
tn3A1.Nodes.Add (tn3A11);
XrwTree.TreeNode tn3A2 = new XrwTree.TreeNode (new GridViewNodeData ("Tree-node 1.2 with multiple columns. " +
"Has one detail level.", false, ThreeState.on,
TArrowOrientation.Right,
"Node 1.2."), null, false, questionSmallGraphic, true);
tn3A2.Editable = true;
tn3A.Nodes.Add (tn3A2);
XrwTree.TreeNode tn3A21 = new XrwTree.TreeNode (new GridViewNodeData ("Tree-node 1.2.1 with multiple columns. " +
"Has no detail level.", true, ThreeState.off,
TArrowOrientation.Up,
"Node 1.2.1."), null, false, attentionSmallGraphic, true);
tn3A21.Editable = true;
tn3A2.Nodes.Add (tn3A21);
XrwTree.TreeNode tn3B = new XrwTree.TreeNode (new GridViewNodeData ("Tree-node 2 with multiple columns. " +
"Has one detail level.", false, ThreeState.unset,
TArrowOrientation.Down,
"Node 2."), null, false, informationSmallGraphic, true);
tn3B.Editable = true;
tree3.RootNodes.Add (tn3B);
XrwTree.TreeNode tn3B1 = new XrwTree.TreeNode (new GridViewNodeData ("Tree-node 2.1 with multiple columns. " +
"Has no detail level.", true, ThreeState.on,
TArrowOrientation.Left,
"Node 2.1."), null, false, questionSmallGraphic, true);
tn3B1.Editable = true;
tn3B.Nodes.Add (tn3B1);
XrwTree.TreeNode tn3B2 = new XrwTree.TreeNode (new GridViewNodeData ("Tree-node 2.2 with multiple columns. " +
"Has no detail level.", false, ThreeState.off,
TArrowOrientation.Right,
"Node 2.2."), null, false, questionSmallGraphic, true);
tn3B2.Editable = true;
tn3B.Nodes.Add (tn3B2);
XrwTree.TreeNode tn3C = new XrwTree.TreeNode (new GridViewNodeData ("Tree-node 3 with multiple columns. " +
"Has one detail level.", true, ThreeState.unset,
TArrowOrientation.Up,
"Node 3."), null, false, informationSmallGraphic, true);
tn3C.Editable = true;
tree3.RootNodes.Add (tn3C);
...
The next sample code shows how to adopt the default column (Columns[0]
) and to define further columns suitable for the data types used by the tree. The column data binding is done according to GridViewNodeData
's properties Name
, Col2
, Col3
, Col4
and Col5
.
tree3.Columns[0].DisplayMemberBinding = new XrwBinding ("Name");
tree3.Columns[0].Header = new XrwGridViewColumnHeader ("Name");
tree3.Columns[0].Editable = true;
XrwGridViewColumn col2 = new XrwGridViewColumn (XrwTree.MeasureCommonCell,
XrwTree.MeasureCommonCellEditorOffset, XrwTree.DrawCommonCell);
col2.DisplayMemberBinding = new XrwBinding ("Col2");
col2.Header = new XrwGridViewColumnHeader ("Boolean");
col2.Header.TextColor = tree3.TextColor;
col2.Editable = true;
tree3.Columns.Add (col2);
XrwGridViewColumn col3 = new XrwGridViewColumn (XrwTree.MeasureCommonCell,
XrwTree.MeasureCommonCellEditorOffset, XrwTree.DrawCommonCell);
col3.DisplayMemberBinding = new XrwBinding ("Col3");
col3.Header = new XrwGridViewColumnHeader ("ThreeState");
col3.Header.TextColor = tree3.TextColor;
col3.Editable = true;
tree3.Columns.Add (col3);
XrwGridViewColumn col4 = new XrwGridViewColumn (XrwTree.MeasureCommonCell,
XrwTree.MeasureCommonCellEditorOffset, XrwTree.DrawCommonCell);
col4.DisplayMemberBinding = new XrwBinding ("Col4");
col4.Header = new XrwGridViewColumnHeader ("Enum");
col4.Header.TextColor = tree3.TextColor;
col4.Editable = true;
tree3.Columns.Add (col4);
XrwGridViewColumn col5 = new XrwGridViewColumn (XrwTree.MeasureCommonCell,
XrwTree.MeasureCommonCellEditorOffset, XrwTree.DrawCommonCell);
col5.DisplayMemberBinding = new XrwBinding ("Col5");
col5.Header = new XrwGridViewColumnHeader ("Text");
col5.Header.TextColor = tree3.TextColor;
col5.Editable = true;
tree3.Columns.Add (col5);
A column creation always registers three delegates,
- one to calculate a cell's required size,
- one to calculate a cell's editor offset and
- one to draw the cell's content.
The default tree column (Columns[0]
) is always created registering the XrwTree.MeasureCommonCell()
, the XrwTree.MeasureCommonCellEditorOffset()
and the XrwTree.DrawCommonCell()
default delegate implementations to the MeasureCell
, MeasureCellEditorOffset
and DrawCell
handler. Because the columns 2 to 5 use data types, that have built-in support (boolean, tree state, enum, and text) for cell measurement, editor offset measurement and drawing, all of them register the XrwTree.MeasureCommonCell()
, the XrwTree.MeasureCommonCellEditorOffset()
and the XrwTree.DrawCommonCell()
default delegate implementations as well.
If there are inidividual data types used for the data object's properties, bound to columns, it might be required to register individual delegats to do cell measurement, editor offset measurement and drawing.
The next sample code shows how to manipulate the viewport and tree behaviour. The delegates are connected to three radio buttons of one radio group and one toggle button. They realize the switch between disabled column dragging, header animated column dragging and full animated header dragging as well as between displayed expander and hidden expander.
radioTree3DragNever.SwitchedOn += delegate(XrwRectObj source)
{
if (viewportTree3.Header != null)
viewportTree3.Header.ColumnDrag = ColumnDragType.Never;
};
radioTree3DragHeaderUpdate.SwitchedOn += delegate(XrwRectObj source)
{
if (viewportTree3.Header != null)
viewportTree3.Header.ColumnDrag = ColumnDragType.HeaderAninated;
};
radioTree3DragFullUpdate.SwitchedOn += delegate(XrwRectObj source)
{
if (viewportTree3.Header != null)
viewportTree3.Header.ColumnDrag = ColumnDragType.FullAnimated;
};
toggleTree3ShowExpander.SwitchedOn += delegate(XrwRectObj source)
{
tree3.ShowExpander = true;
tree3.InvokeRedraw ();
};
toggleTree3ShowExpander.SwitchedOff += delegate(XrwRectObj source)
{
tree3.ShowExpander = false;
tree3.InvokeRedraw ();
};
This is what the most advanved multi column tree looks like:
The column resizing and column reordering works exactly as descibed for the multi column list sample.
Click behaviour, command invocation and in-place editing
The XrwList
widget
- selects a list item on a single click,
- provides multi selection for ranges (with [Shift] key pressed) or noncoherent items (with [Ctrl] key pressed),
- provides the possibility to invoke a delegate on a list item's double click and
- provides the possibility to invoke in-place editing on a list items's second click.
The XrwTree
widget
- selects a tree none on a single click,
- collapses/expands a tree node on double click or on single click at a tree node's expander,
- provides the possibility to invoke a delegate on a tree node's double click and,
- provides the possibility to invoke in-place editing on a tree node's second click.
For XrwList
and XrwTree
widget the double click event must have a delegate registered to XrwList.ItemCommandInvoke
or XrwTree.NodeCommandInvoke
. This feature is rarely used and not demonstrated by the sample application.
The multi selection capabilities are demonstrated by
- the more advanved
XrwList
examples showing the large icon view,
- small icon view and
- list view as well as by
- the
XrwList
multi column list view sample 'Advanved list test'.
For multi selection the MultiSelect
property must be set to true
.
The in-place editing capabilities are demonstrated by
- the
XrwList
multi column list view sample 'Advanved list test' and
- the
XrwTree
multi column tree view sample 'Advanced tree test'.
For in-place editing, the XrwList.
Editable
or XrwTree.
Editable
property must be enabled explicitly for every column and every list item/tree node because it is set false
by default.
The demand to explicitly enable editing for every column and every list item/tree node offers a very accurate control.
The next sample code shows how to achive this for XrwList
(for XrwTree
the calls are identical):
...
ln1A.Editable = true;
...
ln1B.Editable = true;
...
...
list1.Columns[0].Editable = true;
...
col2.Editable = true;
...
In-place editing is currently built-in supported for columns, that display
- single line text (
System.String
),
- boolean (
System.Boolean
),
- three-state (
System.ThreeState
) and
- enumerations (
System.Enum
).
The in-place editing is triggered by a second click on an already selected list item/tree node within the column to edit.
The XrwList
.HandleButtonReleaseDefault
preregistered ButtonRelease
event's delegate call XrwList
.HandleEditRequest
subsequently to invoke in-place editing for the selected list item if the prerequisites are met.
The XrwTree
.HandleButtonReleaseDefault
preregistered ButtonRelease
event's delegate call XrwTree
.HandleEditRequest
subsequently to invoke in-place editing for the selected tree node if the prerequisites are met.
Both HandleEditRequest
methods calculate the column to edit and for that column they either
- switch the value (for
System.Boolean
or System.ThreeState
types) or
- calculate the editor area (position and size) and call the cell editor.
Currently the determination of the required cell editor is done by XrwBaseCellEditorShell
.FindCellEditor
. This static method creates a specialized cell editor shell, suitable for the data type (System.String
or System.Enum
) of the column to edit.
Points of Interest
I wrote the sample application to create an almost complete interactive test for XrwList
and XrwTree
widgets and to demonstrate/explain the advanced features in cooperation with the XrwViewport
and XrwViewportGridViewHeader
widgets.
History
This is the initial version of the article fom 13. August 2014.