How to SelectAll only visible VirtualTreeView items? - delphi

I have hidden some VirtualTreeView items using VirtualTree.IsVisible[n] := false;.
In one function I iterate through all selected items using VirtualTree.GetNextSelected(n); method. But there is a problem - if I manually select a few items for example using CTRL-click or SHIFT, the items which get looped are the ones selected.
But if I press CTRL + A to select all then GetNextSelected gives me all nodes, including those which are hidden.
I did a workaround by checking if (VirtualTree.IsVisible[n]) ... to avoid looping through all items and fetch only visible ones, but this seems like a bug or maybe there is an option which controls how CTRL + A will behave - will it select only visible nodes or all of them?
So is this a bug or maybe I missed some selection option (I examined them all) or am I supposed to use some other method for looping only through visible nodes?

As it appears by David Heffernan and Joachim Marder, it is a design choice and not a bug. For this reason, one needs to introduce a workaround when looping through items using GetNextSelected by checking if (VirtualTree.IsVisible[n]) ... to loop only through visible items.

Related

Delphi 10 TMS FireMonkey grid - problem with SelectRows() and RowSelectionCount

I am using TMS FireMonkey Grid in my app and currently trying to implement the ability to select a range of rows by double-clicking on the first row and then double-clicking on the last row of the range. I use the SelectRows(StartRow,EndRow) method in the OnCellDblClick event handler. It seems to work, except for one thing: the RowSelectionCount does not get set properly (equals 1, regardless how many rows I select) until I call ShowMessage() with something random, like 'Hi!' - after that it gets set to a correct number of selected rows. I figured it has something to do with focus, so I tried to use the SetFocus to change focus to another component of the form and then back to the grid, but that didn't help. Any ideas on how to get RowSelectionCount to carry a correct value right away?
The grid's SelectionMode property is set to smDisjunctRow - according to an email I received from TMS Support, this setting should allow me to use RowSelectionCount.
Thanks in advance!

Need to Update a file after a DBGrid Double-Click

I seem to have a mental block with this one.
I need to save recent changes to a Record. I am doing it OK with buttons for "New" "Edit" "Post" "Next" etc but when a user double-clicks a DBGrid, it is now too late to make changes as the DBGrid selection has moved the database cursor to the selected record.
I can't use AutoUpdate as the data that may have been changed is not something the user would have directly entered, it is a value that is changing all the time.
I'll try to describe it better: While the user is reading a Test-page, a timer is counting down or up. When they click the "Next" or "Prior" buttons I can save the timer setting. But, if they Double-Click the DBGrid I have no way of first changing the current Record before the selection moves to the clicked-Record.
I tried using the
Procedure TForm1.tblTestOnBeforeScroll(...
begin
tblTest.Edit;
tblTest.FieldByName('TimerCt').AsInteger:=ClockCtr;
tblTest.Post;
end;
But, that crashed the database, not surprisingly, but I thought I would give it a try before asking here.
How do I deal with the current record? I do not want to disallow the double-click if possible as it seems nice and intuitive for the user.
I'm afraid that what you do on DBGrid is a wrong in concept action.
Key point is DBGrid is a data aware control. So everything you do on it must based on it's datasource then it's dataset.
There is a DblClick event on TDBgrid. But still you have to check it's dataset to see what record is active.
The code you shown above clearly lead you to an endless loop.
When you doing a scroll, the dbgrid will call OnBeforeScroll event, moving the active record
and call OnAfterScroll. Your OnBeforeScroll code does an data update. Then dbgrid abort its operation cause of data change. Then after data update, it try to scroll again, and the data change happen again. Endless loop happen here.
best regard
Apologies in advance if I'm misunderstanding you, but it seems to me that either the premise of your question is wrong or there is something relevant you haven't told us about what you're doing. Please try the following:
If you don't have one already, please temporarily add a DBNavigator to your form and connect it to the same DataSource as your DBGrid. The point of doing this is so that you can more clearly see what's going on.
Then, in your DBGrid's Options, set dgEditing to True.
Finally, comment out or disable your BeforeScroll handler, save and run your program.
Now, click once in some cell in your DBGrid that contains a value which it's ok to change. Click it a second time so that it selects the value in the cell rather than the cell per se.
Make some change to the cell value, but do nothing else for the time being. You should notice that a) the current record indicator of the DBGrid has changed from a black triangle to an I-Beam graphic, like ][ and b) that the tick and cross buttons of the DBNavigator are now enabled.
Now, without doing anything else on the form, click in another row of the DBGrid. You should find that a) the change to what was the current record in step 5 has been saved and b) the current row indicator reverts to the black triangle and the DBNav's tick and cross buttons are disabled.
(If you don't get the behaviour I've just described, create a minimal new project and try that instead, as some other change you've made in your existing project may be interfering with it).
What I've described in 5 & 6 is the default behaviour of a DBGrid and insofar as I understand your q, that seems to be the behaviour you're trying to achieve. If that's not the behaviour you want, please explain exactly how what you do want differs. It's not clear to me where the user double-clicking on the grid comes into your q, except that that action may move the dataset's cursor (if the dbl-click is on a different row than the current one), but as the default DBGrid behaviour will save changes to the current row before it moves the dataset's cursor, it will do what you seem to want automatically.
Btw, what "AutoUpdate" do you mean? Did you mean TDataSource's AutoEdit property?

How to create a Hierarchical menu in iOS?

In my application, I have a menu that is defined as follows. Each item on the menu is a node that has the following data attributes:
MenuText : (the text that would appear for that item)
isView : Whether this item is a leaf level item or not
subMenus : if isView is false, then there are multiple menu items under this attribute.
Thus, it is a tree structure that can go till any depth. The items in the menu can change periodically and hence the implementation has to be kept flexible. This data is stored in a plist file and read into an NSArray in the code (already implemented).
I now need to create a slide out menu on the left that will be populated by this data hierarchy. I have created the menu pane and added swipe gestures to it, all of which work fine. The menu has to be a collapsible one where clicking on one menu item expands the subViews below it. If the item clicked on is a view, then a new view is loaded on the rest of the screen with appropriate data.
The problem I am facing is the logic to populate the menu (which is a UITableView) from the NSArray data. The following are the two approaches I came up with.
Create a UITableView with as many sections as there are items at the top level of the menu. Then iterate through the menu items recursively. For each menu item that is not a view (i.e. it has subMenus), create a new section with number of rows equal to the number of subMenus under it. When I come across a menu item that is a view and has no further subMenus, add it as a row to the subsection created for the menu one level above it.
Create a menu with one section and as many rows as the number of menu items at the top level. When a menu item is clicked, insert rows under it to represent its sub-menus. When another menu item on the same level is clicked, collapse the previously expanded menu by deleting the inserted rows. When a menu item with no sub menus is clicked, the rest of the screen is populated with data.
I have tried both the approaches and not been able to go beyond the initial steps. For the first method, I understand that I need to add a UITableView as a part of UITableViewCells, which is good, but I need to do that recursively. For the second approach, I need to know the indexPath of each item clicked which can go to many levels.
I would like some suggestions here about which approach I should take and some guidance over how to go about it. Also, if there is any better way to do this, kindly advice. Thanks.
In my opinion, using UINavigationController is the easiest way. You can push as many UITableViews as you want.
If it doesn't fit your design requirement, you can try expandable UITableViews. There are few open sources:
JKExpandTableView
SDNestedTable
iOS-Tree-Component
Thanks for the responses. I ended up doing this using the following control:
Accordion for iOS
It has served my purpose beautifully and I posted it here so that someone with the same requirement may find it.
Take a look at TLIndexPathTools. It has a "Tree" extension that can do this. Try running the Outline sample project. The main task in adapting the sample project would be to write a recursive function to convert your array of nodes into an array of TLIndexPathTreeItem objects. All of the code in the controller:willChangeNode: method is examples of lazy loading and it doesn't sound like you'd need any of that.

TTabSet tab Order....Different At Run Time Than Design Time

I am running Lazarus 0.9.30.2.
I have a TForm on which there is a TPageControl. Within the TPageControl there is a series of TTabSheets. At runtime the order of the TTabSheets differs from design time (see picture).
The order in design time is what I want to see at runtime, at least for the very first time the form is displayed. Why does the order change at run time and is there a way to control this?
#TLama is correct that this is related to way the Windows tab control behaves when in multi-line view. The behaviour you are observing is related to the way selection is handled for multi-line tabs. When you select a tab it is always shown in the bottom row because the visual cue to indicate which tab is selected can only really work for tabs in the bottom row.
Given that constraint the control simply has to rearrange rows of tabs as you modify the selected tab. It's astoundingly confusing for the user. Good UI design never has UI elements changing position like this.
Clearly what is happening here is that the rearrangement is happening at runtime when the form is first shown and for whatever reason this is resulting in a different arrangement from the design time arrangement. Given that the user can arrange the rows in any order just by selecting them I'm not sure you should worry about what order the rows appear in.
If you are dead set on forcing a particular arrangement when the form first shows you can add code like this to a OnCreate handler for the form:
PageControl1.ActivePage := TabSheet9;
PageControl1.ActivePage := TabSheet5;
PageControl1.ActivePage := TabSheet1;
Best practise for UI design is to avoid multi-line tab controls and I urge you to attempt to re-design your UI that way.

How do I move multiple nodes at once in a TJvTreeView?

When you select multiple nodes of a JvTreeView, and then try to drag these nodes, the JvTreeView selects the node you clicked to drag, rather than initiating dragging all the selected nodes. You end up dragging only this single node.
Is it possible to drag multiple nodes in a JvTreeView? I am using Delphi 2007.
UPDATE: Oddly enough, if I hold down CTRL+SHIFT when dragging the items, I can successfully drag them all. Any ideas?
Suppose MultiSelectStyle is the default [msControlSelect], then the answer to your question is 'you don't release the control key when you're selecting the last item and beginning dragging'.
The culprit is in TJvTreeView.WMLButtonDown in JvComCtrls.pas. Code there tests if 'Ctrl' is pressed when the TreeView is MultiSelect, and clears all items and selects the clicked item if 'Ctrl' is not pressed. It should instead test if the clicked item is already selected and do nothing if it is.
You can see the broken behavior without dragging. Multi select a few items and then click a selected item with the mouse. The VCL TreeView do not select or de-select anything, while the JVTreeView, instead, de-selects all items and selects the clicked one.
Yes absolutely, I do it all the time.
Of course Multi-Select needs to be True and you may need to have a look at the TTreeView's MultiSelectStyle. That controls what type of nodes can be selected at the same time. For example if msSiblingOnly is set to true, you can only select sibblings.
Are you using dmAutomatic or dmManual. If the latter, it could be that you are doing something in the OnMouse* events that is negating the multi-selection you made earlier.

Resources