I've often tried to do the owner draw bit but I never seem to get it right,
so I have come to the classroom to learn.
What I want is simple, or at least simple enough, since I like the vsReport
stile of the TListView, however I would like some particular controls over
the individual "cells".
I know they aren't really cells, but visually that's what they look like.
Anyway, given TListView:
Field Value //Column Heads
Start X,Y
Finish X,Y
Dif X,Y
You may notice (or not given the formatting of the news post) that "Dif" is
indented. Now I could hack it and use a couple of spaces, but Instead I
would like to add individual Item/SubItem processessing that will add these
two main things to each "Item" (and possibly SubItem in the future but that
isn't the issue now):
Alignment - Left, Center, Right
FontStyle - Bold, Italic, Underline
Now, I thought I would have to do this via the OwnerDraw so I set up the
event, and when I got the Rect I did a simple:
begin
List:=(Sender as TListView);
List.Canvas.TextOut(Rect.Left, Rect.Top, Item.Caption);
end;
Nothing happened. No text was displayed, nothing.
So I don't know what I'm doing wrong here. Now there is a possibility I
could skip this and use the AdvancedCustomDraw or CustomDraw methods, but
I'm not sure how I would be able to affect an individual Cell without
affecting the entire column/row.
Thanks
Jaeden "Sifo Dyas" al'Raec Ruiner
Fluffy
--
Jaeden "Sifo Dyas" al'Raec Ruiner
aka Fluffy
http://www.wayoftheleaf.net/
> Alignment - Left, Center, Right
TListColumn already has an Alignment property.
> FontStyle - Bold, Italic, Underline
For that, use the OnAdvancedDrawItem event.
> Now, I thought I would have to do this via the OwnerDraw
> so I set up the event
Which event did you use specifically? TListView has several
available.
> List.Canvas.TextOut(Rect.Left, Rect.Top, Item.Caption);
Use TextRect() instead of TextOut() so that long text does not overlap
into the adjacent columns. For drawing aligned text manually, use the
Win32 API DrawText() function intead.
> Nothing happened. No text was displayed, nothing.
Did you set the OwnerDraw property to true?
Gambit
I'm aware of this, but that Alignment property affects the entire "Column",
thus if I want row 1 & 2 left align but row 3 Right aligned... You get my
point.
>> FontStyle - Bold, Italic, Underline
>
> For that, use the OnAdvancedDrawItem event.
But what would I do there? Change the ListView.Font /
ListView.Column[Index].Alignment properties before the drawing stage?
>
>> Now, I thought I would have to do this via the OwnerDraw
>> so I set up the event
>
> Which event did you use specifically? TListView has several
> available.
I set up the only "OwnerDraw" event specifically tagged by the OwnerDraw
Property when set to true. All the others are "CustomDraw", either the
Advanced or standard CustomDraw events. OwnerDraw event is specifically
named ListView1DrawItem(), (plus it is the only "ownerdraw" function that
passes a Rect parameter.)
>
>> List.Canvas.TextOut(Rect.Left, Rect.Top, Item.Caption);
> Use TextRect() instead of TextOut() so that long text does not overlap
> into the adjacent columns. For drawing aligned text manually, use the
> Win32 API DrawText() function intead.
As well I could just as easily uses the Ellipsis funtion that shortens a
string given a specific extent with the "..." at the end. I'm well aware of
these functions, but when the text doesn't even display on the screen that
begs a larger issue is at work other than which textout function to use.
(though I will look into API DrawText() and see if that helps.)
>
>> Nothing happened. No text was displayed, nothing.
>
> Did you set the OwnerDraw property to true?
Naturally.
Thanks
Jaeden "Sifo Dyas" al'Raec Ruiner
Fluffy
> But what would I do there? Change the ListView.Font /
> ListView.Column[Index].Alignment properties before the drawing
stage?
No. You would change the ListView.Canvas.Font and then render the
text using DrawText().
> I set up the only "OwnerDraw" event specifically tagged by
> the OwnerDraw Property when set to true.
That is not the only drawing event available.
> All the others are "CustomDraw"
So? They are still owner-drawing events.
> OwnerDraw event is specifically named ListView1DrawItem()
So? That event only handles one particular aspect of owner-drawing.
There are others available.
> plus it is the only "ownerdraw" function that passes a Rect
parameter.
Not true. All of the other events have access to a TListItem, which
you can get a Rect from via the TListItem.DisplayRect() method. You
can also use the ListView_GetSubItemRect() function to fine-tune the
Rect further.
Gambit
--
>> But what would I do there? Change the ListView.Font /
>> ListView.Column[Index].Alignment properties before the drawing
> stage?
>
> No. You would change the ListView.Canvas.Font and then render the
> text using DrawText().
> ...
> Not true. All of the other events have access to a TListItem, which
> you can get a Rect from via the TListItem.DisplayRect() method. You
> can also use the ListView_GetSubItemRect() function to fine-tune the
> Rect further.
Wow, okay, that helps a lot. Using OnCustomDrawItem I can thus get the Item
Rect from the TListItem passed to the event method.
I found out my problem from before and solved it (code glitch my bad).
However, Now the issue is with this:
ListView_GetSubItemRect()
I have found no such method/function. Where is it?
Thanks
Jaeden "Sifo Dyas" al'Raec Ruiner
>
>
> Gambit
>
>
Well, I've got it somewhat working and thus have found myself as step closer
to my goal of understanding the OwnerDraw features of this particular
component.
However, I still have a big glitch: Refreshing.
So I've got the OnCustumDrawItem() event doing a nifty little lookup into an
array (for temporary purposes) and thus the Items draw accordingly.
I can affect:
-Alignment (left, right, center)
-Font Size (8pt, 10pt, etc)
-Font Style (Italic, underline, bold, etc)
however, If i do affect the alignment to be other than left justified, if
you resize the ListView columns at Run-Time, the previously "painted" text
doesn't erase, so I'm getting a bitt of a trail left behind the text as it
moves (effectively redrawing itself on top of its predecessor at a lesser X
coord value).
How do I get the Item to repaint the background, or do I have to do that
myself?
Thanks
Jaeden "Sifo Dyas" al'Raec ruiner
ps - If I have to do it myself, I expect that would involve the usage of ???
Canvas.Brush (to set the fill color of the rectangle)?
Canvas.Pen (to Set the Border color of the rectangle for "focused" state?
FillRect() or some such method to actually repaint the background of the
Item/Cell?
--
> However, Now the issue is with this:
>
> ListView_GetSubItemRect()
>
> I have found no such method/function. Where is it?
In commctrl.pas.
Gambit
> however, If i do affect the alignment to be other than left
> justified, if you resize the ListView columns at Run-Time,
> the previously "painted" text doesn't erase, so I'm getting a
> bitt of a trail left behind the text as it moves (effectively
> redrawing itself on top of its predecessor at a lesser X
> coord value).
That is a bug in the underlying ListView control itself.
> How do I get the Item to repaint the background, or do I
> have to do that myself?
You couls use the ListView's OnResize event to call its Invalidate()
method, ie:
procedure TForm1.ListView1Resize(Sender: TObject);
begin
TListView(Sender).Invalidate;
end;
Gambit
Given that I now have a greater understanding of the way the TListView
operates I have begun designing an actual component around what I want. It
is simplistic, mind you, primarily intended to give me the functionality I
desire for this project and possibly others, but nothing grand scale.
My idea was simple:
For Each TListView.Items.Item
- Retain Specific Font/Alignment characteristics for the Item
- Retain Specific Font/Alignment characterstics for each SubItem
This can be done very easily, but in many ways I appear to have to always
hook my design into the "OnDrawItem" or into the
"OnCustomDrawItem/OnCustomDrawSubItem" events. The interesting this is that
there appears to be a message method in TCustomListView called
CNNotify().
Now in the protected virtual method of TCustomListView.DrawItem(), the
default component checks to see if the item is custom drawn, and if not it
draws the Item.Caption and exits. This in no way depicts the "Default
Drawing" of the SubItems for that Item if in vsReport view. Thus There does
not seem to be anything that defaultly draws the subitems for each
particular item in the TListView, however as we all know the SubItems DO get
drawn. So where are they drawn? What method in the TListView, TListItems,
TListItem, TSubItem object tree actually "draws" the SubItem text?
I Did find in the CNNotify() Method the result value: CDRF_NOTIFYSUBITEMDRAW
being assigned to the Message Structure result, but my problem is I don't
know who (what method/object) ever sends the message CN_NOTIFY to a
TListView in order to determine what that result does. After the Message is
processed by TCustomListView.CNNotify() i have no idea where it goes or how
the subitems eventaully get processed.
Any points in this general direction would be most benificial.
Reason:
I Want to override the "default" drawing of each Item/SubItem. This
allows the programmer to still implement all the CustomDraw(),
AdvancedCustomDraw() and OnDrawItem() events, but will change the "default"
drawing of the items based on my customizing properties should the
CustomDrawing events be left unassigned.
Thanks
Jaeden "Sifo Dyas" al'Raec Ruiner
aka Fluffy
http://www.wayoftheleaf.net/
> For Each TListView.Items.Item
> - Retain Specific Font/Alignment characteristics for the Item
> - Retain Specific Font/Alignment characterstics for each SubItem
Derive a new class from TListItem. You can add whatever you want to
it. You can then override the virtual CreateListItem() method to
create instances of that class, and then cast a TListItem pointer to
your class type whenever you need to access the new functionality.
> This can be done very easily, but in many ways I appear to have
> to always hook my design into the "OnDrawItem" or into the
> "OnCustomDrawItem/OnCustomDrawSubItem" events.
NEVER NEVER NEVER have a component assign handlers to its own events.
That prevents user code from using the events for its own purposes.
Always override the cooresponding virtual methods instead. In this
case, DrawItem() and CustomDrawItem().
> The interesting this is that there appears to be a message method
> in TCustomListView called CNNotify().
You do not need to intercept that message manually. Let TListView
handle it normally. The message handler calls the virtual drawing
methods when appropriate.
> Now in the protected virtual method of TCustomListView.DrawItem(),
> the default component checks to see if the item is custom drawn, and
> if not it draws the Item.Caption and exits. This in no way depicts
the
> "Default Drawing" of the SubItems for that Item if in vsReport view.
DrawItem() is not responsible for drawing sub items. It is called
when the first column is being drawn. Sub items are drawn separately
by CustomDrawSubItem(), which returns DefaultDraw=True by default,
which tells the OS to handle the drawing internally.
> Thus There does not seem to be anything that defaultly draws the
> subitems for each particular item in the TListView
Yes, there is. See above.
> What method in the TListView, TListItems, TListItem, TSubItem
> object tree actually "draws" the SubItem text?
None of them. By default, the OS itself draws them. TListView does
not do the actual drawing. You would have to override
CustomDrawSubItem() to perform your own drawing, returning
DefaultDraw=False so the OS drawing is disabled.
> I Did find in the CNNotify() Method the result value:
> CDRF_NOTIFYSUBITEMDRAW being assigned to the Message Structure
result
That tells the OS that the component is interested in receiving sub
item drawing notifications.
> my problem is I don't know who (what method/object) ever
> sends the message CN_NOTIFY to a TListView
None of the methods are. The OS itself is sending it. CN_NOTIFY is
the VCL's repackaging of the WM_NOTIFY message, which was originally
sent to the TListView's parent window. WM_COMMAND and WM_NOTIFY
messages are automatically converted by the VCL into CN_COMMAND and
CN_NOTIFY messages so that components can handle their own messages
directly.
> I Want to override the "default" drawing of each Item/SubItem.
Override the Custom/DrawItem() and CustomDrawSubItem() methods. Call
the inherited methods when you want to trigger the event handlers in
the user's code.
Gambit
Awesome, thanks.
Now the only issue I have left is the nature of reintroduce. reintroduce
suppresses compiler messages but does not 'override' the previously static
linked method. Thus if TAncestor and TDecendent have identically named
methods, and a variable of type TAncestor is an instance of type TDecendent,
Variable.Method() will call the TAncestor version on a reintroduce (hidden).
However, there are some functions within TCustomListView that I "NEED" to
override, or at least intercept. I need to do a calculation everytime the
methods UpdateColumn() and UpdateColumns() are called. However, they are
not virtual methods thus i cannot override them. So any suggestions that
would allow me to know that the Columns are being updated/resized etc that
would allow me to perform my operation instead of always recomputing it
every time a Drawing event occurs. (Basically only when the subitem
data/caption is changed do I need to recompute, but I can't find any place
to determine the item data/subitem data has been altered for individual
columns other than the aforementioned methods).
Suggestions?
--
Jaeden "Sifo Dyas" al'Raec Ruiner
aka Fluffy
http://www.wayoftheleaf.net/
My reason, is that there are many events, CustomDraw* and
AdvancedCustomDraw*, which handle specific stages of a ListView's drawing,
allowing the programmer (me) to intercept these stages and handle them
myself. Of course, if I set DefaultDraw:=True; then the default drawing for
that particular item is skipped. This makes sense. However, when the
OwnerDraw:=True; another event is specifically fired, called OnDrawItem(),
and it only occurs when OwnerDraw is True. (Or so the help says). Either
way, when utilizing the ListView_* functions or the TCustomListView.*
methods, there is a specific rectangle (TRect) provided (or determined) for
the specific Item being currently drawn. This TRect appears to be immutable
and is based upon the text metrics of the entire ListView. Thus:
TListView
TFont: MS Sans Serif
Size: 8
With the above settings each line of the ListView will be Calculated
according to the text metrics of the Font "MS Sans Serif" at 8pt type. I am
looking to allow the programmer to affect the inidividual TFont settings of
each Item and its appropriate SubItems.
Example:
TMyListItems
1: TFont = MS Sans Serif, 8
a) SubItem.Object[0] = MyObject.TFont = Times New Roman, Italic, 10
2: TFont = MS Sans Serif, 9, Bold, Align = Center
a) SubItem.Object[0] = MyObject.TFont = Times New Roman, 8, Align =
Right
As you can see with this listing each line will be a different height and
with some lines the height will be determined by the text metrics of the
subitem not the item.
Is it possible to recieve the window messages that link through the
TCustomListView to tell the list view what to use for all Items' textmetrics
individually.
Thanks
--
Jaeden "Sifo Dyas" al'Raec Ruiner
aka Fluffy
http://www.wayoftheleaf.net/
"Jaeden "Sifo Dyas" al'Raec Ruiner" <nos...@wayoftheleaf.net> wrote in
message news:45fb370b$1...@newsgroups.borland.com...