[Closed] Dotnet: Updating a UI control from a different thread
from what I can tell, managed services doesn’t exist for max 2009
On the positive side, I solved my problem by creating a static c# class and routing my function through that.
Nice im happy that u solved your problem, it would be great if you can post example im interested how you did it with maxscript.
It wasn’t exactly with maxscript. As I said, I created the delegate in a c# class, and called it from maxscript using dotnetclass. The method invokes a datagridview progressbar column to change its value:
public class ProgressUpdater
{
public delegate void UpdateProgressDelegate(DataGridView ctr, int index, int amount);
public static void UpdateProgressBar(DataGridView ctr, int index, int amount)
{
UpdateProgressDelegate del = new UpdateProgressDelegate(UpdateSync);
IAsyncResult result = del.BeginInvoke(ctr, index, amount, CallBackVoid, null);
}
public static void UpdateSync(DataGridView ctr, int index, int amount)
{
ctr.Rows[index].Cells["Progress"].Value = amount;
}
public static void CallBackVoid(IAsyncResult result)
{
//garbage
}
}
You’ll notice I’m not using the .invokerequired test because I know for sure all the calls will be from a thread other than the UI thread.
I would pass in your case DataGridViewCell. The cell object can tell you the parent DataGridView, row, and column. You can sort rows (by progress for example) and it doesn’t need to update the progress pointer. Also you don’t need to hardcode Column’s name. Using the same class you can update any cell because value is a property of any datagridview cell types.
Other thing I want to understand is how the webClient object was run in other than UI thread. Probably I’m missing something. You can use UploadDataAsync, DownloadDataAsync, … other Async methods; and DownloadProgressChanged and UploadProgressChanged events to update the UI. It’s a theory, and it might not work in practice for any reason, but I don’t see the problem.
You’re right, I should change that, thanks.
sorry, I don’t understand what you mean, could you elaborate?
I know it’s not good programming practice to hardcode such values but this class only serves this purpose on the project and is not needed for something else. As it’s an on-the-fly assembly, it won’t be used for any other purpose.
I am using the Async methods of the webclient – UploadFileAsync and DownloadFileAsync. What you described was my starting point, but it was giving me errors because the threads that the Async methods started were trying to access the UI.
Surprisingly, using a maxscript try() catch() command inside the async callback events worked somehow, but it would give me the infamous maxscript garbage collection error whenever the GC would run afterwards.
#sorting:
you can make your own sorter for datagridview (for example based on some column values). In your case it’s nice to sort rows by progress. When you sort rows it changes row indexes, but cells are staying the same. So using cell passing method you don’t need to update row index every time when you sort the grid.
#column predefined name:
with cell passing method it doesn’t make any sense any more.
#using progress events:
As I assumed it might have some problems. I hope that is just MAX related issue and can be solved. Unfortunately I don’t have time right now to make a snippet in MAX and check it works, but if you make a simple sample it will be very interesting for me to play with.
Oh, I see now what you mean. In any case sorting is now necessary in my project.
You’re right, I changed it and removed column name for the reason you mentioned. Works great.
I will make a sample tomorrow, but it seems that the events of Up&DownloadFileAsync are not thread safe.
Alright, so I stripped the project down to a sample which uploads 5 junk files to an ftp address you will need to provide as I don’t know any public ones I can upload to.
Replace the 3 first lines of the struct to do this.
The annoying part is that somehow in this particular sample, everything works fine and there is no cross thread errors whatsoever even without using the c# delegate :banghead: :banghead: :banghead:
I hate it when that happens.
Uncompress everything to the same folder and run the ms.
There are two lines in the uploadprogresschanged event, one to update with the delegate and one for direct update.
heh… there is a bad news… it works great I don’t see any problems with Upload and Download.
I have some advice. You are using UserState of UploadProgressChangedEventArgs to get row’s index. Technically it’s not correct. I would override WebClient class to add new Object type property (Tag for example) to assign cell object. Using sender’s Tag in event I would set Value, Color, etc.
Overriding the class sounds a bit more elegant, but could you please explain why using the userstate for this purpose is not correct?
actually you are right. There is no reason to override webclient. The UserState property is Object type. So you can use it to assign grid’s cell (or row). For file size you can use TotalBytesToSend property.
I don’t like an idea to use row index. Because as I said the index can change during Uploading/Downloading process.
The reason I am not using the TotalBytesToSend property is that some FTPs including the one I am using do not return the correct value in this field, they always return 0 (something to do with passive transfers or something like that, I don’t remember). So I must know in advance the size of the file.
For downloads, I first query the size of the file on the ftp and the insert that into the userstate.
I agree it’s not safe to use row indices in general practice but because I know no sorting or deletion will take place it is safe in my case.
edit: Just out of curiosity, If I were saving the cell/row in the user state instead of just the index, would I be suffering a memory/performance hit in any way? Or does it not matter because they are reference values?
as i know it doesn’t matter.
ps. I found interesting way how to create new types of DataGridViewColumn. Technically it’s enough to override GetFormattedValue for cell object. As I have a time I will show the method by example of ProgressColumn.
I realized the reason I might have been having problems in my project but not in this sample is because in my project I first compress the files before uploading them using an external process.
The process Exited event is what triggers the start of the upload. I’ll try to check it out.