Board index » cppbuilder » Re: Deriving from TCustomListView

Re: Deriving from TCustomListView


2003-09-19 03:05:45 AM
cppbuilder70
"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >wrote:
Quote

If you were to look at ComCtrls.hpp, for example, you would see
that TListView does nothing more than promote TCustomListView's protected
properties. TListView does not add any extra functionality to
TCustomListView.

Well, how do you see that? the hpp only includes the header and you see that
the constuctos/destructors are void. But the rest of the lib could do
astonishing extras, or not?
Quote

>Is that generally the case with the vcl class strategy?

Yes. Borland introduced the TCustom... classes specifically for the
purpose

Good decision from Borland.
Quote

As I suspected, you are misusing the properties. To promote an existing
property from protected to public/published, you do not need to include
the
full declarations, just the property names only. In other words:

Ok, but they all compile and work except Colums. Was just curious what was
wrong, as it is not so important: including from hpp is faster and does not
cause that kind of problems.
Quote
>typedef TCustomListView inherited;

It is not necessary, but it is common, particularly if your own code will
ever be invoking base class behaviors. It just makes the code easier to
read and understand what it is actually doing.
Like your statement 'makes the code easier to read and understand'. :-)))
Quote

So the typedef is a
workaround for allowing C++ code to be similar to Delphi code.

Vade retro Delphi.
Quote
>Further I have can not change the propertie's value. What I want
>is the IDE Objetc inspector to for instance set the ViewStyle to
>vsReport and not vsIcon. How do you do that?

See above.

Got it. It means that installing (IDE) the actual constructor is called and
the objector inspector 'reads back' the values?
Quote

>What it is good for then? Only to limit the resource saved values?

Yes, it is only used by the DFM streaming system to cut down on the
overhead
of storing and retrieving unnecessary values.

Which means a good component should properly set the default value for all
those properties which are set by the constructor?
Quote

>Finally, how can I assign an icon for use in the components palette
AFTER
>the component has been created?

Use the Image Editor that comes with BCB to set up a .RES file containing
the desired bitmap, make sure the bitmap itself inside the .RES file is
named the same as the component class name, in all caps, ie MLSVVREP1.
And
then simply add the .RES file to your component's package.

Not really. I have a dcr (and not res) file, which may not be added. Yet
originally it was part of the component like project.
It is not in all-capital, but also the original was. It worked with BCB6,
but specified creating the component.
A final question: how can you reload the component after modifications? I
worked out a way ( Components | Install Package to remove the entire bpl,
then Components | Add Component to reinstall it), but doubt it is the
best/correct way.
Rgds, Martin
 
 

Re:Re: Deriving from TCustomListView

"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >wrote in message
Quote
Do not specify the data type when promoting an existing property. Only
the
name. The only time you specify a data type and read.write specifiers is
when you are actually creating a new property that you are going to
actually
implement in your own code.
That'll be thing I learned today <g>Thanks.
Ralph
 

Re:Re: Deriving from TCustomListView

"Martin Moeller" < XXXX@XXXXX.COM >wrote in message
Quote
Well, how do you see that? the hpp only includes the header and
you see that the constuctos/destructors are void. But the rest of
the lib could do astonishing extras, or not?
No. Just like C++, Pascal has to follow the rules that you cannot use
something that is not declared first. The HPP header files are just
IDE-generated C++ header files for the VCL's Pascal code, and as such as
(almost) an exact mirror of that code. If you do not see something declared
in the HPP files, then they most likely are not declared in the Pascal code
either. In order for a class to do something extra on top of its base
class, whether the class is written in Pascal or C++, the class's
declaration in the header must include declarations for the new
functionality, whether that be new members, overridden menbers, etc. As you
can clearly see, TListView does not declare anything new which
TCustomListView does not already have. And its generated
constructors/destructor are empty (which, btw, they don't even exist in the
Pascal code at all, the C++ IDE auto-generated them only for the sake of the
HPP code being compliant with the C++ language).
Besides, Boland distributes the VCL source code anyway, so I can tell you
with 100% certainty that TListView does not do anything extra at all.
Quote
Ok, but they all compile and work except Colums.
Columns (plural) works fine for me. Did you use the EXACT code that I gave
you? I notice that you were trying to publish the Canvas and Column[]
(singular) properties before. You cannot publish those, as 1) they are both
read-only and you can only published read/write properties, and 2) you
cannot publish properties that use array notation. They both have to remain
public instead of published. And in fact they are both already declared as
public in TCustomListView, so there is no need to promote them anyway.
Quote
Got it. It means that installing (IDE) the actual constructor is
called and the objector inspector 'reads back' the values?
Component instances you see at design-time on a form are real instances.
They have been constructed normally and they reside in memory normally and
they are executing as if you were running outside of the IDE normally. So
yes, the Object Inspector is merely displaying the current values from an
actual running instance of the component. The only difference is that the
VCL has support for detecting the presence of the IDE so that is can
internally control how certain component actions behave, such as mouse
clicks and such. In all other regards, the component instance is real and
its coding is being executed as it would be outside of the IDE.
Quote
Which means a good component should properly set the default
value for all those properties which are set by the constructor?
Only those properties with values other than the defaults. No need to
reassign a value that is already assigned.
Quote
Not really.
Yes, really. I have been using custom RES files for all of my own
components for years and it works fine from BCB3 all the way of to BCB6
without problem.
Quote
I have a dcr (and not res) file, which may not be added.
I told you to creae a *new* RES file.
Some will say that it is possible to use DCR files. Some will say that they
have done it successfully. I believe them. However, I have never had a
single DCR file work correctly for me, which is why I always recommend RES
instead. Besides, DCR is a Delphi file anyway. For a C++ component, RES is
just easier to work with anyway, in my opinion.
Quote
A final question: how can you reload the component after modifications?
Simply recompile the package. If it is already installed, the IDE will
start using the new library files immediately.
Quote
I worked out a way ( Components | Install Package to remove the
entire bpl, then Components | Add Component to reinstall it)
That will work as well, though it is a little redundant.
Gambit
 

{smallsort}

Re:Re: Deriving from TCustomListView

"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >wrote:
Quote

Use the Image Editor that comes with BCB to set up a .RES file containing
the desired bitmap, make sure the bitmap itself inside the .RES file is
named the same as the component class name, in all caps, ie MLSVVREP1.
And
then simply add the .RES file to your component's package.

However, I have never had a
single DCR file work correctly for me, which is why I always recommend RES
instead. Besides, DCR is a Delphi file anyway. For a C++ component, RES
is
just easier to work with anyway, in my opinion.

Still not working.
Now I have the res file, well named (in caps, same name as component) in the
same directory as the cpp.
It could be addded once but showed not the icon, so I deleted it from the
package window and checked.
Now I can not add it again, it opens an error window with following text:
' the project already contains a form or module named MLSVVREP1. '
Pls help, thanks, Martin
 

Re:Re: Deriving from TCustomListView

"Martin Moeller" < XXXX@XXXXX.COM >wrote:
Quote


>
>Use the Image Editor that comes with BCB to set up a .RES file
containing
>the desired bitmap, make sure the bitmap itself inside the .RES file is
>named the same as the component class name, in all caps, ie MLSVVREP1.
And
>then simply add the .RES file to your component's package.
>

>However, I have never had a
>single DCR file work correctly for me, which is why I always recommend
RES
>instead. Besides, DCR is a Delphi file anyway. For a C++ component,
RES
is
>just easier to work with anyway, in my opinion.
>

Still not working.

Now I have the res file, well named (in caps, same name as component) in
the
same directory as the cpp.
It could be addded once but showed not the icon, so I deleted it from the
package window and checked.

Now I can not add it again, it opens an error window with following text:

' the project already contains a form or module named MLSVVREP1. '

Probably got it.
- The bmp within the resource file MUST be all caps and have the same name
as the component
- The res file does not have to be all caps and MUST NOT have the same name
as cpp file.
That allows loading and now displays fine. As a matter of fact, I have the
same name for component and its cpp file.
Thanks, Martin
 

Re:Re: Deriving from TCustomListView

"Martin Moeller" < XXXX@XXXXX.COM >wrote in message
Quote
Now I have the res file, well named (in caps, same name as
component) in the same directory as the cpp.
Did you name the RES file itself, or the bitmap INSIDE the RES file? It
sounds like you named the file. You are supposed to be naming the actual
bitmap instead, not the RES file itself. That is another of the beauties I
like about using RES files - the file can be named anything you want.
Whereas with DCR, the file has to be specifically named after the component
it belongs to.
Gambit
 

Re:Re: Deriving from TCustomListView

"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >wrote in message
Quote
Whereas with DCR, the file has to be specifically named after the
component
it belongs to.
What do you mean? I have .dcr files attached to packages named differently
than the component. As long as the bitmap is named after the component (all
caps) it'll work.
Ralph
 

Re:Re: Deriving from TCustomListView

"Ralph Kazemier" < XXXX@XXXXX.COM >wrote:
Quote

>Whereas with DCR, the file has to be specifically named after the
component
>it belongs to.

What do you mean? I have .dcr files attached to packages named
differently
than the component. As long as the bitmap is named after the component
(all
caps) it'll work.

Confirm. While, tried and retried, it does not work with res files: they
must be named differently res and cpp, and res does not have to be all-caps.
The naming in my project, as asked by Remy, is very simple: same name all
over (component class & component source file, bitmap all-caps and res
file - which does not work). Also tried removing both files from package;
adding res file first is ok, but get the same error on adding cpp file
later.
Anyhow, what should the strategy be (question) :
- very limited number of packages, for component types
- single res file for bitmap or several bitmaps in one res file (a- is it
possible, b- does it make sense)
?
Tks & Regards, Martin
 

Re:Re: Deriving from TCustomListView

"Martin Moeller" < XXXX@XXXXX.COM >wrote in message
Quote
Anyhow, what should the strategy be (question) :

- very limited number of packages, for component types
- single res file for bitmap or several bitmaps in one res file (a- is it
possible, b- does it make sense)
Though there might be other possibilities, this is how I work;
1) Open Image Editor
2) Select 'File|New...|Component Resource File (.dcr)'
3) Right click 'Contents' in the treeview
4) Select 'New|Bitmap'
5) Set Width and Height to 24. Set Colors to 256.
6) Rename the new bitmap entry to the name of the component in all caps
7) Draw the bitmap
Repeat steps 3-7 for each component you're installing
8) Select 'File|Save'
9) Typically the .dcr file is saved in the same directory
where the package resides
10) Open the package in BCB
11) Open the main .cpp file
You'll probably see something like;
//----...
#include <vcl.h>
#pragma hdrstop
USERES("myPackage.res");
12) Add the your resource file, so that it looks something like;
//----...
#include <vcl.h>
#pragma hdrstop
USERES("myPackage.res");
USERES("myResource.dcr");
13) Build & Install the package
Ralph
 

Re:Re: Deriving from TCustomListView

"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >wrote
Quote

No. Just like C++, Pascal has to follow the rules that you cannot use
something that is not declared first.

Besides, Boland distributes the VCL source code anyway, so I can tell you
with 100% certainty that TListView does not do anything extra at all.

Still I should understand it also without asking every time..
Suppose that what I should have noticed is that TListView declares no new
members nor methods,
Quote

Columns (plural) works fine for me. Did you use the EXACT code that I
gave
you? I notice that you were trying to publish the Canvas and Column[]
(singular) properties before. You cannot publish those, as 1) they are
both
read-only and you can only published read/write properties, and 2) you
cannot publish properties that use array notation. They both have to
remain
public instead of published. And in fact they are both already declared
as
public in TCustomListView, so there is no need to promote them anyway.

Forget it, probably I messed up column and columns, maybe also declarations
from TListView and TCustomListView. If at all, deriving from a Custom class
to promote all first is better done pasting from the header file rather than
from the help anyhow.
Quote

>Which means a good component should properly set the default
>value for all those properties which are set by the constructor?

Only those properties with values other than the defaults. No need to
reassign a value that is already assigned.

Too many defaults. From what I understood, the value 0 (also NULL and false)
is anyhow assigned by the compiler creating a class instance (with or
without beeing a component), thus no assignement is needed while it should
still be specified as { default } in order to avoid unnecessary saving of
that value in the resource file. If I want a different value, I need to
write the assignement statement into the constructor (typically), in which
case I can/should as well specify the {default}. Values without {default} or
set to a different value than what specified, will all be saved to projects
res file.
Quote

I have been writing custom VCL
components for several years, and I am in the VCL's own source code almost
daily. I know how the VCL works.

Taken good notice...
With the aim to adress specifically a virtual TListView in Report mode, I
was considering the opportunity to enhance the TListColumn to include more
references to the data displayed and their coordinates.
But then there is a general uncertity I have: it is quite easy to derive a
TListColumn adding my wanted features, but then the pointer in the
TCustomListView is still of type original TListColumn. Therefore, without
rewriting the whole column part, he will try to allocate standard
TListColums and crash as my class has a different size. Thus no way to
override TListColumn maintaining the rest of the original methods. Or is
there another trick ?
And considering my very limited BCB skills, would it be possible at all (for
me) ?
Finally, I learn that components are compiled to obj when inserted and
linked from there. How about the debugg information, will he somehow
automatically strip them as per the general project setting ( and recompile
the component if necessary), or will I have to do this manually loading the
non-debugg version once it is stable enough.
And as they are pre-compiled, there can be no template components as well as
any template component members. What I'm thinking of is to at least
partially automate the data flow between my virtual report viewer and the
std::container holding the data. It's quite vague still, but somehow I'm
looking for the possibility to set the pointer/refrence of the container
into the ListView and call some standard interface functions.
Can I hardcode an event handler? Not publishing it, but beeing called to
execute my fixed build in code?
Tks & Regds, Martin
 

Re:Re: Deriving from TCustomListView

"Martin Moeller" < XXXX@XXXXX.COM >wrote in message
Quote
Confirm. While, tried and retried, it does not work with res files:
they must be named differently res and cpp
Tell that to my own component packages which have RES and CPP files named
the same and they work fine :-p.
What you're referring to is only the IDE trying to side-step you. Although
the IDE doesn't like files being named the same, the compiler and linker are
perfectly fine with it. I just modify the project's main files manually to
get around the IDE's complaining. Works fine.
Quote
- single res file for bitmap or several bitmaps in one res file (a- is it
possible, b- does it make sense)
Yes, it is possible to have multiple bitmaps in a single RES file.
Perticularly useful when you have multiple components in the same package.
I don't thing DCR supports that, every component would need its own
individual DCR file instead.
Gambit
 

Re:Re: Deriving from TCustomListView

"Ralph Kazemier" < XXXX@XXXXX.COM >wrote in message
Quote
Though there might be other possibilities, this is how I work;
<snip>
What you describe is what the "Add to Project" option of the IDE already
does.
Gambit
 

Re:Re: Deriving from TCustomListView

"Martin Moeller" < XXXX@XXXXX.COM >wrote in message
Quote
Still I should understand it also without asking every time..
Look in the help file at the "Publishing inherited properties" topic.
Quote
From what I understood, the value 0 (also NULL and false) is anyhow
assigned by the compiler creating a class instance (with or without beeing
a component)
Only for classes which are derived from TObject, as TObject explitically
zeros out its own memory during its construction. That is not the case for
non-TObject classes. The memory for those classes is left uninitialized, it
is your own responsibility to initialize your class members yourself
appropriately.
Quote
If I want a different value, I need to write the assignement statement
into the constructor (typically)
No, not typically - always. That is the only place default values can
safely by assigned. That is what constructors are meant for.
Quote
But then there is a general uncertity I have: it is quite easy to derive a
TListColumn adding my wanted features, but then the pointer in the
TCustomListView is still of type original TListColumn.
Correct, as the Columns property is managed at the TCustomListView level.
You have no control over it.
Quote
Therefore, without rewriting the whole column part, he will try to
allocate standard TListColums and crash as my class has a different size.
No, it will not crash. It will, however, pre-allocate the standard
TListColumns collection before your own code has any chance to run.
Your only option is to reimplement the entire Columns mechanism, either as
an override to the existing Columns property, or as a secondary property.
TCustomComboBoxEx has to do something like that in BCB6 for its new ItemsEx
property, for example.
Quote
Finally, I learn that components are compiled to obj when inserted
and linked from there. How about the debugg information, will he
somehow automatically strip them as per the general project setting
( and recompile the component if necessary), or will I have to do this
manually loading the non-debugg version once it is stable enough.
Component packages are still projects like any other. You can compile
packages in both Release and Debug modes. The compiling mode of a package
is independant of the compiling of the application that is using the
package. You can have a Debug package in a Release application, and vice
versa. Of course, to actually step through and debug the package, it has to
be compiled in Debug mode, its TDS file has to be present on the IDE's
search path (since Borland project do not compile the debug info directly
into the code, they use a separate TDS file instead), and the application
usually has to be compiled in Debug mode as well.
Quote
Can I hardcode an event handler?
Components should never assign handlers to their own events. That prevents
the user's code from assign its own handlers. What you should be doing
instead is overriding the inherited methods that are responsible for
triggering the events in the first place. For example, to handle the OnData
event, override the OwnerDataFetch() method instead.
Quote
Not publishing it
If you do not publish it yourself, there is nothing stopping the user from
publishing it manually with another descendant class if they really want
their own access.
Gambit
 

Re:Re: Deriving from TCustomListView

"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >wrote in message
Quote

What you describe is what the "Add to Project" option of the IDE already
does.
The Add to Project option can not be used for .dcr files. It will only
accept .c, .cpp, .obj, .asm, .rc, .res, .pas, .lib, .dcu or .def files.
Ralph
 

Re:Re: Deriving from TCustomListView

"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >wrote:
Quote

>From what I understood, the value 0 (also NULL and false) is anyhow
>assigned by the compiler creating a class instance (with or without
beeing
>a component)

Only for classes which are derived from TObject, as TObject explitically
zeros out its own memory during its construction. That is not the case
for
non-TObject classes.

Fills a gap: some 'strange' behaviours make sense now.
Quote

>If I want a different value, I need to write the assignement statement
into the constructor (typically)

No, not typically - always.

Ok, the alternative I meant was an initializing function called by allocator
and whenever a restart is required.
Quote

>Therefore, without rewriting the whole column part, he will try to
>allocate standard TListColums and crash as my class has a different
size.

No, it will not crash. It will, however, pre-allocate the standard
TListColumns collection before your own code has any chance to run.

Your only option is to reimplement the entire Columns mechanism, either as
an override to the existing Columns property, or as a secondary property.

Suppose that reimplementing the entire Column mechanism means (the easy
part) to override the columns property by a MyColumns, but then that must be
handling all what the Columns did.
Otherwise, given that MyColumns derives from Columns and adds both a few
variables and a few functions, I just can not immagine how that could step
into the shorter allocate space. Also, provided I allocated entirely by
myself the MyColumns array (or list), would it then be sufficient for the
rest of the component to declare a MyColumns function calling the
Columns::function, ( plus what is needed for the added performance of
course) ?
And, sorry for a standard C++ question, will I have only to bother about the
protected/public members and methods such as listed in the help as per
object oriented programming logic?
No problem with secondary property (suppose you mean a second array/list
complementing those data of the Columns); it's just that such a solution
wouldn't be too smart.
Quote

>Can I hardcode an event handler?

Components should never assign handlers to their own events. That
prevents
the user's code from assign its own handlers. What you should be doing
instead is overriding the inherited methods that are responsible for
triggering the events in the first place. For example, to handle the
OnData
event, override the OwnerDataFetch() method instead.

Ok, one of the major change to virtual data. But slso knowing that
OnDataFetch() generates OnData event, from the documentation it is unknown
when it is called and what it does with the item passed. Therefore, having
to replace it and without calling TCustomListView::OnDataFetch() to avoid
the OnData event, I can override it but how can I know what to put into it?
Rgds, Martin
P.S. will be away for the rest of the week - don't cancel the thread pls.