Count refreshes to TListView - delphi

I've got a TListView to which I may add anything from none to several hundred items depending on the day the user has selected from the log file. I use this code to prevent unnecessary refreshes:
listEvents.Items.BeginUpdate();
listEvents.Items.Clear();
// Add events
listEvents.Items.EndUpdate();
Even so, on my fast development PC I can see a few fast flickers of the list. On the (much slower) production PCs, the flicker is noticeable and rather ugly. My question is there any way to count the number of refreshes to the TListView by hooking into an event? I could then increment a variable and display the value of the variable on a label while I debug this. I tried the TListView::OnDrawItem event but that wasn't called at all.

I suspect you are not using the virtual listview. Use the virtual listview approach to display data. Set OwnerData property to true and handle your display in OnData event. That should prevent the flicker. Pseudo code for this would be:
procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);
begin
Item.Caption := FloatToStr(Item.Index + 1);
Item.SubItems.Add('Your data here');
end;

Try something like this to avoid flicker:
// Disable
SendMessage(listEvents.Handle, WM_SETREDRAW, Integer(False), 0);
try
listEvents.Items.BeginUpdate();
listEvents.Items.Clear();
// Add events
listEvents.Items.EndUpdate();
finally
// enable
SendMessage(listEvents.Handle, WM_SETREDRAW, Integer(True), 0);
end;
You may no longer be necessary to use BeginUpdate and EndUpdate.
Regards.

Related

FireMonkey FlowLayout isn't refreshing items based on visible property

How are you? Hope you doing fine.
My question: I have a FlowLayout with a lot of layouts inside it, and I need to hide a few based on a radio button selection, in design time it works fine I set the visible property to false and all the other layouts realign the right way, but when I do this at runtime it doesn't work, it keeps a white gap between the layout that has been hidden and the next one. When I do a resize manually (go to the form border and drag a little) it realigns and gets right, but if I select another radio the layout gets back and it override another layout so I need to resize manually again to realign. I tried to look at the source code of Resize but I got nothing relevant. What I tried: Repaint, Realign, InvalidateRect, RecalcAbsolute. Is there any way that I have to force the refresh of components?
procedure
TFrmApontamentoProducaoOrdemProducao.rbOrdensProducaoQuantidadeParcialClick(
Sender: TObject);
begin
if not lytQuantidadeParcial.Visible then
lytQuantidadeParcial.Visible := True;
// Tried to realign here
end;
procedure
TFrmApontamentoProducaoOrdemProducao.rbOrdensProducaoQuantidadeTotalClick(
Sender: TObject);
begin
if lytQuantidadeParcial.Visible then
lytQuantidadeParcial.Visible := False;
// Tried to realign here
end;
It's a simple code, but it's giving me a little problem. Thanks for the help, if you need more code or more details just let me know.
You must surround your code that makes changes to the layout of the TFlowLayout with a pair of FlowLayout1.BeginUpdate; and FlowLayout1.EndUpdate; To assure that the update counter stays in sync, you should also use a try..finally..end block.
For example
procedure TForm21.Button6Click(Sender: TObject);
begin
FlowLayout1.BeginUpdate;
try
Layout3.Visible := not Layout3.Visible;
finally
FlowLayout1.EndUpdate;
end;
end;
Using BeginUpdate cause access violation on a project when insert chars from tcp-ip comunication in real time on FlowLayout, the solution:
var
PodeAtualizarGrade:TCriticalSection;
// ...
FormCreate
PodeAtualizarGrade:=TCriticalSection.Create;
// ...
PodeAtualizarGrade.Enter;
(FlowLayout.AddObjects)
PodeAtualizarGrade.Leave;
Works fine on my project.

How do I know when a control can be focused?

I have my own Treeview control derived from TCustomTreeView.
I have added some of my own procedures to the class such as adding nodes. When this procedure is called at runtime I wanted the newly added node to be selected and for the Treeview to be focused so the new node is highlighted.
Here is a extract:
procedure TMyTreeView.AddGroup(AName: string);
var
Node: TTreeNode;
Obj: TGroup;
procedure AddToTree;
begin
Obj := TGroup.Create(AName);
FGroups.Add(Obj);
Node := Items.AddObject(Node, AName, Obj);
with Node do
begin
ImageIndex := 0;
SelectedIndex := 0;
end;
Selected := Node;
SetFocus;
end;
begin
Node := nil;
AddToTree;
end;
The above works but I am facing the common error message when calling from the Forms OnCreate event:
Cannot focus a disabled or invisible window
I know you can use the OnActivate event or just don't use OnCreate at all which will not result in the error, but anyone else who may use the component may not realise this.
So I wanted to know if there is a way to determine if my Treeview (or any control) is able to receive the focus, then I could add a little checking of my own, something like:
if ControlIsFocusable then
begin
Selected := Node;
SetFocus;
end;
I know there is the Loaded procedure you can override which tells us when the control is loaded but then that would only work on first run. If the control became hidden by the user at runtime (or was not visible to begin with) the Cannot focus a disabled or invisible window error is still going to show up.
The dirty way to do it when not run in the debugger is:
try
Selected := Node;
SetFocus;
except
end;
But that defeats the purpose and I hate handling errors in this way.
So basically I wanted to know if there was a way to determine if a control can receive focus, so that we can set the focus to it?
I'm not going to answer the question that you asked, because I think you are doing this wrong.
The control should not call SetFocus on itself. I can imagine no scenario where that is the correct behaviour. The form or application or framework should determine focus. Not the control.
Imagine what happens when you have a form with two such controls? Imagine using the keyboard to focus a button, which you then press with the SPACE bar. If the action attached to the button calls your control's method which then changes the focus, you've just gone against the platform UI guidelines. You control now places a severe burden on any application that attempts to consume it.

Numbering ListView

How can I numbering items in ListView (vsReport)? Now I have something like that:
Item := ListView1.Items.Add;
Item.Caption :=inttostr(Item.Index+1);
but it only works if items are not sorted. If I sort everything is mixed.
Whenever the list is sorted you need to loop through the items and update the caption.
for i := 0 to ListView1.Items.Count-1 do
ListView1.Items[i].Caption := IntToStr(i+1);
Personally I would switch to using the list view in virtual mode which makes adding an index column trivial. As you have it at present you need to work hard to keep the list's contents in sync with the underlying data. With a virtual list view that problem dissolves.
Try moving the logic of numbering to a procedure, and call this method after of sort the listview.
try this sample
procedure SetNumbering(ListView : TListView);
var
i : integer;
begin
ListView.Items.BeginUpdate;
try
for i := 0 to ListView.Items.Count-1 do
ListView.Items.Item[i].Caption:=IntToStr(i+1);
finally
ListView.Items.EndUpdate;
end;
end;
I would take an approach more like doing some custom drawing. About 1/4 of the time I ever use list controls, I wind up using its custom drawing capabilities to accommodate for things like this. Refer to This Article which goes into some detail on how to accomplish custom drawing. You can check the index of the item as it's being drawn, and draw your number to the left of each item. I can put together a sample if you would like, but it is quite a bit of coding to do. But not only do you accomplish the numbering you want, but you can also do many other things like implement your own styles, draw images, draw other controls, etc.

Live update of StringGrid when horizontal scroll bar is moving?

In Delphi 2010, I need to display a grid that has a horizontal scroll bar with about 15 columns x 5 rows.
I chose to use a StringGrid.
However, while the mouse button is down dragging the horizontal scroll bar, I want the grid to scroll live.
The StringGrid component, it appears, does not scroll live. It waits until the mouse button is released before updating the column and scrolling if necessary.
Also, the horizontal scroll bar button (is that what it's called) is not proportional to the number of columns. And for a down-arrow when on the bottom row to move to the top of the next column to the right...
These seem like common needs, so I was surprised not to find them in TStringGrid.
Any suggestions on a way around these two problems? I can use a DbGrid or other standard component, but my preference is to not use a commercial grid if I can avoid it. And I'm not going to use shareware or freeware...
TIA
For the first question, you can set goThumbTracking in the StringGrid's Options at design-time, or at run-time: StringGrid1.Options := StringGrid1.Options + [goThumbTracking];
For the third question, you can provide the functionality you need by using keyboard event handlers of the control. An example;
procedure TForm1.StringGrid1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
StringGrid: TStringGrid;
begin
StringGrid := Sender as TStringGrid;
case Key of
VK_DOWN:
if StringGrid.Row = StringGrid.RowCount - 1 then begin
Key := 0;
StringGrid.Row := StringGrid.FixedRows;
if StringGrid.Col = StringGrid.ColCount - 1 then
StringGrid.Col := StringGrid.FixedCols
else
StringGrid.Col := StringGrid.Col + 1;
end;
VK_UP: //...;
VK_RIGHT: //;
VK_LEFT: //;
end;
end;
For the second question, the scrolling code seems to be buried in private methods of TCustomGrid. I have no clue how to achieve that..
If noticed you are not interested in third party components - Freeware, I am not fond of these either, but we all must make sacrifices sometimes if we want to get the problems solved. This is one of these sacrifices! This component is to good to be ignored. You will not create something like it yourself if you don't have a couple of years of free time.
Either write a new component based on TStringGrid (I would not - it is not the best tool in the box to begin with)
But take some time and learn TVirtualStringTree. The component is years ahead of TStrignGrid. The source is available and there are many who uses it.
And there are events already implemented to react on scrollbar changes
OnScroll, OnShowScrollbar
http://www.delphi-gems.com/index.php?option=com_content&task=view&id=12&Itemid=38
Search on stackoverflow and you can read much more about tvirtualstringtree
Second the suggestion to use TVirtualStringTree. Working with the TStringGrid component is like stabbing yourself in the belly with a rusty scissor.

How do I know when I've stopped scrolling a TScrollBar?

I've used some programs with scroll bars that update the linked content while you're still dragging the "thumb", and others that don't until you release the mouse. This implies that there are different types of Windows messages involved here. But all I can find from TScrollBar is an OnScroll event which fires continually while you're dragging. It also doesn't have a OnMouseDown or OnMouseUp event. Is there any way to set up an "OnEndDragging" notification for a TScrollBar?
Try this code (tested with Delphi 2009), it will fill the form client area with a random colour while you track the thumb, and fill it in yellow when the thumb is released:
procedure TForm1.ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode;
var ScrollPos: Integer);
begin
Randomize;
if ScrollCode = scTrack then
Color := RGB(Random(256), Random(256), Random(256));
if ScrollCode = scEndScroll then
Color := clYellow;
end;
The TScrollCode values map to the WPARAM values that you will find documented for WM_HSCROLL and WM_VSCROLL.
Programs that update their scrolling region "live" as the user drags the thumb are handling the sb_ThumbTrack code for the wm_HScroll and wm_VScroll messages. Those that only update when the user releases the thumb are handling the sb_ThumbPosition code.
There's a compromise on those two options, which is to update after the user hasn't moved the thumb for a little bit, even if the user hasn't actually released it yet. Handle sb_ThumbTrack, and then set a timer. If the timer fires, update the display. If another sb_ThumbTrack arrives, reset the timer.

Resources