0

Work is done in a number of threads and a TListView is updated from these threads. They each have a reference to their associated TListItem in the ListView, so it doesn't matter if the listview gets sorted later. The right cell will be updated anyway.

But now I'd like to use a stringgrid instead of listview. However, if the user sorts the stringgrid, how will the thread know what cell to update? I find no reference to put in the thread for the callback. I'd like to have a reference and not have to look up the the right cell every time. Is this possible?

EDIT: Here is what I store in the list we have discussed in comments:

TInfoPackList contain objects of type:

TInfoPack = class
  ID: Integer;
  Name: string;
  Location: string;
  Thread: TMyThread;
end;

There are more members but these are the ones that are relevant to show in the grid.

Rimfire
  • 378
  • 1
  • 3
  • 12
  • Are you using the TStringGrid or a 3rd party StringGrid like TMS's AdvStringGrid? – Blurry Sterk Feb 18 '15 at 07:00
  • Flags are being raised by your post but I am going to assume that you already know that the VCL is not thread-safe and that you have to do special precautions when accessing anything of the VCL from threads. – Blurry Sterk Feb 18 '15 at 07:03
  • 4
    This sounds all wrong to me. Threads shouldn't have references to gui objects. Use a virtual control. Have the threads do their work. Let them update a shared data structure. Have the virtual control show a view of that. Use whatever synchronization is necessary. – David Heffernan Feb 18 '15 at 07:51
  • @BlurrySterk - Yes I know VCL/LCL is not threadsafe. Thats no problem, I know more or less how threads work. – Rimfire Feb 18 '15 at 22:18
  • @DavidHeffernan - The ListView is on the main form, and the thread class in its own unit. The data is in a threadsafe/typesafe list and after being loaded, the listview is populated with the lists data. And when a thread is created (suspended mode), the eventhandler that updates the listview is assigned to the threads methodpointer. And the thread uses synchronize when the event fires. I see only one questionable thing and thats the fact that the thread has the reference to a ListItem of the listview. But thats the only way I found to update the listview without doing lookups for each update? – Rimfire Feb 18 '15 at 22:30
  • @BlurrySterk - Its a usual TStringGrid. – Rimfire Feb 18 '15 at 22:31
  • You can certainly decouple the thread's updating from the presentation. And you should. – David Heffernan Feb 18 '15 at 22:38
  • @DavidHeffernan Sorry, I still dont understand. Can you be more concrete how to decouple? What kind of virtual control do you mean? – Rimfire Feb 19 '15 at 02:52
  • 1
    A virtual list view for instance. Anyway, do it as I originally said and it's decoupled. Threads work on shared dataa structure. List view presents it. – David Heffernan Feb 19 '15 at 08:01
  • @DavidHeffernan Aha. Now I understand, a ListView with OwnerData = true. I have implemented that now and the ListView is filled with data. However when the objects in the list are updated by the thread, the listview doesn't reflect the change. How to fix that? – Rimfire Feb 19 '15 at 16:03
  • 1
    You need to tell the list view that its items need to be repainted. Use the `UpdateItems` method to do that. http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TCustomListView.UpdateItems – David Heffernan Feb 19 '15 at 16:30

2 Answers2

1

Threads have to deal with some data storage, not visual component. Grid just shows the contents of data storage in needed order. If you need to keep sort order in the storage, maintain indexes.

Note that this approach resembles DataSet+TDBGrid

Edit:
1. Of course, threads may update visual controls, but with minimal information and minimal data flow. They are not intended for continuous interaction with interface.
2. Visual controls shouldn't be used for data storage. Consider Model-View-Controller pattern, that stimulates us to separate mechanisms of internal data representation and user view representation.

MBo
  • 77,366
  • 5
  • 53
  • 86
  • Thanks, but isn't it common to update visual controls from threads using method pointers and synchronization? How else to inspect realtime whats going on inside the thread. – Rimfire Feb 19 '15 at 00:11
1

TStringGrid in Delphi does not have sorting capabilities in which case you would have to sort it manually and as such you will be able to keep a list of which cell belongs to which thread. Easy.

With TMS TAdvStringGrid you can use the OnRawCompare event to do the comparison of each cell to do a manual sort where you will also be able to keep a list of which cell belongs to which thread.

But in my honest opinion I would rather do the following:

  • Create one string pointer in each thread. The thread will change that string whenever it wants to.
  • Add that string pointer into a global or parent-global TList or TThreadList depending on whether you have thread-safe ways of accessing the TList or not.
  • Depending on when you want to sort I would sort the list either as soon as the thread changes its string in its string pointer or do the sort after many threads' string pointers have changed maybe via a timer or after a certain amount in a ChangeCounter. TList has a Sort function which you will have to read up on how it is done.
  • After each sort I will then manually populate the StringGrid with the strings in the already sorted list.

Always keep in mind that you must have thread-safe ways of accessing the Lists. Things like CriticalSections. TThreadList has a lock functionality which locks the list access until unlocked.

You have to do more work here because just like MBo answered here, unlike TListView a StringGrid only stores strings which it displays and not Objects which you can access from a thread. So that makes it difficult for a thread to know where a string is inside a StringGrid unless you search for it but what about duplicates. So the answer lies in storing your data somewhere else like I described above and then only use the StringGrid to be the display of that data.

Blurry Sterk
  • 1,595
  • 2
  • 9
  • 18
  • BlurrySterk - Data is already stored in a threadsafe list. So as you said, instead of sorting the grid, I can sort the list instead and re-populate the grid. Thats fine. Regarding the thread, it is updating the grid by calling an eventhandler in the grids form. That handler is assigned to a methodpointer in the thread and called via synchronization. But believe it or not, after 5 hrs of trying, I cant figure out the syntax how to assign the string of the stringgrid cell to a string pointer and so change the value in the string... Can you show how? – Rimfire Feb 18 '15 at 22:15
  • What type of data is stored in the threadsafe list? Hopefully it is actual pointers to strings. If you re-populate the grid after sorting then you will not need to figure out a way to assign the string of the stringgrid cell to a string pointer and so change the value in the string. Just iterate through the sorted list and set each correlating cell to the value of the current item in the list at that point of the loop. Does it make sense? Loop through list, read the current item's string and set grid cell to same. A for loop with I as index will work. I will also point to grid row index. – Blurry Sterk Feb 18 '15 at 22:26
  • Another question.. How many items/strings are you talking about here? – Blurry Sterk Feb 18 '15 at 22:31
  • Thank you very much for your attention. I have added the data type to the question above. Should I then add the string pointer as a member of the InfoPack class? You see, it spawns a thread when InfoPack.Update is called. And then I could do MyThread.Create(MyStringPointer) and the thread has access to the right grid cell. Unfortunately I dont know how to use string pointers. – Rimfire Feb 18 '15 at 23:12
  • Number of Items is maximum 500, both items in list and rows in the stringgrid. They match each other. – Rimfire Feb 18 '15 at 23:19
  • You have now accepted my answer but I would like to know whether you got it right. – Blurry Sterk Feb 19 '15 at 06:13
  • Think so, but not with the string pointer. What you said about sort the list and then repopulate the grid was useful. The updating of the grid cell is done by an eventhandler thats assigned to a procedural pointer within the thread. Called by synchrionze. That event handler takes two arguments: the string message to show in the cell and the correct row number. The TInfoPack now has a GridNo integer field that the thread can access when it calls the eventhandler. When the list and grid is sorted, the GridNo value changes accordingly. And no search is needed. – Rimfire Feb 19 '15 at 15:19