【问题标题】:Cloning a TStringGrid Component克隆 TStringGrid 组件
【发布时间】:2010-12-04 06:28:16
【问题描述】:

我开始学习 Delphi。所以我决定从头开始编写一个像 MS Excel 这样的应用程序。在一个新的 Form1 中,我确实放置了一个仅包含 1 页的 TPageControl 组件。在那个页面中,我确实放了一个 TAdvStringGrid 和一个带有一些按钮(button1、button2)的 TPanel 和一个 Popup1 菜单,用于定义网格上的一些操作,如复制单元格、复制行、删除行等。对于那个 StringGrid,还有,我已经定义了一些属性,如颜色、字体等。我在主窗体中添加了一个带有按钮的工具栏,以便向 PageControl 添加更多页面。该按钮的 OnClick 方法定义了两个操作:
1) 将新的 Page2 添加到 PageControl,并且 2)在新创建的 Page2 中添加一个新的 StringGrid 。

在 Tpagecontrol 的新页面中创建的新的(运行时定义的)StringGrid 应该继承(获取、复制、克隆、复制)在设计时已经在第一页中定义的 StringGrid 父级的属性和方法,并且应该是能够像 StringGrid 父级一样调用 PopUp1 菜单。我们如何做到这一点?

一开始,我以为我应该使用 assing() 复制 StringGrid 属性,但是当使用这种方法时,当鼠标右键单击新的 StringGrid 时不会弹出弹出菜单...和按钮(button1 Form1 的 button2) 仅适用于第一个 StringGrid,但不适用于新添加的 StringGrid。我确实在某处读到,为了解决这个问题,我可以通过使用写入和读取 TMemoryStream 来复制 StringGrid 组件(将父字符串网格保存到内存流中,创建一个新的字符串网格,然后将该内存流读入新创建的字符串网格),所以我做了,但是当程序执行这个组件克隆方法时,我得到一个运行时错误。 :-(

我确实仔细检查了帮助。没有找到关于该主题的内容。似乎没有任何 Delphi 组件库或第三方组件可以处理此类任务。有人可以帮忙吗? :o)

【问题讨论】:

  • 虽然尝试学习 Delphi 是非常令人钦佩的,但编写一个“像 MS Excel”这样的应用程序作为一个学习项目可能被认为有点雄心勃勃。好听的“Hello, World”怎么了?
  • 不,他的想法是对的。通过尝试创建一个不做任何事情的程序,你不会学到任何东西。构建一个电子表格,即使是一个简单的电子表格,也会教授各种重要的编程原则。
  • 感谢梅森,你完全正确。我相信编写程序是真正学习编程的方式。我有一些 Java 经验,但 Delphi 对我来说是全新的。有很多事情要弄清楚。我相信一个“雄心勃勃”的项目可以帮助我学习这种语言/IDE的整洁和微小的东西,前提是我让自己骑着这匹野马。谢谢!

标签: delphi properties components cloning


【解决方案1】:

我会使用选项卡控件而不是页面控件。这样,您最终会得到多个选项卡,但只有一个页面和网格。然后我会制作某种数据结构来保存我所有的单元格信息,并将这个结构呈现到网格中。这样,我可以拥有多个结构,并让活动选项卡决定要呈现哪个结构。你最终也会在你的 gui 和你的逻辑之间获得更松散的耦合,使以后更容易改变事情。例如。如果您需要将来自不同电子表格的某些值引入当前电子表格的单元格中,您可以加载一个结构并挑选出想要的值。根本不需要为第二个电子表格制作任何 gui。

对于 3.party 组件,我会推荐 TMS FlexCell 和TAdvSpreadGrid。这将为您提供所需的大部分内容。

【讨论】:

  • 我明白了。听起来确实是一个有趣的解决方案。我必须检查 DataStructure 渲染是否适用于小网格和大网格(大量包含数据的列和行)。在运行时,如果我们更改任何网格中的某些内容,则该更改必须反映在 DataStructure 中。这意味着每次我在网格中进行微小更改时都要编写一个循环进入 DataStructure 的例程?例如,我在单元格[25,42]中输入了一些东西,所以网格的onclick方法调用DataStructure,找到正确的网格,找到正确的单元格并写入值,是吗?
  • 是的,类似的。在下面有一个相当有效的结构很重要,但只要床单不是太大,就不应该有太多麻烦。 [row-reference, list]-pairs 的列表,其中每个列表都包含一个 [column-reference, value] 对。如果您保持列表排序,您将有一个相当快的查找。一个更简单的解决方案是将 cellreference [row, column] 转换为某种哈希值,并将 [hashvalue, cellvalue] 对存储在某种字典中。
【解决方案2】:

对于学习者来说是一个棘手的选择 :) 但是您不需要开始流式传输。

查找 TPersistent 的 assign() 过程,这是您轻松复制部分网格所需的例程。例如

for i := 0 to StringGrid1.RowCount - 1 do
  StringGrid2.Rows[i].Assign(StringGrid1.Rows[i]);

为了便于开始使用 Tag 属性(StringGrid1.Tag := 1、StringGrid2.Tag := 2 等)区分您的网格。

弹出菜单也很简单:

StringGrid2.popupmenu := stringGrid1.popupMenu 然后你必须在弹出例程中决定哪个网格是“活跃的”我喜欢的东西

Tform1.popupMenuItem1Click(Sender: TObject)
  if Sender is TStringGrid then
    Case TStrigngGrid(Sender).Tag of
     1: // Grid 1
     2: // Grid 2

您可以对按钮使用相同的简单逻辑。

正如 neftali 所说,最好的办法是将创建的网格放在 ObjectList 中。 然后你会得到稍微复杂但可扩展的结果:

Tform1.popupMenuItem1Click(Sender: TObject)
 var AGrid: TStringGrid;

  if Sender is TStringGrid then
     AGrid := MyListOfStringGrids[MyListOfStringGrids.IndexOf(Sender)];
       DoMenuItem1Stuff(AGrid);

玩得开心

【讨论】:

  • 这个 PopupMenuItemClick 看起来很有希望,看起来有助于解决部分问题!谢谢。我现在就去检查。关于网格,好吧,我不需要复制网格内容。我需要将第一个网格的一些属性和方法复制到一个新的运行时创建的网格中。如果我按照 Vegar 的建议使用 TadvStringGrid,事情会变得更糟......
【解决方案3】:

"...Form1 的按钮(button1 和 button2)仅适用于第一个 StringGrid,但不适用于新添加的 StringGrid。为了解决这个问题,我确实在某处读过..."

没有解决这个问题的通用方法。 Delphi 提供了不同的工具来解决它。

  1. 您可以创建一个包含所有 StringGrid 的对象列表 (TObjectList);在 Button1 Click 事件中,您必须搜索此时您正在使用的网格是什么。例如(其他按钮的 BIS):

var  
  index:integer;  
  sg:TStringGrid;  
begin  
  ...  
  // search the active page   
  index := pageControl.ActivePageIndex;    //0, 1, 2,...  
  // USe this for search the StringGrid  
  sg := TStringGrid(OList.Objects[index]);  
  // the code that you have at the event bus woking with sg 
  // not stringgrid1, stringgrid2,...
  ...
  sg.Color :=      
  ...

如果您不想使用 ObjectList,还有其他选择。您可以对所有 StringGrid 使用 Tag 属性。分配 0, 1, 2, 3,...
提取索引(活动页面)并搜索具有相同值的属性 Tag 的 TStringGrid。您可以使用 FindComponent 来做到这一点。第一种方法更好。 ;-)

问候。 P.D:请原谅我的英语不好。

【讨论】:

  • ObjectList 的想法和你的英语都很好:)
【解决方案4】:

亲爱的,我正在努力学习Delphi

Delphi 风格是寻找/创建/购买完成这项工作的组件并在设计时使用它们。您可以尝试基于网格制作自定义组件或使用TFrame。查看来自Custom Component Development 的链接和Delphi 附带的帮助文件。

如果您确实需要动态克隆控件,here 是我发现的一个使用 stream.ReadComponent 的示例。

【讨论】:

  • 亲爱的 eed3si9n,我很抱歉我的无知,但是“子类化网格”是什么意思?我确实检查了 Delphi 中的帮助,我看不到链接。您链接中的 TMemoryStream 方法正是我在 StackOverflow 中询问之前所做的尝试......我使用 TAdvStringGrid(如 Vegar 所建议的那样),但 TMemoryStream 不适用于它。
  • @Biolyzer,根据维基百科,“子类是从其超类继承某些属性的类。[...] 子类和超类通常分别称为派生类和基类,这是创造的术语由 C++ 创建者 Bjarne Stroustrup 撰写。”
猜你喜欢
  • 2012-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-25
  • 1970-01-01
  • 1970-01-01
  • 2015-02-12
  • 1970-01-01
相关资源
最近更新 更多