Sort TListBox Items Alphabetically in Delphi 7 - delphi

I am attempting to trigger a sort on the items within a TListBox control after adding/editing entries.
I see that there is a Sorted property which I've set to true, however, this doesn't dynamically sort the ListBox every time I make a change to the contents. There doesn't seem to be any Sort procedure or function available and calling Update or Refresh does not have the desired effect.
I've reached the stage where I'm considering pulling the contents of the ListBox into a TStringList object, sorting that and then putting everything back into the ListBox again. This seems a bit insane though, surely I am overlooking some better method.
Here's an example of changing an existing item:
myListBox.Items[myIndex] := newString; // Update Text
myListBox.Items.Objects[myIndex] := TObject(my_object); // Update associated object
I would expect the control to update to keep things sorted alphabetically but it doesn't.

The sorted property of a list box is actually backed by the Win32 list box style LBS_SORT. That will sort the list box when a new item is added. But it will not do so when an existing item is modified.
So the easy way to work around this is to set Sorted to True, then, instead of modifying existing values, remove the old value and add the new one. So your code would become:
myListBox.Items.Delete(myIndex);
myListBox.Items.AddObject(newString, TObject(my_object));
And if you think about it, your code would have been doomed to failure if the list box behaved the way you expected it to. Because after you modified the text of the item, if the list was re-sorted then myIndex would no longer refer to the same item.

Related

Microsoft Graph API remove Check List Item

I have a plannerTask and in its Details it has a CheckList. I use it to programatically insert CheckListItems in it, and it all works like a charm when inserting or retrieving the tasks.
My problem arrives when I am going to insert a new CheckListItem and the CheckList already has 20 items. It returns a MaximumChecklistItemsOnTask (because it is forbidden to insert more than 20 items in a check list).
Solution could be to remove the oldest item, but I am not able to do it. I have tried this:
var elementToRemove = oldDetails.Checklist.Where(c => c.Value.IsChecked).OrderBy(c => c.Value.LastModifiedDateTime).First();
oldDetails.Checklist = oldDetails.Checklist.Where(c => c.Value.LastModifiedDateTime <> elementToRemove.Value.LastModifiedDateTime);
But it throws a casting error in the second line:
Unable to cast object of type
'WhereEnumerableIterator1[System.Collections.Generic.KeyValuePair2[System.String,Microsoft.Graph.PlannerChecklistItem]]'
to type 'Microsoft.Graph.PlannerChecklistItems'.
Which is the right way to remove the oldest element from the ChecklistItem?
UPDATE:
In first place I retrieve a plannerTask from the server. Then I get the details from this plannerTask. So oldDetails is a plannertaskdetails object (https://learn.microsoft.com/en-us/graph/api/resources/plannertaskdetails?view=graph-rest-1.0). Inside the plannertaskdetails object (oldDetails), I have the plannerchecklistitems object (oldDetails.Checklist): https://learn.microsoft.com/en-us/graph/api/resources/plannerchecklistitems?view=graph-rest-1.0.
If plannerchecklistitems were just a List, it would be as easy as list.Remove(item), but it is not a normal list, and that is why I am not able to remove the item.
UPDATE 2:
I have found this way to remove the item from oldDetails:
oldDetails.Checklist.AdditionalData.Remove(elementToRemove.Key)
But, the the way I send the changes to the server is this:
await graphClient.Planner.Tasks(plannerTask.Id).Details.Request().Header("If-Match", oldDetails.GetEtag).UpdateAsync(newDetails)
As it is a PATCH request (not a PUT one), I only have in newDetails the records that have changed, it is, the new records. How could I specify there that a record has been deleted from the list? Sorry if my English is not good enough to express myself properly, but what I mean is that newDetails is not the full list, it only contains the records that must be added and I do not know how to specify in that request that one record must be deleted.

Refresh one row in cxGrid

I have seen there is a problem refreshing only current row if the data are changed in another form (and DataSet too) or even if the data are changed on server side (in trigger or from another user).
So i find solution that partially work with DevExpress's cxGrid and have following issue.
frm:=TfrmEdb01.Create(Self); // create edit-form
try
frm.LoadData(0); // do the insert in TAdQuery
frm.ShowModal; // Show edit form
// after closing an Edit-Form, make a new record in cxGrid
row:=tv01.dataController.InsertRecord(tv01.Controller.FocusedRecordIndex);
// for each column in grid...
for col:=0 to tv01.DataController.ItemCount-1 do
begin
// ...get its FieldName
sFld:=tv01.DataController.GetItemFieldName(col);
// and if this field exist in edit-form's Query
if frm.q01.FindField(sFld)<>nil then
//then assign this value to the newly created row in the grid.
tv01.DataController.Values[row,col]:=frm.q01.FieldByName(sFld).Value;
end;
finally
frm.Free;
end;
And realy, when I step out from the edit-form there is a New row created in the cxGrid. Problem is in that:
- if is double-clicked (to open edit-form again) then the previous record is opened. It seems to me the Data acctualy did not updated in Query object, only in GridView... maybe I should use values from Grid instead of Query object.
- if select another record (scroll them up and down), and then want to select again this new record, then it can not be focused at all. Only if I reload whole dataSet, then this new record can be selected same as any other.
Cheers!
p.s. To clearify a problem, I will provide whole test-project. There is also SQL script to create a table in the database, and AnyDac connection must be set to Your database.
There is also an image thate will/should ilustrate a problem.
You have call post to save the changes to the underlying dataset. Do it just before finally.
tv01.DataController.Post();
See the help for TcxCustomDataController.Post for more details.

paper-menu with multiple selections, howto deselect all

I use paper-menu with multiple selections (multi). Everything works fine so fare, but with a deselect all method things seems more complicated.
With html
<paper-menu multi selected-values="{{selectedValues}}">...
Dart
#property
List<String> selectedValues = [];...
Things got binded, and every iron-select/iron-deselect event results in a correct update of the selectedValues list in dart.
With clear('selectedValues') the list empties and the logic behaves like no selection is done, but in Dartium the items that previous was
selected remains marked as selected.
I have also tried with the selectedItems List or with the foreach deselect with the select method to PaperMenu, but still not successful update
in Dartium.
Anyone with ideas how to implement this?
Found a workaround for the issue with the select method. The menu with selected values can be replaced with a new similar element created with the Dom api. One drawback is the bindings can't be set up, so these needs to be hacked with get and set methods at the element. Otherwise this seems to work ok. The calls to the Dom api are shown below.
ParticipantMenu oldPm = $$('#id_filterselection') as ParticipantMenu;
ParticipantMenu newPm = document.createElement('participant-menu');
Polymer.dom(parentNode).insertBefore(newPm, oldPm);
Polymer.dom(parentNode).removeChild(oldPm);
PolymerDom.flush();

Wix: ListBox value limitation

Value field in ListBox table has String[64] type. Is there posible to expand this 64-characters limitation? I need to store some directory pathes there.
It's probably (never tried) possible in WiX to override the default schema of he ListBox table. I know in InstallShield I just go to the direct editor and adjust it. WiX has a template schema that is used to build the MSI and you might be able to use the Table element to redefine it. Or it might just give you an error message saying you are defining a well known table.
However, I'm not sure if there would be any side effects in the ListBox control if you exceed 64 char. I don't see anything in the MSI SDK saying what's allowed so I guess your milage may vary.
Here's a trick that you might like though. It's called the evil twin dialog trick. See, in MSI there's a bug that UI elements don't refresh very well and this trick works around it. Consider this:
Dialog1 with ListBox associated to property TESTPROP and Items One value 1 and Two value 2.
Textlabel that displayes [TESTPROP].
When start the dialog the text label is empty after clicking a row in the listbox. Click back and next and suddenly it has the expected text of 1 and 2.
Now create a clone of this dialog ( Dialog2 ) and put a control event on the Listbox of dialog1 that says NewDialog Dialog2 condition=1 and put a control event on the Listbox of dialog2 that says NewDialog Dialog1 condition = 1
Now when you run it the screen refreshes ( although with a big of an ugly flicker ) See it looks like it's the same dialog only it's really the evil twin dialog that's being transitioned to so that the data refreshes correctly.
Now for extra credit use your custom actions to do something like this
ListBox Item 1 Text C:\Pro...Foo\Bob value LISTBOXDIRPROP1
ListBox Item 2 Text C:\Pro...Foo\Ed value LISTBOXDIRPROP2
Property LISTBOXDIRPROP1 = C:\Program Files\Foo\Bob
Property LISTBOXDIRPROP2 = C:\Program Files\Foo\Ed
Then set your TextLabel to display [[TESTPROP]]. This tells it to get deference the value of the value of the property. In other words, TESTPRO = LISTBOXDIRPROP1 = C:\Proggram Files\Foo\Bob
This trick would allow you to display a line that fits the 64 char constraint yet gives additional information when the user selects it.

to track the modified rows and manually update from the TClientDataSet's Delta

Is there any way to manually track the changes done to a clientdataset's delta and update the changes manually on to then db. i have dynamically created a clientdataset and with out a provider i am able to load it with a tquery, now user will do some insert update and delete operations on the data available in the cds, and at final stage these data(modified) should be post in to database by using a tquery(not apply updates)..
After populating your data set from the TQuery call MergeChangeLog so that the records do not stand out as newly inserted, and be sure that LogChanges is set.
Then when at the final stage, before updating the query with the dataset, set StatusFilter so that only the records that you want to take action on should be showing. For instance;
ClientDataSet1.StatusFilter := [usDeleted];
You can also use UpdateStatus on a record to see if it has been modified etc..
But be careful that, is seems that there will be multiple versions of a record, and it is a bit difficult to understand how the "change log" keeps track. And there also can be multiple actions on a record, like modifying it a few times and then deleting it.
Change:= TPacketDataSet.create;
Change.Data:= YourClientDataSet.Delta;
while not Change.Eof do
begin
Change.InitAltRecBuffers(False);
if Change.UpdateStatus = usUnmodified then
Change.InitAltRecBuffers(True);
case Change.UpdateStatus of
usModified: ;//your logic read codes in Provider.pas for further hint
usInserted: ;//your logic read codes in Provider.pas for further hint
usDeleted: ;//your logic read codes in Provider.pas for further hint
end;
Change.Next;
end;
Above should work regardless of number of modified
Cheers
Pham

Resources