65.9K
CodeProject 正在变化。 阅读更多。
Home

释放列表和树形控件(C# X11)的强大功能

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2014年8月13日

CPOL

17分钟阅读

viewsIcon

12634

downloadIcon

175

如何在 C# 中最大限度地利用 Roma Widget Set 的列表和树形控件。

引言

本文重点介绍 Roma Widget Set (Xrw) 中的两个控件:XrwListXrwTree。Roma Widget Set 是一个用于 X11 的零依赖 GUI 应用程序框架(它只需要免费的 Mono 标准安装中的程序集和免费的 X11 发行版中的库;它不特别需要 GNOME、KDE 或商业库),并且完全用 C# 实现。

请在《使用 Roma Widget Set 编程 (C# X11) - 一个零依赖的 GUI 应用程序框架 - 第 1 部分,基础》中查找概念和一般性描述。它应始终作为首选参考来源。

关于简单控件(XrwListXrwTreeXrwViewportGridViewHeader 属于此类)的参考描述,请见《使用 Roma Widget Set 编程 (C# X11) - 一个零依赖的 GUI 应用程序框架 - 第 2 部分,简单控件》

关于复合控件(XrwViewport 属于此类)的参考描述,请见《使用 Roma Widget Set 编程 (C# X11) - 一个零依赖的 GUI 应用程序框架 - 第 3 部分,复合控件》

背景

XrwListXrwTree 控件只有在与 XrwViewportXrwViewportGridViewHeader 控件配合使用时才能发挥其全部性能。如何应用这种控件组合,是本文超出单个控件参考描述的地方。这其中包括:

  • XrwList 的文本视图、大图标视图、小图标视图、列表视图和高级(多列)视图
  • XrwList 的列表视图项选择通知
  • 对于包含 XrwListXrwListXrwViewport,自动检测显示滚动条的需求、独立于需求的始终可见或始终隐藏的滚动条
  • XrwList 的单选和多选
  • XrwTree 的文本视图和高级(多列)视图
  • XrwViewport 内使用 XrwViewportGridViewHeaderXrwListXrwTree 显示列标题
  • XrwViewport 内使用 XrwViewportGridViewHeaderXrwListXrwTree 调整列大小和重新排序
  • XrwListXrwTree 的就地编辑(包括有关编辑器调用的信息)

使用代码

示例应用程序是使用 Mono Develop 2.4.1 for Mono 2.8.1 在 OPEN SUSE 11.3 Linux 32 bit EN 和 GNOME 桌面上编写的。移植到任何更旧或更新的版本应该都不是问题。示例应用程序的解决方案包含一个项目,其中包含了所有必要的源代码。

该示例应用程序还通过了 Mono Develop 3.0.6 在 OPEN SUSE 12.3 Linux 64 位 DE 和 GNOME 桌面、IceWM、TWM 和 Xfce 上针对 Mono 3.0.4 的测试。

Xlib/X11 窗口处理基于 X11Wrapper 程序集 0.5 版本,该版本定义了对 libX11.so 进行 Xlib/X11 调用的函数原型、结构和类型。它最初是为《使用 Mono Develop 进行 Xlib 编程 - 第 1 部分:底层(概念验证)》项目开发的,并在《使用 Roma Widget Set 编程 (C# X11) - 一个零依赖的 GUI 应用程序框架 - 第 1 部分,基础》项目期间得到了发展。

该示例应用程序为 XrwListXrwTree 控件实现了多个复杂度和功能级别。

要试玩可执行文件,请在 32 位系统上启动 /bin/Debug/32/UnleshListAndTree.exe,或在 64 位系统上启动 /bin/Debug/64/UnleshListAndTree.exe。

要加载项目,请在 32 位系统上使用 UnleshListAndTree32.sln,或在 64 位系统上使用 UnleshListAndTree.sln。

让我们先从一些 XrwList 示例开始,稍后再继续讨论 XrwTree

XrwList 示例

纯文本,无滚动

XrwList 最简单的实现方式是不与 XrwViewportXrwViewportGridViewHeader 一起使用,并且只显示静态文本。不推荐这种省略外层 XrwViewport 的方法——除非 XrwList 的最小尺寸能保证完整显示所有列表项。

示例代码展示了如何定义列表控件(使用最少推荐的调用)并连接 EntrySelectionChanged 事件的匿名委托。

// Define the list.
XrwList list1    = XrwList.NewListWidget (parent);
list1.ExpandToAvailableHeight = true;
list1.ExpandToAvailableWidth  = true;
parent.AddChild (list1);

// Be notified about selection change.
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 += "-";
    // Tip --- define: labelList1Feedback.ExpandToAvailableWidth = true;
    labelList1Feedback.Label = selection;
    labelList1Feedback.InvokeRedraw ();
}; 

XrwList 控件的(不可见)默认列在构造期间自动创建,并且相应的 MeasureCellMeasureCellEditorOffsetDrawCell 处理程序会连接到它们的回退委托实现。

labelList1Feedback 小部件仅用于显示选择结果,以检查 EntrySelectionChanged 委托的功能。该小部件显示文本“Selected item(s):”后跟选中项的索引。

下一个示例代码展示了如何创建只显示静态文本的列表项。静态文本(XrwList.ListItem 构造函数的第一个参数)直接用于初始化 XrwList.ListItemData 属性。所有 XrwList.ListItem 都被添加到 XrwListItems 集合中。

// Define the list items.
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);
...

这样就足以显示一个像这样的简单列表

大图标视图、小图标视图和带滚动的列表视图

XrwList 的下一个更高级的实现是与外层的 XrwViewport 一起使用,并为每个列表项显示图标和静态文本。

下一个示例代码展示了如何为大图标视图定义视口和列表控件(使用最少推荐的调用),并连接 EntrySelectionChanged 事件的匿名委托。该委托现在支持单选和多选。

// Define the viewport.
XrwViewport viewportList2 = XrwViewport.NewViewportGadget (parent);
viewportList2.ExpandToAvailableWidth  = true;
viewportList2.ExpandToAvailableHeight = true;
viewportList2.ForceBars = false;
parent.AddChild (viewportList2);
// Define the list.
XrwList list2    = XrwList.NewListWidget (viewportList2);
list2.ExpandToAvailableHeight = true;
list2.ExpandToAvailableWidth  = true;
list2.View = ViewType.LargeIcon;
viewportList2.AddChild (list2);

// Be notified about selection change.
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 ();
    }
};

listView2.View = ViewType.LargeIcon 将视图类型设置为大图标视图。

labelList2Feedback 小部件仅用于显示选择结果,以检查 EntrySelectionChanged 委托的功能。该小部件显示文本“Selected item(s):”后跟所有选中项索引的列表,以“,”分隔。

下一个示例代码展示了如何操作视口和列表的行为。匿名委托连接到一个单选组的三个单选按钮和一个切换按钮。它们实现了在自动显示滚动条、始终显示滚动条和始终隐藏滚动条之间切换,以及在单选和多选之间切换。

// Viewport manipulation.
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);
};

// Multi selection manipulation.
toggleMultiselect2.SwitchedOn += delegate(XrwRectObj source)
{
    list2.MultiSelect = true;
};
toggleMultiselect2.SwitchedOff += delegate(XrwRectObj source)
{
    list2.MultiSelect = false;
};

如果多选功能被开启,则必须使用 XrwList.SelectedItems() 方法而不是 XrwList.SelectedItem() 来确定选中的列表项。要区分多选功能是否开启,可以使用 XrwList.MultiSelect 属性。

下一个示例代码展示了如何创建显示大图标和静态文本的列表项。静态文本(XrwList.ListItem 构造函数的第一个参数)直接用于初始化 XrwList.ListItemData 属性。

// Define the images.
X11Graphic informationLargeGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
                                                          X11Graphic.StockIcon.Information32);
X11Graphic questionLargeGraphic    = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
                                                          X11Graphic.StockIcon.Question32);
            
// Define the list items.
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);
...

这个更高级的大图标视图列表看起来是这样的

为小图标视图定义视口和列表控件并连接 EntrySelectionChanged 事件匿名委托的代码与大图标视图的代码非常相似。

唯一的区别是 XrwList.View 属性被设置为 ViewType.SmallIcon

操作视口行为的代码与大图标视图的代码非常相似,此处省略。

下一个示例代码展示了如何创建显示小图标和静态文本的列表项。静态文本(XrwList.ListItem 构造函数的第一个参数)直接用于初始化 XrwList.ListItemData 属性。

// Define the images.
X11Graphic informationSmallGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
                                                          X11Graphic.StockIcon.Information16);
X11Graphic questionSmallGraphic = XrwTheme.GetGraphic (_surface.Display, _surface.ScreenNumber,
                                                       X11Graphic.StockIcon.Question16);
// Define the list items.
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);
...

这个更高级的小图标视图列表看起来是这样的

列表的小图标视图和列表视图显示之间的主要区别在于 listView4.View = ViewType.List 属性值。

这个更高级的大列表视图列表看起来是这样的

视口滚动和单选/多选

大图标视图、小图标视图和列表视图示例提供了一个单选组,用于在自动显示滚动条、始终强制显示滚动条和始终隐藏滚动条之间切换 XrwList 外层 XrwViewport 的滚动功能。连接 SwitchedOn 事件匿名委托的代码已在大图标示例中展示过。

// Viewport manipulation.
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);
};

大图标视图、小图标视图和列表视图示例还提供了一个切换开关,用于开启或关闭 XrwList 的多选功能。连接 SwitchedOnSwitchedOff 事件匿名委托的代码已在大图标示例中展示过。

// Multi selection manipulation.
toggleMultiselect2.SwitchedOn += delegate(XrwRectObj source)
{
    list2.MultiSelect = true;
};
toggleMultiselect2.SwitchedOff += delegate(XrwRectObj source)
{
    list2.MultiSelect = false;
};

列大小调整和重新排序

显示列标题的多列列表视图可以调整其列的大小并重新排序。要使用这些功能,XrwViewportHeaderVisibility 属性必须设置为 Visibility.Visible,并且 XrwViewportHeaderPreferredHeight 属性必须设置为适当的值。列大小调整始终启用。要启用列重新排序,XrwViewport 包含的 XrwViewportGridViewHeaderColumnDrag 属性必须设置为 ColumnDragType.HeaderAnimatedColumnDragType.FullAnimated

示例代码展示了如何为多列列表创建列表项。现在,XrwList.ListItem 构造函数的第一个参数不再是静态文本,而是一个 GridViewNodeData 实例。GridViewNodeData 是一个数据对象的示例实现,它已为与 XrwList/XrwTree 一起使用做好了准备。准备工作的主要方面是提供可以绑定到 XrwList/XrwTree 列的公共属性。

// Define the list items.
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;
...

下一个示例代码展示了如何调整默认列(Columns[0])并定义适合列表所用数据类型的其他列。列数据绑定是根据 GridViewNodeDataNameCol2Col3Col4Col5 属性完成的。

// Configure and define the list columns.
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);             

创建一个列总是会注册三个处理程序:

  • 一个用于计算单元格所需的大小,
  • 一个用于计算单元格编辑器的偏移量,以及
  • 一个用于绘制单元格的内容。

默认列表列(Columns[0])在创建时总是通过向处理程序注册 XrwList.MeasureCommonCell()XrwList.MeasureCommonCellEditorOffset()XrwList.DrawCommonCell() 委托来创建。因为第 2 到第 5 列使用的数据类型具有对单元格测量、编辑器偏移测量和绘制的内置支持(布尔值、树状态、枚举和文本),所以它们也都向处理程序注册了 XrwList.MeasureCommonCell()XrwList.MeasureCommonCellEditorOffset()XrwList.DrawCommonCell() 委托。

如果数据对象的属性使用了自定义数据类型并绑定到列,则可能需要注册单独的委托来进行单元格测量、编辑器偏移测量和绘制。

下一个示例代码展示了如何操作视口和列表的行为。匿名委托连接到一个单选组的三个单选按钮和一个切换按钮。它们实现了在禁用列拖动、标题动画列拖动和完全动画标题拖动之间切换,以及在单选和多选之间切换。

// Viewport and list manipulation.
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;
};

这个最先进的多列详细视图列表看起来是这样的

要使用列大小调整,必须显示 XrwViewport 包含的 XrwViewportGridViewHeader 控件(XrwViewpor.HeaderVisibility 设置为 Visibility.Visible)。

当光标在每列的右分隔符处变为水平双箭头时,会向用户直观地指示可以调整列大小。

目前,调整列大小的敏感区域在列右分隔符左右 5 像素的范围内。

要调整列的大小,必须将水平双箭头光标向左或向右拖动,并在目标位置释放。

要使用列重新排序,需要一个多列表格,并且 XrwViewport 中包含的 XrwViewportGridViewHeader 控件的 ColumnDrag 属性必须设置为 ColumnDragType.HeaderAnimatedColumnDragType.FullAnimated

如果 XrwViewport 包含的 XrwViewportGridViewHeader 控件的 ColumnDrag 属性设置为 ColumnDragType.Never,则在调整列大小的敏感区域之外,光标会变为箭头

如果 XrwViewport 包含的 XrwViewportGridViewHeader 控件的 ColumnDrag 属性设置为 ColumnDragType.HeaderAnimatedColumnDragType.FullAnimated,则在调整列大小的敏感区域之外,光标会变为手形 2,并向用户直观地指示其已准备好重新排序该列。

目前,放置要重新排序的列的敏感区域在其列分隔符左右 5 到 15 像素的范围内。

要重新排序一列,必须将手形 2光标向左或向右拖动并放置在目标位置。如果手形 2光标移动到重新排序列的敏感区域,则列标题会移动到新的顺序位置(如果设置了 ColumnDragType.HeaderAnimated),或者列标题和列表列内容会移动到新的顺序位置(如果设置了 ColumnDragType.FullAnimated)。

单选按钮无拖动标题更新完全更新分别在 ColumnDragType.NeverColumnDragType.HeaderAnimatedColumnDragType.FullAnimated 行为之间切换。

XrwTree 示例

纯文本,无滚动

XrwTree 最简单的实现方式是不使用外围的 XrwViewport,并且只显示静态文本。不推荐这种省略外层 XrwViewport 的方法——除非 XrwTree 的最小尺寸能保证完整显示所有树节点。

示例代码展示了如何定义树形控件(使用最少推荐的调用)并连接 EntrySelectionChanged 事件的匿名委托。

// Define the list.
XrwTree tree1    = XrwTree.NewTreeWidget (parent);
tree1.ExpandToAvailableHeight = true;
tree1.ExpandToAvailableWidth  = true;
parent.AddChild (tree1);

// Be notified about selection change.
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 ();
};

XrwTree 控件的(不可见)默认列在构造期间自动创建,并且相应的 MeasureCellMeasureCellEditorOffsetDrawCell 处理程序会连接到它们的回退实现委托。

labelTree1Feedback 小部件仅用于显示选择结果,以检查 EntrySelectionChanged 委托的功能。该小部件显示文本“Selected node path:”后跟所选节点的路径。节点的路径是从根节点开始到叶节点结束的所有相关节点索引的串联,以“:”分隔。

下一个示例代码展示了如何创建只显示静态文本的树节点。静态文本(XrwTree.TreeNode 构造函数的第一个参数)直接用于初始化 XrwTree.TreeNodeData 属性。一些树节点有子节点。所有根节点都添加到 XrwTreeRootNodes 集合中。所有子节点都添加到其父 XrwTree.TreeNodeNodes 集合中。

// Define the tree nodes.
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);
...

这样就足以显示一个像这样的简单树

带滚动的图标和展开器

XrwTree 的下一个更高级的实现是与外层的 XrwViewport 一起使用,并为每个树节点显示图标、展开器和静态文本。

下一个示例代码展示了如何定义视口和树形控件(使用最少推荐的调用)来显示小图标和展开器。连接 EntrySelectionChanged 委托的代码与上一个示例相同。

// Define the viewport.
XrwViewport viewportTree2 = XrwViewport.NewViewportGadget (parent);
viewportTree2.ExpandToAvailableWidth  = true;
viewportTree2.ExpandToAvailableHeight = true;
viewportTree2.ForceBars = false;
parent.AddChild (viewportTree2);
// Define the tree.
XrwTree tree2    = XrwTree.NewTreeWidget (viewportTree2);
tree2.ExpandToAvailableHeight = true;
tree2.ExpandToAvailableWidth  = true;
tree2.ShowExpander = true;
viewportTree2.AddChild (tree2);

// Be notified about selection change.
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 ();
}; 

labelTree2Feedback 小部件仅用于显示选择结果,以检查 EntrySelectionChanged 委托的功能。该小部件显示文本“Selected node path:”后跟所选节点的路径。节点的路径是从根节点开始到所选节点结束的所有相关节点索引的串联,以“:”分隔。

下一个示例代码展示了如何操作视口和树的行为。这些委托连接到一个单选组的三个单选按钮和一个切换按钮。它们实现了在自动显示滚动条、始终显示滚动条和始终隐藏滚动条之间切换,以及在显示展开器和隐藏展开器之间切换。

// Viewport manipulation.
// Viewport and tree manipulation.
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 ();
};

下一个示例代码展示了如何创建显示小图标和静态文本的树节点。静态文本(XrwTree.TreeNode 构造函数的第一个参数)直接用于初始化 XrwTree.TreeNodeData 属性。同样,一些树节点有子节点。所有根节点都添加到 XrwTreeRootNodes 集合中。所有子节点都添加到其父 XrwTree.TreeNodeNodes 集合中。

// Define the images.
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);

// Define the tree items.
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);
...

这个更高级的带图标和展开器的树看起来是这样的

列大小调整和重新排序

显示列标题的多列树视图可以调整其列的大小并重新排序。要使用这些功能,XrwViewportHeaderVisibility 属性必须设置为 Visibility.Visible,并且 XrwViewportHeaderPreferredHeight 属性必须设置为适当的值。列大小调整始终启用。要启用列重新排序,XrwViewport 包含的 XrwViewportGridViewHeaderColumnDrag 属性必须设置为 ColumnDragType.HeaderAnimatedColumnDragType.FullAnimated

示例代码展示了如何为多列树创建树节点。现在,XrwTree.TreeNode 构造函数的第一个参数不再是静态文本,而是一个 GridViewNodeData 实例。GridViewNodeData 是一个数据对象的示例实现,它已为与 XrwList/XrwTree 一起使用做好了准备。准备工作的主要方面是提供可以绑定到 XrwList/XrwTree 列的公共属性。

// Define the tree nodes.
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);
...

下一个示例代码展示了如何调整默认列(Columns[0])并定义适合树所用数据类型的其他列。列数据绑定是根据 GridViewNodeDataNameCol2Col3Col4Col5 属性完成的。

// Configure and define the tree columns.
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);

创建一个列总是会注册三个委托,

  • 一个用于计算单元格所需的大小,
  • 一个用于计算单元格编辑器的偏移量,以及
  • 一个用于绘制单元格的内容。

默认的树列(Columns[0])总是在创建时,将 XrwTree.MeasureCommonCell()XrwTree.MeasureCommonCellEditorOffset()XrwTree.DrawCommonCell() 默认委托实现注册到 MeasureCellMeasureCellEditorOffsetDrawCell 处理程序。因为第 2 到第 5 列使用的数据类型具有对单元格测量、编辑器偏移测量和绘制的内置支持(布尔值、树状态、枚举和文本),所以它们也都注册了 XrwTree.MeasureCommonCell()XrwTree.MeasureCommonCellEditorOffset()XrwTree.DrawCommonCell() 默认委托实现。

如果数据对象的属性使用了自定义数据类型并绑定到列,则可能需要注册单独的委托来进行单元格测量、编辑器偏移测量和绘制。

下一个示例代码展示了如何操作视口和树的行为。这些委托连接到一个单选组的三个单选按钮和一个切换按钮。它们实现了在禁用列拖动、标题动画列拖动和完全动画标题拖动之间切换,以及在显示展开器和隐藏展开器之间切换。

// Viewport and tree manipulation.
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 ();
};

这个最先进的多列表格树看起来是这样的

列大小调整和列重新排序的工作方式与多列列表示例中描述的完全相同。

点击行为、命令调用和就地编辑

XrwList 控件

  • 单击可选择一个列表项,
  • 提供范围多选(按住 [Shift] 键)或非连续项多选(按住 [Ctrl] 键),
  • 提供在双击列表项时调用委托的可能性,以及
  • 提供在第二次点击列表项时调用就地编辑的可能性。

XrwTree 控件

  • 单击可选择一个树节点,
  • 双击或单击树节点的展开器可折叠/展开树节点,
  • 提供在双击树节点时调用委托的可能性,以及
  • 提供在第二次点击树节点时调用就地编辑的可能性。

对于 XrwListXrwTree 控件,双击事件必须有一个委托注册到 XrwList.ItemCommandInvokeXrwTree.NodeCommandInvoke。此功能很少使用,示例应用程序中未作演示。

多选功能由以下示例演示:

  • 展示大图标视图的更高级 XrwList 示例,
  • 小图标视图和
  • 列表视图,以及
  • XrwList 多列列表视图示例“高级列表测试”。

要进行多选,MultiSelect 属性必须设置为 true

就地编辑功能由以下示例演示:

  • XrwList 多列列表视图示例“高级列表测试”和
  • XrwTree 多列树视图示例“高级树测试”。

对于就地编辑,必须为每个列和每个列表项/树节点明确启用 XrwList.EditableXrwTree.Editable 属性,因为默认情况下它被设置为 false

要求为每个列和每个列表项/树节点明确启用编辑,提供了非常精确的控制。

下一个示例代码展示了如何为 XrwList 实现这一点(对于 XrwTree,调用是相同的)

// Enable editing for distinct items.
...
ln1A.Editable = true;
...
ln1B.Editable = true;
...

// Enable editing for distict columns.
...
list1.Columns[0].Editable = true;
...
col2.Editable = true;
...

就地编辑目前内置支持显示以下内容的列:

  • 单行文本(System.String),
  • 布尔值(System.Boolean),
  • 三态值(System.ThreeState)和
  • 枚举(System.Enum)。

就地编辑由第二次点击已选中的列表项/树节点内要编辑的列来触发。

XrwList.HandleButtonReleaseDefault 预注册的 ButtonRelease 事件委托会随后调用 XrwList.HandleEditRequest,以便在满足先决条件时为所选列表项调用就地编辑。

XrwTree.HandleButtonReleaseDefault 预注册的 ButtonRelease 事件委托会随后调用 XrwTree.HandleEditRequest,以便在满足先决条件时为所选树节点调用就地编辑。

两个 HandleEditRequest 方法都会计算要编辑的列,并针对该列执行以下操作之一:

  • 切换值(对于 System.BooleanSystem.ThreeState 类型)或
  • 计算编辑器区域(位置和大小)并调用单元格编辑器。

目前,所需单元格编辑器的确定由 XrwBaseCellEditorShell.FindCellEditor 完成。这个静态方法会创建一个专门的单元格编辑器外壳,适用于要编辑的列的数据类型(System.StringSystem.Enum)。

关注点

我编写这个示例应用程序是为了给 XrwListXrwTree 控件创建一个几乎完整的交互式测试,并结合 XrwViewportXrwViewportGridViewHeader 控件来演示/解释其高级功能。

历史

这是本文于 2014 年 8 月 13 日的初始版本。

© . All rights reserved.