Get screen coordinates of selected row in TListView - delphi

I have a TListView (actually, a custom descendant) display in ViewStyle vsReport. One row is selected. I would like to get the screen coordinates of that row (or a cell within that row). Is there any way for me to do this?
(My goal is to display a small form over the list view giving the effect that it dropped down from the selected row).
I am using Delphi 2010 for this particular application.

For a list view in vsReport style I believe the best approach is to use the LVM_GETITEMRECT and LVM_GETSUBITEMRECT messages.
The VCL does not wrap this functionality up for you but it should not be too difficult to work it out from the MSDN docs.
Whilst it is very simple to use the TListItem.Position property exposed by the VCL, as far as I can tell this does not help you obtain the row height or indeed the coordinates of sub items.
Update
As NGLN very helpfully points out, the CommCtrl unit does expose ListView_GetItemRect and ListView_GetSubItemRect which are more convenient to use than the equivalent Windows messages above.

var
sel: TListItem;
pnt: TPoint;
begin
sel := ListView1.Selected;
if not Assigned(sel) then Exit;
pnt := ListView1.ClientToScreen(Sel.Position);

Related

Auto layout of vertical panels in Delphi

I have been away from Delphi for a few years, doing web apps.
I have gotten used, in HTML of just declaring successive <div> ... </div> and having them placed below each other, with spacing provided by CSS.
I want to do a Delphi app where I have a collection of data, let's say it's data about movies. Initially, I would have a panel for each , showing only the title. If you click one, it will expand to show actors, director, plot summary, etc. Click another and the first will shrink & the new one expand (max one expanded; click that one again to shrnkk it).
So, I have two layout problems: 1) to calculate the initial .top of each panel and 2) to re-calculate as different panels are clicked.
I am quite capable, of course, of coding all of this manually, but it seems tedious and error prone.
Is there an accepted way to do this? A VCL component that comes with Delphi? Should I be asking on https://softwarerecs.stackexchange.com/ for a 3rd party VCL component?
The closest VCL control is probably TCategoryPanelGroup, which is composed of vertically aligned expandable panels (TCategoryPanel).
The control does not have an 'autocollapsepanels' or 'maxexpandedpanelcount' property, but you can use some simple code to achieve the required behavior.
E.g. the below OnExpand event handler, if attached to all category panels in the group, will cause an expanded panel to close others.
procedure TForm1.CategoryPanelExpand(Sender: TObject);
var
I: Integer;
begin
if Sender is TCategoryPanel then
for I := 0 to CategoryPanelGroup1.Panels.Count - 1 do
if CategoryPanelGroup1.Panels[i] <> Sender then
TCategoryPanel(CategoryPanelGroup1.Panels[i]).Collapse;
end;
You can set AlignWithMargins property of your panels to true, then adjust spacing by using the Margins properties of the panels.

FireMonkey grid with different controls in same column

What can you do if you want different cell controls in the same column of a grid in FireMonkey.
The cell control seems to belong to the column, but there are situations (like the property editor), where some rows need a checkbox while other rows need a combobox or an edit control.
Thanks in advance.
The following steps should get you up and running:
Create a style, add to it the controls you want to show (i.e. a TCheckbox, a TCombobox and a TEdit). Add these within a TLayout, and set each controls StyleName to something memorable.
In your cells ApplyStyle use FindStyleResource to extract the controls you added above using their StyleNames.
When the grid calls your cell's SetData method, you need to set the Visible property for each control so only the appropriate one is shown. If you can't determine this from the data passed in, add an event handler to the cell to get the data.
You'll need to sort out the keyboard handling, which gets pretty messy. If memory serves, you need to pass keys from the grid/cell to the control (or or is it trap movement keys from the controls and pass them to the grid? Sorry if I forget exact detail).
Sorry I can't give a more detailed answer, but covering this completely would take a whole series of blog posts.
Use a style - set the cell style when you set the cell data - then us the onapplystyle event to do anything clever you require with the newly styled cell.
This way you can add what controls you need to the style and then access the controls (to set events etc) with the onapplystyle.
Hint - FindStyleResource is your friend here :-)
I have also needed a property editor and looked for a way hosting different cell types in one column. Using different styles for each row may be a solution as suggested above, but since Firemonkey grid doesnt reserve any cell control for a specific row, each time the cell control would be shown on the row, the true style would be applied to it. This is not a big problem for a static property editor, however for a real grid which has got may rows and different cell types in each row a diferent strategy is needed. So I came up with a different solution, I considered cell type proxies between TColumn and cell controls, so that each cell proxy will reserve the cell controls that is responsible for. First of all, I have a new TColumn (TvariantColumn) which is responsible for the top strategy.
vColumn := TVariantColumn.Create(Self);
vColumn.Header := 'Variant Column';
vColumn.OnGetCellProxyIndex := GetCellProxyIndex;
Grid1.AddObject(vColumn);
Then create any proxies like
vColumn.NewCellProxy(TTextProxy);
vColumn.NewCellProxy(TColorComboProxy);
vColumn.NewCellProxy(TComboColorProxy);
You can also handle Proxy specific jobs after you create it, like
with TProgressProxy(vColumn.NewCellProxy(TProgressProxy)) do //4
begin
Min := 0;
Max := 100;
end;
with TPopUpProxy(vColumn.NewCellProxy(TPopupProxy)) do //5
begin
Items.Add('Istanbul');
Items.Add('Paris');
Items.Add('NewYork');
end;
I have blogged my method in my website and published a detailed article where you can find more about the subject.

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.

Delphi. How to shift Frames using TreeView?

Please help me with my question.
I have TreeView and Frames, how can I shift them if I click on an item of TreeView?
Is it better to use PageControl (PageControl1.Pages[i].TabVisible := false;) instead of Frames or Frames fit better?
Thank you very much!
To answer your first question "how to ... using a TreeView?" : Implement the OnChange event of the TreeView. The node parameter refers to the newly selected item.
About your second question "Should I use Frames or a PageControl?" : Well, one does not exclude the other and you perfectly can use both. Indeed, I advice to do so when you use the contents of such a TabPage multiple times. In those cases, place the Frame with Align = alClient on your TabPage.
Frames are usefull to design an arbitrary reusable container. For instance: you could set the same FrameType on every Page of the PageControl, assuming they all look the same but each working with different data.
Another possible minor advantage of using frames is not to get confused about all the controls on the TabPages.
But if every TabPage is unique in terms of visual style or control layout, then it's perfectly ok to not use frames and design the pages on the PageControl directly.
And about the shifting part: I don't exactly understand what you want to accomplish by setting the visibility of a tab, but shifting to another page (depending entirely on your implementation) based on the node could be as simple as:
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
PageControl1.ActivePageIndex := Node.Index;
end;

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.

Resources