1
votes

I am starting to learn Delphi. So I decided to write an application like MS Excel from scratch. In a new Form1, I did put a TPageControl component containing only 1 page. In that page, I did put a TAdvStringGrid and a TPanel with some buttons (button1, button2) and a Popup1 menu for defining some actions on the grid, like copy cell, copy row, delete row, etc. For that StringGrid, also, I have defined some properties, like colors, fonts, etc. I added a toolbar with a button to the main form, in order to add more pages to the PageControl. The OnClick method of that button defines two actions:
1) to add a new Page2 to the PageControl, and 2) to add a new StringGrid In the new created Page2 .

That new (runtime defined) StringGrid created in a new Page of a Tpagecontrol should inherite (get, copy, clone, duplicate) the properties and methods of the StringGrid parent already defined in the first page at design time, and should be able to call the PopUp1 menu just like the StringGrid parent. How we do this?

At the beginning, I thought I just should copy the StringGrid properties using assing(), but when using this approach, the popup menu does not pop up when right mouse clicking on the new StringGrid... and the buttons (button1 and button2) of Form1 only work with the first StringGrid but not with the new added one. I did read somewhere that in order to solve this problem, I could duplicate the StringGrid component by using the write and read TMemoryStream (save the parent stringgrid into a memorystream, create a new stringgrid and then read that memorystream into the new created stringgrid), so I did, but when the program executes this component cloning method, I get a runtime error. :-(

I did carefully chech the Help. Nothing found on that topic. Seems there is not any Delphi component library or third party components that cope with this kind of task. Can anybody help, please? :o)

4
While trying to learn Delphi is very admirable, writing an application "like MS Excel" as a learning project might be considered a tad ambitious. What happened to good ole' "Hello, World"?Svein Bringsli
No, he's got the right idea. You don't learn anything by trying to create a program that doesn't do anything. Building a spreadsheet, even a simple one, will teach all sorts of important programming principles.Mason Wheeler
Thnak you Mason, you got it absolutely right. I believe writing programs is how one can really learn programming. I have some experience in Java, but Delphi is completely new for me. There are many things to figure out. I believe an "ambitious" project can help me to learn the tidy and tiny stuffs of this languague/IDE, only if I keep myself riding this wild horse. Thanks!user184938

4 Answers

2
votes

I would use an tabcontrol instead of an pagecontrol. That way, you would end up with multiple tabs but only one page and grid. I would then make some kind of data structure to keep all my cell information in, and render this structure to the grid. This way, I can have multiple structures, and let the active tab decide which structure to render. You will also end up with a looser coupling between your gui and your logic, making it easier change things later. E.g. if you need to bring in some values form a different spreadsheet into a cell in the current spreadsheet, you can load up a structure and pick out the wanted values. No need to make any gui for the second spreadsheet at all.

For a 3.party component, I will recommend TMS FlexCell and TAdvSpreadGrid. That will give you most of what you need.

1
votes

A tricky choice for a learner :) however you do not need to start streaming things.

Look up the assign() procedure for TPersistent this is the routine you need to copy parts of the grid easily. For example

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

for an easy start differentiate your grids with the Tag property(StringGrid1.Tag := 1, StringGrid2.Tag := 2 Etc.

The popup menu is pretty simple too:

StringGrid2.popupmenu := stringGrid1.popupMenu But then then you must decide in the Popup Routine Which Grid is "Active" some thing l like

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

You can use the same simple logic with the buttons.

As neftali mentioned the best thing would be to put the created grids in an ObjectList. You would then end up with the slightly more complex but expandabe:

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

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

Have fun

0
votes

"...and the buttons (button1 and button2) of Form1 only work with the first StringGrid but not with the new added one. I did read somewhere that in order to solve this problem..."

There is no generic method for solving this. Delphi offers different tools to solve it.

  1. You can create a list of Objects (TObjectList) that containts all the StringGrid; At Button1 Click event you must search what is the grid that you are using at this moment. For example (BIS for the other buttons):

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 :=      
  ...

If you don't want use ObjectList, there are alternatives. You can use Tag property for all StringGrids. Assign 0, 1, 2, 3,...
Extract the index (active page) and search the TStringGrid that have the property Tag with the same value. You can do this with FindComponent. The first methos is better. ;-)

Regards. P.D: Excuse for my bad english.

0
votes

Dear all, I am trying to learn Delphi

The Delphi style is to find/create/buy a component that does the job and use them in the design-time. You could try making a custom component based on a grid or use TFrame. See links from Custom Component Development and help files that comes with Delphi.

If you really need to clone the control dynamically, here's an example I found that uses stream.ReadComponent.