Board index » delphi » More dynamic objects

More dynamic objects

Hello again,

Well I thought I had this figured out, but it seems not...

My program has a form which when opened reads in 'n' 'reasons'
(strings) and displays each in it's own TLabel which the user
can edit.

I wasn't originally going to use TLabels as I figured a
TStringGrid would be the most suitable, but you can't have
multi-line entries in a string grid, and I thought of using
TMemo boxes which are easily editable, but which it is hard to
get to resize to fit the text - while each string isn't going to
have carriage returns, I want the strings to word wrap to fit on
multiple lines if need be without scrollbars (except on the form
itself if needed).  Each reason is seperated from the last by a
bevel line which I put in a TList too....

Anyway the problem is that it all seems to be read in fine, it's
just not displaying anything - here is (hopefully) the relevant
code:

unit reasons;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, stdctrls, extctrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    theReasons:TList;
    theSeperators:TList;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
     theReasons:=TList.create;
     theSeperators:=TList.create;
end;

procedure TForm1.FormShow(Sender: TObject);
var
   i:integer;
   tmpReason:TLabel;
   tmpSeperator:TBevel;
begin

     tmpSeperator:=TBevel.Create(Form1);
     tmpReason:=TLabel.Create(Form1);
     tmpSeperator.parent:=(Form1);
     tmpReason.parent:=(Form1);
     tmpSeperator.visible:=true;
     tmpReason.visible:=true;
     tmpReason.Caption:='Moo!';

     theSeperators.add(tmpSeperator);
     theReasons.add(tmpReason);
     showmessage('tmpReason = '+tmpReason.caption+' and list = '
+TLabel(theReasons.Items[0]).caption);
end;

end.

This form is created and destroyed from the main form with the
following code:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs,
  StdCtrls;

type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

uses reasons;
{$R *.DFM}

procedure TForm2.Button1Click(Sender: TObject);
begin
     with TForm1.create(Application) do
     try
        ShowModal;
     finally
            free;
     end;
end;

end.

Ok the above form2 is the main form (the second one) and form1
is not automatically created in the project, but created when
the button is pressed.

The showmessage when the form is created does display:

tmpreason = Moo! and list = Moo!

so why aren't they displaying????  I've set their visable
properties to true, and put them in places where they will
definately be seen...

OK, I've tested it a bit more and found out something... If I
run form1 on it's own, it works fine, it's when form1 is not
created at startup but created when the button is pressed that
it doesn't work.  Can anyone tell me what I need to do to work
around this?  (ooh - I *did* have the code right, it just
doesn't work in this situation...)

Regards

OgO
--
Unless otherwise specified, all my posts relate to Delphi 3.

My Freeware: http://qjc.cjb.net/freeware.html
My Win9x Cursors: http://qjc.cjb.net/cursors.html
Feel free to ICQ me.  My ICQ #: 12889482
I left my Glasses in my email - you better take them out!

 

Re:More dynamic objects


Quote
"OgO" <quent...@qjc.cjbGlasses.net> wrote in message
> I wasn't originally going to use TLabels as I figured a
> TStringGrid would be the most suitable, but you can't have
> multi-line entries in a string grid, and I thought of using
> TMemo boxes which are easily editable, but which it is hard to
> get to resize to fit the text - while each string isn't going to
> have carriage returns, I want the strings to word wrap to fit on
> multiple lines if need be without scrollbars (except on the form
> itself if needed).  Each reason is seperated from the last by a
> bevel line which I put in a TList too....

     procedure BuildMemoEdits (theLines : tStrings; lft, tp : integer);

     var i : integer;

     begin
     for i := 0 to (Memo1.Lines.Count - 1) do
          begin
          with tMemo.Create (Self) do
               begin
               Visible := False;
               Parent := Self;
               WordWrap := True;
               SetBounds (lft, tp, 150, 50);
               Text := theLines [i];
               Visible := True;
               ClientHeight := (Lines.Count + 1) * Round
(Screen.PixelsPerInch / 72 * Font.Size);
               tp := tp + Height + 2;
               end;
          end;
     end;

Re:More dynamic objects


In comp.lang.pascal.delphi.misc, on 04 Dec 2001, Bruce Roberts
announced:

...

Quote
>                ClientHeight := (Lines.Count + 1) * Round
> (Screen.PixelsPerInch / 72 * Font.Size);
>                tp := tp + Height + 2;

...

Thanks for the suggestion, I may end up doing it that way, but
right now, that isn't main problem, which is that no matter what
I do it doesn't appear.... so I can't test things and see what
works or not...

I can't seem to declare dynamic data/controls in a form which is
itself created dynamically....

If I create the form at startup it works fine, but I don't want
to do that, because it's a waste of memory...

Does anyone have any ideas how I can make it work?

Regards

OgO
--
Unless otherwise specified, all my posts relate to Delphi 3.

My Freeware: http://qjc.cjb.net/freeware.html
My Win9x Cursors: http://qjc.cjb.net/cursors.html
Feel free to ICQ me.  My ICQ #: 12889482
I left my Glasses in my email - you better take them out!

Re:More dynamic objects


Quote
"OgO" <quent...@qjc.cjbGlasses.net> wrote in message
> Thanks for the suggestion, I may end up doing it that way, but
> right now, that isn't main problem, which is that no matter what
> I do it doesn't appear.... so I can't test things and see what
> works or not...

If you do, it'll work :-).

Quote

> I can't seem to declare dynamic data/controls in a form which is
> itself created dynamically....

> If I create the form at startup it works fine, but I don't want
> to do that, because it's a waste of memory...

Are you sure that creating the form automatically at startup works and
creating it dynamically doesn't. Because the code you show for creating the
form dynamically should work without a problem. However, in the control
creation code I don't see any setting of the top, left, width, or height
properties.

Re:More dynamic objects


In article <Xns916D667C67967ogo1mynxwowaust...@203.109.250.24>, OgO

Quote
<quent...@qjc.cjbGlasses.net> writes:
>I wasn't originally going to use TLabels as I figured a
>TStringGrid would be the most suitable, but you can't have
>multi-line entries in a string grid,

Whyever not <g> you just need to code the OnDrawCell event handler to draw the
multi-lined text as follows ...

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Longint;
                                     ARect: TRect; State: TGridDrawState);
var
  RowHt : integer;
  CellText : string;
begin
  with TStringGrid(Sender) do begin
    if (ARow < FixedRows) or (ACol < FixedCols) then begin
      {fixed cell colours}
      Canvas.Font.Color := clBlack;
      Canvas.Brush.Color := clBtnFace;
      end
    else
      if gdSelected in State then begin
        {selected cell colours}
        Canvas.Font.Color := clWhite;
        Canvas.Brush.Color := clNavy;
        end
      else begin
        {normal cell colours}
        Canvas.Font.Color := clBlack;
        Canvas.Brush.Color := clWhite;
      end;
    {end; if (ARow < FixedRows) or (ACol < FixedCols) else}
    Canvas.FillRect(ARect);
    CellText := Cells[ACol, ARow];
    InflateRect(ARect, -2, -2); // reduce for text margins
    {get calculated row height}
    RowHt := DrawText(Canvas.Handle, PChar(CellText), -1, ARect, DT_CALCRECT or
DT_WORDBREAK);
    {adjust if necessary}
    if (RowHt + 3) > DefaultRowHeight then
      RowHeights[ARow] := RowHt + 3;
    {draw text}
    DrawText(Canvas.Handle, PChar(CellText), -1, ARect, DT_WORDBREAK);
  end;
end;

Looking at your solution

Quote
>so why aren't they displaying????  I've set their visable
>properties to true, and put them in places where they will
>definately be seen...

I don't see any code to "put them in places". They should be placed somwhere on
and relative to their Parent setting Left, Top, Width & Height (or preferably
use SetBounds()) to position and size the items. I'm not sure why you use a
TList though.

Alan Lloyd
alangll...@aol.com

Re:More dynamic objects


"OgO" <quent...@qjc.cjbGlasses.net> skrev i melding
news:Xns916D667C67967ogo1mynxwowaustcom@203.109.250.24...

Quote
> procedure TForm1.FormShow(Sender: TObject);
> var
>    i:integer;
>    tmpReason:TLabel;
>    tmpSeperator:TBevel;
> begin

>      tmpSeperator:=TBevel.Create(Form1);
>      tmpReason:=TLabel.Create(Form1);
>      tmpSeperator.parent:=(Form1);
>      tmpReason.parent:=(Form1);
>      tmpSeperator.visible:=true;
>      tmpReason.visible:=true;
>      tmpReason.Caption:='Moo!';

>      theSeperators.add(tmpSeperator);
>      theReasons.add(tmpReason);
>      showmessage('tmpReason = '+tmpReason.caption+' and list = '
> +TLabel(theReasons.Items[0]).caption);
> end;

#1: Try to keep the TBevel out - it may be reciding on top of your label, ???
#2: You haven't set Label's Top and Left, from what I see
#3: Personally I don't like initialization code in the OnShow event...;-)

--
Bjoerge Saether
Asker, Norway
bjorge@hahaha_itte.no (remve the obvious)

Re:More dynamic objects


In comp.lang.pascal.delphi.misc, on 04 Dec 2001, Bj?rge S?ther
announced:

Quote
> #1: Try to keep the TBevel out - it may be reciding on top
> of your label, ???

I got rid of the TBevel, I needed something to seperate the
TLabels, but have since gotten rid of them in favour of TMemo's.

Quote
> #2: You haven't set Label's Top and Left,
> from what I see

I forgot to put that code in the example, but it's there :)

Quote
> #3: Personally I don't like initialization
> code in the OnShow event...;-)

Every time I try putting initialization code in the OnCreate
event (which I assume is where you think it should go?) I need
to do things like set components placement on the form which
crashes the program if the form hasn't yet been created... (I
think that's what happens, I haven't tried it lately -
definately doing some things to the form don't work until the
OnCreate event has finished.

I generally have a form global variable 'firstrun' which is a
boolean and set to true in the onCreate event

In the OnShow event I have something like:

     if (firstrun = false) then exit;
     firstrun:=false;

then the initialisation code.

Regards

OgO
--
Unless otherwise specified, all my posts relate to Delphi 3.

My Freeware: http://qjc.cjb.net/freeware.html
My Win9x Cursors: http://qjc.cjb.net/cursors.html
Feel free to ICQ me.  My ICQ #: 12889482
I left my Glasses in my email - you better take them out!

Re:More dynamic objects


In comp.lang.pascal.delphi.misc, on 04 Dec 2001, AlanGLLoyd
announced:

Quote
> In article
> <Xns916D667C67967ogo1mynxwowaust...@203.109.250.24>, OgO
> <quent...@qjc.cjbGlasses.net> writes:

>>I wasn't originally going to use TLabels as I figured a
>>TStringGrid would be the most suitable, but you can't have
>>multi-line entries in a string grid,

> Whyever not <g> you just need to code the OnDrawCell event
> handler to draw the multi-lined text as follows ...

I *could* do that yes :)

What I've ended up doing is using TMemo's rather than TLabels
and removed completely the TBevel's and it's working alright
except I do a check on each memo's onChange event to resize them
and keep it all looking nice.  One of the things I do is keep an
empty memo as the last in the list, if the user puts text in
that I create a new one below it and if the user removes all the
text from the second last memo I delete the last memo.

This is the code I have to delete the last memo (it get's called
at the right time - I've checked that):

theReasons is a TList (of TMemos)
numreasons is an integer which at this point represents the last
item in the list and
reasons is the name of the form:

          theReasons.delete(numreasons);
          theReasons.capacity:=theReasons.count;
          reasons.Refresh;

This seems to work partially, although the memo is left on
screen and you can still enter it and enter data in it (although
since it doesn't 'exist' that doesn't have any effect.

Quote
> Looking at your solution

I forgot to include the setting top, width etc in the example,
but it is set, sorry 'bout that, and thanks for your help.

Regards

OgO
--
Unless otherwise specified, all my posts relate to Delphi 3.

My Freeware: http://qjc.cjb.net/freeware.html
My Win9x Cursors: http://qjc.cjb.net/cursors.html
Feel free to ICQ me.  My ICQ #: 12889482
I left my Glasses in my email - you better take them out!

Re:More dynamic objects


In comp.lang.pascal.delphi.misc, on 04 Dec 2001, Bruce Roberts
announced:

Quote
> "OgO" <quent...@qjc.cjbGlasses.net> wrote in message

> If you do, it'll work :-).

I *have* now done it with TMemo's and loaded the form at program
startup and it works fine, except for the deleting the last
TMemo problem which I outlined in my reply to Alan Lloyd's reply
to my original question...

If I now change the form to not load on startup and change the
code where the form is called from:

     reasons.Show;

to:

     with treasons.create(Application) do
     try
        ShowModal;
     finally
            free;
     end;

The program loads fine, but when I click on the menu item which
brings up the troublesome window I get an:

EInvalidOperation exception error with message 'Control '' has
no parent window'

on the showmodal line.

I have created the TList with:

theReasons := TList.Create;

and create the TMemo's which get added to this list with:

     tmpReason:=TMemo.Create(reasons);

where reasons is the name of the form.

Quote
>> I can't seem to declare dynamic data/controls in a form
>> which is itself created dynamically....

>> If I create the form at startup it works fine, but I don't
>> want to do that, because it's a waste of memory...

> Are you sure that creating the form automatically at startup
> works and creating it dynamically doesn't. Because the code
> you show for creating the form dynamically should work
> without a problem. However, in the control creation code I
> don't see any setting of the top, left, width, or height
> properties.

Did the above answer that question - I don't understand why it
doesn't work if the form is created dynamically either - perhaps
it is a bug with Delphi 3?

As for the top and left etc I did set those, I just forgot to
put them in the example, my bad.

Regards

OgO
--
Unless otherwise specified, all my posts relate to Delphi 3.

My Freeware: http://qjc.cjb.net/freeware.html
My Win9x Cursors: http://qjc.cjb.net/cursors.html
Feel free to ICQ me.  My ICQ #: 12889482
I left my Glasses in my email - you better take them out!

Re:More dynamic objects


Quote
"OgO" <quent...@qjc.cjbGlasses.net> wrote in message
>      if (firstrun = false) then exit;

In Pascal one doesn't (and in fact shouldn't) test a boolean against a
boolean constant. The above line should be written

    if not FirstRun then exit;

Quote
>      firstrun:=false;

Personally I don't like using Exit, and there really isn't a need to use it.
One can just as easily write

    if not fHaveInitialized
    then begin
        fHaveInitialized := True;
        // the code
        end;

Re:More dynamic objects


Quote
"OgO" <quent...@qjc.cjbGlasses.net> wrote in message
> theReasons is a TList (of TMemos)
> numreasons is an integer which at this point represents the last
> item in the list and

Are you sure that NumReasons is the index of the last entry in theReasons,
or is it the last entry + 1?

Quote
> reasons is the name of the form:

>           theReasons.delete(numreasons);

There is no need for the following two lines. In fact, AFAIK tList doesn't
have a Refresh method.

Quote
>           theReasons.capacity:=theReasons.count;
>           reasons.Refresh;

> This seems to work partially, although the memo is left on
> screen and you can still enter it and enter data in it (although
> since it doesn't 'exist' that doesn't have any effect.

Deleting an entry in a tList doesn't release whatever the entry points to.
You want

        tMemo (theReasons [theReasons.Count - 1]).Free;
        theReasons.Delete (theReasons.Count - 1);

Re:More dynamic objects


In comp.lang.pascal.delphi.misc, on 05 Dec 2001, OgO
announced:

Quote
> If I now change the form to not load on startup and change
> the code where the form is called from:

>      reasons.Show;

> to:

>      with treasons.create(Application) do
>      try
>         ShowModal;
>      finally
>             free;
>      end;

> The program loads fine, but when I click on the menu item
> which brings up the troublesome window I get an:

> EInvalidOperation exception error with message 'Control ''
> has no parent window'

> on the showmodal line.

> I have created the TList with:

> theReasons := TList.Create;

> and create the TMemo's which get added to this list with:

>      tmpReason:=TMemo.Create(reasons);

> where reasons is the name of the form.

Ok, I've found the problem, it's in that tmpReason.  It seems
fine and crashes on the two lines marked (it crashes on the
first, if I comment it out, it crashes on the second - both with
that 'control has no parent window' exception' and if I comment
them both out, the program runs fine, but nothing gets added to
the TMemo by the program (because I have to comment out any
lines which clear or add to the TMemo....

var
   i,totalnum,linenum:integer;
   tmpReason:TMemo;
begin

     regdata:=TRegistry.Create;
     tmpReason:=TMemo.Create(reasons);
     tmpReason.parent:=(reasons);
     tmpReason.visible:=true;

     tmpReason.Lines.Clear;   // these two lines crash...
     tmpReason.Lines.Add(''); // I don't know why...

Any Ideas?

Regards

OgO
--
Unless otherwise specified, all my posts relate to Delphi 3.

My Freeware: http://qjc.cjb.net/freeware.html
My Win9x Cursors: http://qjc.cjb.net/cursors.html
Feel free to ICQ me.  My ICQ #: 12889482
I left my Glasses in my email - you better take them out!

Re:More dynamic objects


In comp.lang.pascal.delphi.misc, on 05 Dec 2001, Bruce Roberts
announced:

Quote
> "OgO" <quent...@qjc.cjbGlasses.net> wrote in message

> There is no need for the following two lines. In fact, AFAIK
> tList doesn't have a Refresh method.

>>           theReasons.capacity:=theReasons.count;
>>           reasons.Refresh;

the 'reasons.refresh;' line is a bit useless, but according to
help for the delete call which I used just before this:

Delete removes the item at the position given by the Index
parameter.

procedure Delete(Index: Integer);

Description

Call Delete to remove the item at a specific position from the
list. The index is zero-based, so the first item has an Index
value of 0, the second item has an Index value of 1, and so on.
Calling Delete moves up all items in the Items array that follow
the deleted item, and reduces the Count.
To remove the reference to an item without deleting the entry
from the Items array and changing the Count, set the Items
property for Index to nil.

Note

Delete does not free up any memory associated with the item. To
free the memory that was used to store a deleted item, set the
Capacity property.

So doesn't that mean that I *do* need that line to set the
capacity in order to free the memory?

id = the tag of the current TMemo, which is set to the index of
that TMemo in the TList.  This is so that I can have each TMemo
call the one function for the OnChange event, and use the tag of
the sender to determine which TMemo has changed.

Currently if a memo which isn't the only one and isn't the last
one in the list has all it's text removed, this code is executed
(to remove that TMemo from the form and the list):

// move all the items in the list up one
// delete doesn't seem to work properly as it should do this
// but doesn't properly because I then get exceptions accessing
// the 'new' item in place of the deleted one.

          for counter:=id to theReasons.count-2 do
               theReasons.Exchange(counter,counter+1);

// delete the last item and free it's memory
          TMemo (theReasons[theReasons.count-1]).Free;
          theReasons.Delete(theReasons.count-1);
          theReasons.capacity:=theReasons.count;
          numreasons:=numreasons-1;

// make sure the tag property of each item is set properly
          for counter:=0 to theReasons.count-1 do
              TMemo(theReasons[counter]).tag:=counter;

This seems to work fine now.

Thanks for your help.

Regards

OgO
--
Unless otherwise specified, all my posts relate to Delphi 3.

My Freeware: http://qjc.cjb.net/freeware.html
My Win9x Cursors: http://qjc.cjb.net/cursors.html
ICQ: 12889482 - AIM/Yahoo: quentisl - MSN: quent...@mail.com
I left my Glasses in my email - you better take them out!

Re:More dynamic objects


In comp.lang.pascal.delphi.misc, on 05 Dec 2001, Bruce Roberts
announced:

Quote
> "OgO" <quent...@qjc.cjbGlasses.net> wrote in message

>>      if (firstrun = false) then exit;

> In Pascal one doesn't (and in fact shouldn't) test a boolean
> against a boolean constant. The above line should be written

>     if not FirstRun then exit;

Ok, is that because the second way uses less instructions, or is
there another reason?

Quote

>>      firstrun:=false;

> Personally I don't like using Exit, and there really isn't a
> need to use it. One can just as easily write

>     if not fHaveInitialized
>     then begin
>         fHaveInitialized := True;
>         // the code
>         end;

Yes, good idea - actually when I originally wrote that part of
the code I was at the same time testing to see when the formshow
event was called.

Speaking of when events are called, I discovered something
strange with my program:

Because of the error/bug/oddity/whatever, whereby I can't simply
initialise the reasons form on demand because when I try
assigning text to the memo's in the TList on that form I get the
exception that they don't have a window parent..... I have to
initialise that form on startup and have it wasting that extra
few bytes of memory.

And because it gets upset if I try playing with window
components before the form is shown I have to do most of the
'initialisation' in the OnShow event as above.

The TList is populated in the OnShow event on it's first run.  
The form resise event does things with the items in the list,
making sure they are in the right place.  I noticed that if I
don't actually go to that form (and thus run the onshow event),
the program crashes on exit trying to run the forms onresise
event - what's the deal there????

I've since worked around the problem, but it's the same reason
that the onshow code wasn't written properly in the first place
- Delphi does some unexpected things!

Regards

OgO
--
Unless otherwise specified, all my posts relate to Delphi 3.

My Freeware: http://qjc.cjb.net/freeware.html
My Win9x Cursors: http://qjc.cjb.net/cursors.html
ICQ: 12889482 - AIM/Yahoo: quentisl - MSN: quent...@mail.com
I left my Glasses in my email - you better take them out!

Re:More dynamic objects


Quote
"OgO" <quent...@qjc.cjbGlasses.net> wrote in message
> Delete does not free up any memory associated with the item. To
> free the memory that was used to store a deleted item, set the
> Capacity property.

> So doesn't that mean that I *do* need that line to set the
> capacity in order to free the memory?

The memory being released is just 4 bytes. Unless you are doing a large
number of deletions without insertions or additions its not worth the
overhead.

Quote

> id = the tag of the current TMemo, which is set to the index of
> that TMemo in the TList.  This is so that I can have each TMemo
> call the one function for the OnChange event, and use the tag of
> the sender to determine which TMemo has changed.

> Currently if a memo which isn't the only one and isn't the last
> one in the list has all it's text removed, this code is executed
> (to remove that TMemo from the form and the list):

> // move all the items in the list up one
> // delete doesn't seem to work properly as it should do this
> // but doesn't properly because I then get exceptions accessing
> // the 'new' item in place of the deleted one.

>           for counter:=id to theReasons.count-2 do
>                theReasons.Exchange(counter,counter+1);

If you are using Tag to hold the index you will have to adjust it as well.
So what you need is
        for counter := id to theReasons.Count - 2 do
            begin
            theReasons.Exchange (counter, counter + 1);
            tMemo (theReasons [counter]).Tag := counter;
            end;

However there is a way to work without using the Tag. Its less efficient,
but unless you are working with a very large number of entries or doing alot
of references it will simplify the code. Use the list's IndexOf method with
the memo as the parameter.

Quote

> // delete the last item and free it's memory
>           TMemo (theReasons[theReasons.count-1]).Free;
>           theReasons.Delete(theReasons.count-1);
>           theReasons.capacity:=theReasons.count;

Adjusting Capacity isn't worth the overhead when a single deletion takes
place.

Quote
>           numreasons:=numreasons-1;

You really don't need to keep numReasons. Use theReasons.Count instead.

Quote

> // make sure the tag property of each item is set properly
>           for counter:=0 to theReasons.count-1 do
>               TMemo(theReasons[counter]).tag:=counter;

Oh. I see.
Go to page: [1] [2]

Other Threads