Jump to content
JohnLM

Is it possible to copy all properties from one TMemo to a dynamically created TMemo?

Recommended Posts

Specs:  XE7, VCL, Win7 

 

I have a Panel with a TabControl in it and a Memo inside the first TabSheet on a Form that was all created and configured during design time.  And, on the Form in another Panel, I have a [Add] button to dynamically create a new TabSheet and Memo. 


The main Memo is the default with all the settings that I would like the dynamically created Memos to have.  But setting up all the necessary properties would be tedious and if I change something in the main Memo at design-time, I may not remember to update that to the dynamic ones.   

 

var
  Form1: TForm1;
  idx: integer=0; // index numb, for adding new tabs to the pagecontrol.

procedure TForm1.btnAddClick(Sender: TObject);
var
  TabSheet: TTabSheet;
  memo: TMemo;
begin
  inc(idx);
  TabSheet := TTabSheet.Create(PageControl1);
  TabSheet.Caption := 'Untitled-'+idx.ToString;
  TabSheet.PageControl := PageControl1;

  memo := tmemo.Create(pagecontrol1);
  // settings section
  memo.Color := $0017110D;
  memo.Align := alClient;
  .
  .
  .
  // end of settings section
  memo.Parent := tabsheet;
  memo.Show;
  memo.SetFocus;
end;

note: I have a lot more properties than what is posted above. 


Is there a better way to copy all the properties of the main Memo to the dynamically created ones and how can I do this?
 

Edited by JohnLM
updated missing code fragment

Share this post


Link to post
1 hour ago, JohnLM said:

if I change something in the main Memo at design-time, I may not remember to update that to the dynamic ones.

So don't set them at design-time; Set them at run-time in a function that will then also be used setup the other memos.

 

There aren't that many properties on a TMemo so I really don't think it's worth it to try and be clever here but if you really want to you can either use RTTI or streaming (see TStream.WriteComponent/ReadComponent). If your memo use event handlers you will get into trouble with the stream method.

Share this post


Link to post

Keep it simple - create a class helper for TMemo and add an Assign method and just set the props you need. This will be quicker and easier than messing with RTTI (which can be a bit of a rabbit hole with some property types).

  • Like 1

Share this post


Link to post
19 hours ago, Vincent Parrett said:

Keep it simple - create a class helper for TMemo and add an Assign method and just set the props you need. This will be quicker and easier than messing with RTTI (which can be a bit of a rabbit hole with some property types).

This is the way.

 

Share this post


Link to post

Update on my progress. . . 
 

You guys can do this!  I have no doubt!  I know you all can figure this out, lickady-split or a few minutes, but I am not in your category.  I am just a lowly hobbiest at best. 


Unfortunately, I am no component expert, nor am I am Pro at Pascal programming, let alone OOP and all. I am just a casual guy who codes small Windows utilities and other fun and interesting things. Occasionally I will try and learn OOP and component anything but I do not get very far, as I am a very slow learner.  
Anyway. I have looked into the suggestions here, and also my own searches. 


I found some resources and spent a great deal of time trying to understand them and then build them, but to no avail. I am just a complete failure in these advanced areas. 


For instance, in one resource (web search) I did find a routine that shows how to pull properties from all components on the form.  


And in another resource (web search) I found another routine showing how to pull properties similarly but slitely less detail.  


And yet in another resource showing similar, I can pull and narrow down to just tmemo's properties, which is just what I was looking for as a start. 


And then, in another resource, I found where I could: pull the properties of a tmemo and show it in another memo and make changes to the properties in that memo and the routine would show a memo with the changed state.  But, I could not get it to copy from another memo, its properties because it would issue an error: "memo has no parent window" and other errors as well.  I tried many scenarios of code ideas but to no avail. 


There is one more resource that I found (from the lazarus website) to try out, the next time I come back from a break from this endeavor.  


As for now and after three very long days and nights and many many hours, so many hours, of trying to figure this out, I am giving this up.  I am taking a break and going back to other projects.  

 

Thank you all for your suggestions. 

Share this post


Link to post
On 1/7/2025 at 3:11 PM, JohnLM said:

Is there a better way to copy all the properties of the main Memo to the dynamically created ones and how can I do this?

Yes - don't make a copy at all! Use a TFrame instead.
 

https://docwiki.embarcadero.com/RADStudio/en/Working_with_Frames

 

At design-time, create a new TFrame class, put a TMemo on it, and configure it as needed.  Then at runtime, you can create a new instance of your Frame class (which will have all of your design-time property values) and assign a Parent to it as needed.  In this case, a new TabSheet.

 

With this approach, you also won't need to waste a TabSheet at design-time just to hold your default values. But, if you really want to, you can place your Frame on the 1st TabSheet at design-time, too.

Edited by Remy Lebeau

Share this post


Link to post

Update on progress. . . continues. . . 

 

@Remy Lebeau, I have tried your suggestion.  And after many hours, I finally got it working, I think.  I never worked with Frames before.  I still don't understand them but in this case, I do believe I have it working, but with a few slight issues. 

 

1st, the code, now modified for Frame support: 

procedure TForm1.btnAddClick(Sender: TObject);
var
  TabSheet: TTabSheet;
  mem: unit2.TFrame2;
begin
  inc(idx);
  TabSheet := TTabSheet.Create(PageControl1);
  TabSheet.Caption := 'Untitled-'+idx.ToString;
  TabSheet.PageControl := PageControl1;
  mem := tframe2.Create(nil);
  mem.Parent       := tabsheet;
  mem.Name         := 'Untitled'+idx.ToString;
  mem.memo1.Align  := alClient;
  mem.memo1.Show;
  mem.memo1.SetFocus;
end;

And the results: 

 

2060238209_im-dyanmicallycreatedtabswithframessupport.png.faec7fd031dbb8b86217607bf4b8886c.png

 

As for the issues: 

 

1) all the tabs I create have the same name, "memo1".  (see code) 

2) the alignment to client is quirky, but I managed to get it to work to fill the tab area.  (see code) 

3) the memo's names (for each tab do not show up in the memo's view.  They all say the same thing, "memo1". (see code) 

   they should be "Untitled1, Untitled2, Untitled3, ..." in each memo, just like when you add a new memo to your form in the IDE. 
 

Share this post


Link to post

Instead of setting a unique name, you can set it to an empty string.

And what you show in the screen shot is not the memo's name but the memo's Lines property contents. You can set that easily to whatever you want either. Memo.Lines.Text := 'bla blub';

 

But that's not what was meant when you were told use frames:

 

Just design a frame the way you want it, and create multiple instances of that particular frame class.

Edited by dummzeuch

Share this post


Link to post

When you add a memo or memos to a form, the default behavior is that Delphi will give the .Name and .Text properties a detault value and assign them the same string to represent that component.  Thus, memo1.name=memo1 for instance.  It prefills this info for our reference at design-time and we can change it or remove it.  I myself usually remove it, the .Text portion and I will usually rename .Name to something much shorter, like .Name=m2 for instance. 

 

procedure TForm1.btnAddClick(Sender: TObject);
var
  TabSheet: TTabSheet;
  memo: unit2.TFrame2;
begin
  inc(idx);
  TabSheet := TTabSheet.Create(PageControl1);
  TabSheet.Caption := 'Untitled-'+idx.ToString;
  TabSheet.PageControl := PageControl1;
  memo := tframe2.Create(nil);
  memo.Parent    := tabsheet;
  memo.Name      := 'Untitled'+idx.ToString;
  memo.memo1.Align  := alClient;
  memo.memo1.Lines.Add(memo.name); // <-- this line will show the memo's .Name portion in each dynamically create new tab/memo.
  memo.memo1.Show;
  memo.memo1.SetFocus;
end;

 

1027195734_im-dynamictabsex2.png.4ad68e97c30be94f22c00a170e183a65.png

 

I guess I don't actually need the .Name portion to show. I was just trying to demonstrate to myself that I was creating unique tabs AND memo's successfully as demonstrated above. 

 

 

Edited by JohnLM
typo and added updated source code and added an image

Share this post


Link to post

On a side note - the TFrame should have an Owner assigned, not just a Parent, eg:

memo := tframe2.Create(TabSheet);

 

  • Thanks 1

Share this post


Link to post

I had to change the memo to frame2.  The memo is inside the frame. 

 

update code snippet #4 

procedure TForm1.btnAddClick(Sender: TObject);
var
  TabSheet: TTabSheet;
  frame2: unit2.TFrame2;
begin
  inc(idx);
  TabSheet := TTabSheet.Create(PageControl1);
  TabSheet.Caption := 'Untitled-'+idx.ToString;
  TabSheet.PageControl := PageControl1;
  frame2 := tframe2.Create(nil);
  frame2.Parent    := tabsheet;
  frame2.Name      := 'Untitled'+idx.ToString;
  frame2.memo1.Align  := alClient;
  frame2.memo1.Lines.Add(frame2.name); // <<-- this line will show the memos .Name portion in each dynamically create new tab/memo.
  frame2.memo1.Show;
  frame2.memo1.SetFocus;
end;

 

Next steps is to get the text from whichever memo I am typing in the tabsheet. 

 

That should be easy, I think I just need to grab the frame.memo1.lines or frame.memo1.text should return the text from any memo I am currently typing in.

 

I will look into later today.  I have to get some sleep now. until then. . .

Edited by JohnLM

Share this post


Link to post

Progress update. . . -- on that last next steps I just mentioned, to pull the text from the active tabsheet for the memo I am focused in. . .

 

I have successfully figured out how to pull the text from the dynamically created memo's.   It was quite difficult to solve, but I did it.  And I did not copy someone else's code snippet. 

 

And the code snippet to accomplish it: 

procedure TForm1.PageControl1Change(Sender: TObject);
var
  frame   : TFrame2;
  edit    : TEdit;
  pgCount : integer;
  tabIdx  : integer;
begin
  tabIdx  := PageControl1.ActivePageIndex;
  pgCount := pagecontrol1.PageCount;
  frame   := (pagecontrol1.Pages[tabIdx].Components[0] as tframe2);
  edit    := (pagecontrol1.Pages[tabIdx].Components[1] as TEdit);
  StrLines.Assign(frame.memo.Lines);
  listbox1.Items.Assign(frame.memo.Lines);
end;

 

Output results are:  text from each memo (left pane) is copied into the listbox (right pane) for proof of concept. 

 

DynamicTabsMemos_by_JohnLM.thumb.gif.f3af7b4ada65dad22fa9751c0fa0e20f.gif

 

All this is part of a project that I am working on, to create a tiny hobby IDE for Python for myself. 

 

It appears that this topic has been solved. 

 

Share this post


Link to post

That code can be simplified:

procedure TForm1.PageControl1Change(Sender: TObject);
var
  sheet   : TTabSheet;
  frame   : TFrame2;
begin
  sheet   := PageControl1.ActivePage;
  frame   := sheet.Components[0] as TFrame2;
  StrLines.Assign(frame.Memo.Lines);
  ListBox1.Items.Assign(frame.Memo.Lines);
end;

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×