about listview OnDrawItem - delphi

procedure TCnCustBuildForm.lstWizardsDrawItem(Sender: TCustomListView;
Item: TListItem; Rect: TRect; State: TOwnerDrawState);
begin
//When first come here, rect.left = 0, rect.right = 100, which means listview.width = 100.
FillRect(Sender.Canvas.Handle, Rect, GetStockObject(WHITE_BRUSH));
//....
AutoResizeColumn(TListView(Sender), 0);
//set column 0 width to 173
AutoResizeColumn(TListView(Sender), 0);
//set column 1 width to 54
//now row width should be 173 + 54 = 227.
I debug the app, with process as follows:
when first call this function, listview.width = 100;
after redraw the first row, call AutoResizeColumn to set listview.width = 227;
then the 2nd time into this function, to redraw the second row, now the rect param changed, rect.width = 227;
So FillRect function should Fill a rectangle of width = 227, but it only fill width = 100, this means not clean all the row?
BTW: For why I ask this question, cause of each time I draw the second column to the right, the previous data of the second column remains, so it seems as 3 rows(the middle row is previous place of the true 2nd row, which should be cleaned ,but not).

You shouldn't be doing anything inside the DrawItem event handler except drawing the item based on the current state of everything, including the column widths. Then when anything about the list view changes that requires an item to be drawn, DrawItem will fire. Don't change the column widths inside DrawItem. Change them elsewhere, then if the change requires an item to be drawn, DrawItem will be called.

Related

Firemonkey Listbox needs a TopItem

I need to be able to get and set the top item in a listbox - something like a ListBox.TopItem property would be great. I have been unable to find anything that does this job. Any ideas would be greatly appreciated please.
Edit:
For example:
Listbox1 and ListBox2 items are the following:
1 Data1
2 Data2
3 More Data
4 More again
5 Yet more
6 and this will do.
Showing in 2 listboxes, both 3 items high:
1 Data1
2 Data2
3 More Data
and I want to programmatically make them show
3 More Data
4 More again
5 Yet more
and I want to find out what item the top one is.
(previous answer text deleted)
Update after clarification of question.
Ok, so you want
a) to scroll the list programmatically
b) to read the topmost item programmatically
The following scrolls the list so that a given item becomes the top visible item. Note however, that the list can not be scrolled up beyond the point that there would be empty space between the last item and the viewport's bottom.
procedure TForm25.SetTopVisibleItem(idx: integer);
var
ptf: TPointF;
begin
ptf := PointF(0, idx * ListBox1.ItemHeight);
ListBox1.ViewportPosition := ptf;
end;
To get the current topmost item in the viewport:
function TForm25.GetTopVisibleItem: TListBoxItem;
var
x, y: single;
begin
x := 3; // a small offset is required to assure that
y := 3; // the point is within an item
result := ListBox1.ItemByPoint(x, y);
end;

TStringGrid: Why OnClick after OnKeyDown

Why does a Delphi StringGrid sometimes calls the OnClick event after an OnKeyDown?
Debugging screenshot:
My OnKeyDown event handler:
var
Top: Integer;
Bottom: Integer;
CurrentRow: Integer;
begin
Top := Grid.TopRow;
Bottom := Grid.TopRow + Grid.VisibleRowCount - 1;
if (Key = 38) then CurrentRow := Grid.Row - 1
else if (Key = 40) then CurrentRow := Grid.Row + 1;
// Disable OnClick because sometimes a 'TStringGrid.Click' is called anyway...
// (when clicking on form top window bar and navigating)
Grid.OnClick := nil;
if (CurrentRow < Top - 1) or (CurrentRow > Bottom + 1) then begin
if (Key = 38) then Grid.Row := Bottom
else if (Key = 40) then Grid.Row := Top;
end;
Grid.OnClick := GridClick;
end;
Edit:
It seems the 'OnClick' is not being fired when the last line is selected and pushing 'Down' or when the first line is selected and pushing 'Up'.
Way to reproduce:
Add a TStringGrid to a form and populate with a few lines. Add 'OnClick' and 'OnKeyDown' handler. No specific code needs to be added in these two handler methods. Select a row in the stringgrid on the form and press the up or down arrow on your keyboard.
Edit 2:
This isn't the solution, but to prevent the code in 'OnClick' being executed after pressing up, down, pageup or pagedown, I set a variable in 'OnKeyDown' what key was pressed and check for that variable in 'OnClick'.
Edit 3:
Updated stack trace and way to reproduce.
Well, that hard-coded key codes aren't the case making the least bit transparent, but you witness this effect when you use a direction key (up, down, left, etc...) to change the selection.
Why the OnClick event handler is called, is because TCustomGrid.OnKeyDown calls TCustomGrid.FocusCell, which calls Click.
Exactly why changing focus to another cell would establish a click I do not know, we would have to ask the developers I imagine. Perhaps to simulate the default behaviour when changing focus to another cell by clicking instead of keyboard.
Since you seem to handle direction key presses yourself, maybe you could consider to prevent this from happening at all by ignoring the key any further:
if Key in [VK_PRIOR..VK_DOWN] then
Key := 0;

How to animate row height in Titanium iphone

I am working on Table view in Titanium iphone.
I tried to add animation style for each row.
That is, when user click on each row , then row height need to increase, So I able to show hided contents in row. And when user click again then the row height need to decrease.
My code is,
for collapsed view,
var animation = Ti.UI.createAnimation({height: 1, duration: 500});
row.animate(animation);
and for expanded view,
row.animate(Ti.UI.createAnimation({ height:200, duration:300 });
but above both code are not working,
If I set row height without using animation then its working fine.
How to apply animation style for table view row...
can any one please.
you can try this
http://developer.appcelerator.com/question/119198/animating-row-height-changes
TableViewRow cannot be animated. It's documentation bug. You need to make your own animation function (ie, setTimeout).
The height of the row is animated by default (on iOS) when you change it, so :
row.height = '40dp';
If you set the row height directly to a numeric value, it will be animated by default on iOS. The default animation will override any custom animations you might come up with for example using the standard setTimeout javascript function.
The difficulty is to make the row content appear animated as well. Let's say row.details points to a container view that holds the hidden row details to be shown on expansion. It's height would be initialy set to zero. The following piece of code will animate the whole row in a smooth way:
var total_height_increase = 260;
var height_step = 5;
var duration = 200;
var time_step = duration * height_step / total_height_increase;
var num_steps = duration / time_step;
var i=0;
var increase_height = function(){
setTimeout(function(){
row.details.height += height_step;
i += 1;
if (i > num_steps) return;
increase_height();
}, time_step);
};
increase_height();
row.height += total_height_increase;

Delphi: How to draw some text in requested width and number of lines, with ending ellipsis?

I need to draw some text in a table cell with fixed width (in pixels) and fixed number of text lines. If the text is clipped by cell rectangle, it must end with ellipsis. The problem is I can't calculate the text rectangle correctly (or the TextRect/DrawText procedure isn't working correctly, I'm not sure).
I tried to use this method of calculating text rectangle:
var
TextRect: TRect;
tm: TEXTMETRIC;
...
GetTextMetrics(Canvas.Handle, tm);
TextLineHeight := tm.tmHeight + tm.tmExternalLeading;
TextRect.Bottom := TextRect.Top + TextLineHeight * NumberOfLines;
Canvas.TextRect(TextRect, 'some long long long text',
[tfTop, tfLeft, tfEndEllipsis, tfWordBreak]);
The clipping rectangle has been calculated correctly, but the ellipsis isn't appearing.
Ellipsis appearing when I decrease the height of clipping rectangle by 1 pixel:
TextRect.Bottom := TextRect.Top + TextLineHeight * NumberOfLines - 1;
But some pixels of the bottom line of my text are clipped then.
How to do it correctly?
Since the api puts the end-ellipsis only when the last line does not fit in the specified rectangle, one workaround could be to specify tfModifyStringin formatting options in a first call to 'TextRect' with a rectangle with reduced height, then call 'TextRect' again with a proper sized rectangle and the modified text:
var
Text: string;
...
Text := 'some long long long text';
SetLength(Text, Length(Text) + 4); // as per DrawTextEx documentation
Dec(TextRect.Bottom);
Canvas.TextRect(TextRect, Text,
[tfTop, tfLeft, tfEndEllipsis, tfWordBreak, tfModifyString]);
Inc(TextRect.Bottom);
Canvas.TextRect(TextRect, Text, [tfTop, tfLeft, tfWordBreak]);
I'd be keeping an eye though, in case a future version of the OS decides to clip the last line entirely if it doesn't entirely fit in the rectangle.. :)
I'd try calculating the needed rectangle via Canvas.TextRect(..., [tfCalcRect, ...]).

Virtual StringTree: How to determine if the node text is completely shown?

When TVirtualStreeTree.HintMode = hmTooltip, the node text will become the hint text when the mouse is hovered over a node and column where the node text is not completely shown. But I have to set HintMode = hmHint, so that I can in the even handler supply various hint text based on the position the current mouse cursor is, and in that HintMode the hint text is not generated automatically.
My question is how to know if the a node text is shown completely or not, so that I know should I supply the node text or empty string as the hint text?
Thanks.
You can call TBaseVirtualTree.GetDisplayRect to determine the text bounds of a node. Depending on the Unclipped parameter, it will give you the full or actual text width. TextOnly should be set to True:
function IsTreeTextClipped(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
var
FullRect, ClippedRect: TRect;
begin
FullRect := Tree.GetDisplayRect(Node, Column, True, True);
ClippedRect := Tree.GetDisplayRect(Node, Column, True, False);
Result := (ClippedRect.Right - ClippedRect.Left) < (FullRect.Right - FullRect.Left);
end;
Note that the function will implicitly initialize the node if it's not been initialized yet.
You can use what the tree control itself uses. Here's an excerpt from the cm_HintShow message handler for single-line nodes when hmTooltip mode is in effect.
NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, True, True, True);
BottomRightCellContentMargin := DoGetCellContentMargin(HitInfo.HitNode, HitInfo.HitColumn
, ccmtBottomRightOnly);
ShowOwnHint := (HitInfo.HitColumn > InvalidColumn) and PtInRect(NodeRect, CursorPos) and
(CursorPos.X <= ColRight) and (CursorPos.X >= ColLeft) and
(
// Show hint also if the node text is partially out of the client area.
// "ColRight - 1", since the right column border is not part of this cell.
( (NodeRect.Right + BottomRightCellContentMargin.X) > Min(ColRight - 1, ClientWidth) ) or
(NodeRect.Left < Max(ColLeft, 0)) or
( (NodeRect.Bottom + BottomRightCellContentMargin.Y) > ClientHeight ) or
(NodeRect.Top < 0)
);
If ShowOwnHint is true, then you should return the node's text as the hint text. Otherwise, leave the hint text blank.
The main obstacle with using that code is that DoGetCellContentMargin is protected, so you can't call it directly. You can either edit the source to make it public, or you can duplicate its functionality in your own function; if you aren't handling the OnBeforeCellPaint event, then it always returns (0, 0) anyway.
The HitInfo data comes from calling GetHitTestInfoAt.

Resources