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

MC++ 中的 DataGridSample

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.18/5 (2投票s)

2004 年 9 月 7 日

7分钟阅读

viewsIcon

57148

downloadIcon

900

关于在 Windows 窗体中实现绑定到嵌套 ArrayList 的 DataGrid 控件的文章。

图 1. 带有 DataGrid 控件的用户输入表单 – 记录已展开

引言

本文旨在提供一个在托管 C++ (MC++) 编程的 Windows 窗体中使用 DataGrid 控件的示例。我希望 DataGridDataSource 是对象的 2 个嵌套 ArrayList。附带的示例项目是我第一次尝试使用 DataGrid 控件。我在网上找到的大部分文章和示例都是用 C# 或 VB .Net 编写的,所以我觉得有必要分享一些我在熟悉 DataGrid 控件时学到的经验,来帮助其他 C++ 程序员。

用户界面必须允许向父 ArrayList 添加新的 Positions,并使用其他数据输入控件添加相关的 ScaleFactors。它还必须允许删除选定的子 ScaleFactor 或从 DataGrid 控件中删除选定的父 Position。最后,UI 必须允许删除给定 Position 中的所有 ScaleFactors 或删除所有 Positions。

DataGrid 控件可能是从 System.Windows.Forms.Control 类派生的最强大的类。WinForm 中的 DataGrid 对象通常绑定到相关数据表的 数据源。任何时候,DataGrid 中只有一个表被显示。如果数据表之间建立了父子关系,并且启用了 DataGrid 的导航,则用户可以通过单击记录的行标题中的展开器来导航相关表,该展开器会生成一个类似 Web 的链接到子表。参见图 1。单击链接时,会显示与单个父记录相关的子表中的记录。参见图 2。当 DataGrid 显示子表中的记录时,DataGrid 的标题中会提供一个后退按钮,允许用户导航回父表。

图 2. 带有 2 个 ScaleFactor 对象输入的 DataGrid 控件

工作原理

图 1 显示的 WinForm 显示了示例项目在用户输入一些数据后的最终表单。所有文本框在设计时都设置了初始值,以提供预期的此类数据的示例。当父 ArrayList(即 DataGridDataSource)中至少有一个父 Position 对象时,“ScaleFactor”组将被启用。每次添加新记录后,所有初始值都会被清除。通过选择一个 Position 记录,然后在“ScaleFactor”组框的文本框中输入数据来将 ScaleFactors 添加到给定的 Position。当前记录指示器所在的记录将是接收输入的 ScaleFactors 的当前记录。

要删除选定的 Position 对象,请单击所选记录的 RowHeader(最左边的灰色列),然后单击“Remove Selected”按钮。删除 Position 对象时,还会删除所有相关的子 ScaleFactor 对象。要删除选定的 ScaleFactor 对象,请先单击所选 Position 记录的展开器(RowHeader 中的 + 按钮),然后单击类似 Web 的链接以显示相关子 ScaleFactors 的列表。然后,选择一个 ScaleFactor RowHeader,然后单击“ScaleFactor”组框中的“Remove Selected”。

要清除与给定 Position 对象相关的所有 ScaleFactors,请选择一个 Position 行并单击“ScaleFactor”组框中的“Clear All”按钮。要清除所有 Position 记录,请单击主窗体的“Clear All”按钮,该按钮会清空整个 DataGrid,甚至会删除与底层 Position ArrayList 的数据绑定。

除了不必要的情况(例如清除所有 Positions 的情况)外,我在项目中的几乎每个方法中都刷新了 DataGrid。这看起来可能有些过度,但它可以确保在每次操作后 DataGrid 的显示都保持一致的状态。

用户可以通过单击单元格编辑 Position 或 ScaleFactor 的任何数据成员,然后单击另一个记录或按 CTRL + Enter。事实上,Visual Studio .Net 帮助中有一个完整的键盘快捷键列表,恰当地命名为Windows Forms DataGrid 控件的键盘快捷键

要将 DataGrid 控件绑定到对象数组,对象类必须包含公共属性。父 Position ArrayList 包含一个数据成员,该成员本身就是一个 ScaleFactor 对象组成的 ArrayList,如以下代码片段所示。这建立了 DataGrid 的 DataSource 的数据成员之间的父子关系。

// Parent class containing the ScaleFactor class
public: __gc class Position {
private:
  float x;
  float y;
  float z;
  myArrayList * scaleFactors;

public:
  Position( void ) {
        // Allocate memory on the heap for each child ArrayList
        // when a parent is created.
  scaleFactors = new myArrayList;
  };
  __property float get_X( void ) { return x; };
  __property void set_X( float input ) { x = input; };
  __property float get_Y( void ) { return y; };
  __property void set_Y( float input ) { y = input; };
  __property float get_Z( void ) { return z; };
  __property void set_Z( float input ) { z = input; };
  __property myArrayList * get_ScaleFactors( void ) { return scaleFactors; };
  __property void set_ScaleFactors( myArrayList * input ) 
    { scaleFactors = input; };
};

绑定到 ArrayLists

父对象和子对象通过 DataGrid 使用 CurrencyManager 对象删除,该对象会更新 DataGrid 中显示的记录。同时,对象会从父对象或给定父对象的子 ArrayList 中删除。

DataGrid 用于显示嵌套 ArrayLists 的当前状态,允许用户修改任何记录中的字段值,并允许删除选定的父或子 ArrayList 项。Position 和 ScaleFactor 类的公共属性映射到 DataGrid 中的数据列。

System.Collections.ArrayList 类使用一个数组来实现 IList 接口,该数组的大小会根据需要动态增加。Visual Studio .Net 帮助中的几个类讨论了使用 ArrayList 作为 DataGrid 的绑定数据源,在大多数情况下,帮助会警告说“……ArrayList 在绑定时必须包含项。空的 ArrayList 将导致网格为空。”Microsoft 建议并像下一个代码片段所示,在运行时使用 SetDataBinding 方法设置 DataGridDataSourceDataMember。绑定发生在添加到父 ArrayList(称为 positionList)的每个 Position 时。这种绑定方式是必要的,因为 ArrayList 没有关联的 DataAdapter,并且 DataGridDataSource 必须在添加到数据源的每个新 ArrayList 项时进行更新。

  Position * newParentObj = new Position;
  newParentObj->X = Single::Parse( tboX->Text );
  newParentObj->Y = Single::Parse( tboY->Text );
  newParentObj->Z = Single::Parse( tboZ->Text );
  int addIndex = positionList->Add( newParentObj );
  // Bind the DataGrid to positionList
  dataGrid2->SetDataBinding( positionList, S"" );

通过 DataGridTableStyle,您可以控制 DataGrid 中显示的数据的外观。所有 DataGridTableStylesDataGridColumnStyles 都必须以编程方式创建,因为在设计时不存在 DataSource。此外,在创建 DataGridColumnStylesDataGridTableStyles 时,Visual Studio .Net 帮助会警告:

"始终先创建 DataGridColumnStyle 对象并将它们添加到 GridColumnStylesCollection,然后再将 DataGridTableStyle 对象添加到 GridTableStylesCollection。当您将一个空的 DataGridTableStyle 添加到集合中时,系统会自动为您生成 DataGridColumnStyle 对象。因此,如果您尝试将 [您的] 新 DataGridColumnStyle 对象添加具有重复 MappingName 值的对象到 GridColumnStylesCollection 中,将会引发异常。"

Visual Studio .Net 帮助还规定,当将 DataGrid 控件绑定到 ArrayList 时,您应该将 DataGridTableStyleMappingName 设置为“ArrayList”(类型名称)。每个 DataGridTableStyleMappingName 也必须是唯一的。通过创建一个与 ArrayList 相同的子类并赋予不同的名称,可以像下一个代码片段所示那样将 PositionObjectsTableScaleFactorsTable 都映射到 ArrayLists

// Giving ArrayList a new name to keep unique MappingNames
public: __gc class myArrayList : public ArrayList { };
      .
      .
      .
  PositionsStyle->MappingName = S"ArrayList";
      .
      .
      .
  ScaleFactorsStyle->MappingName = S"myArrayList";

DataGrid 在添加、删除或清除 DataSource 中的记录的几乎每个方法中都会被刷新。父 ArrayListCurrencyManager 也会调用 Refresh 方法。

删除记录

确定用户在 DataGrid 中选择了哪一行,然后确定所选行是来自父表还是子表的记录,是本项目中最难逻辑化的部分。Visual Studio .Net 帮助中的 DataGrid 类概述提供了解决第一个难题的方法:

"要确定用户单击了控件的哪个部分,请在 DataGrid 控件的 MouseDown 事件中使用 HitTest 方法。HitTest 方法返回一个 DataGrid.HitTestInfo 对象,该对象包含单击区域的行和列。"

要确定选择的是父记录还是子记录,我们必须知道 DataGrid 控件正在显示 DataSource 中的哪个列表。DataMember 属性为我们获取了这个列表。由于 DataSourceArrayList,最顶层的 DataMember 是一个空字符串,这带来了一个小小的复杂性。下一个代码片段显示了我如何确定正在显示的表,然后将父或子 CurrencyManager 的 Position 属性设置为选定的记录,这使得该记录成为当前或活动记录。

    if( parentCurrencyMgr != 0 ) {
       if( dataGrid2->DataMember->ToString()->Equals(String::Empty) ) {
          parentCurrencyMgr->Position = hti->Row;
       }

       if( dataGrid2->DataMember->ToString()->Equals(S"ScaleFactors") ) {
          Position * currentParentObj = 
             dynamic_cast(parentCurrencyMgr->Current);
          // get rid of any pre-existing CM pointing to a child ArrayList
          if( childCurrencyMgr != 0 ) childCurrencyMgr = 0;
          childCurrencyMgr = dynamic_cast<CurrencyManager *>
             (dataGrid2->BindingContext->get_Item( currentParentObj->
             ScaleFactors, S"" ));
          if( 0 <= hti->Row && hti->Row < childCurrencyMgr->Count )
             childCurrencyMgr->Position = hti->Row;

摘要

这个 MC++ 原型项目是我第一次尝试使用 DataGrid 控件,它可能是 Control 类中最强大的控件。在经历了处理这个控件的挫折和收获之后,我希望继续使用类型化 DataSets 作为 DataGridDataSource。如有问题或善意的评论,请发送电子邮件给我。

© . All rights reserved.