Updating progressBar using threads and delegate method with arguments in C++/CLI using Visual Studio 2022 - winforms

I have this code in my Managed C++/Cli in Visual Studio 2022, I want to update the progressBar value in different stages of my c++ function. So far I have 3 delegates UpdateUi(), UpdateUiDone() and UpdateProgress(int percent) which passes an int percent as an argument. These delegates have the methods UiDoSome(), UiDosomeDone() and UpdateProgressBar(int percent). How this code runs is that on a click of a button, a new thread starts which calls the function ThreadProc. SPP is the name of the windows form class. Here is my code:
private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) {
Thread^ t = gcnew Thread(gcnew ThreadStart(this, &SPP::ThreadProc));
t->Start();
}
ThreadProc function:
public: System::Void ThreadProc() {
label6->Invoke(gcnew UpdateUi(this, &SPP::UiDoSome));
-> progressBar1->Invoke(gcnew UpdateProgress(this, &SPP::UpdateProgressBar(25)));
//My code goes in here......
label6->Invoke(gcnew UpdateUiDone(this, &SPP::UiDosomeDone));
}
Delegates definition:
public: delegate void UpdateUi();
public: delegate void UpdateUiDone();
public: delegate void UpdateProgress(int percent);
Delegates' methods:
public: void UiDoSome() {
label6->Text = "processing...";
}
public: void UiDosomeDone() {
label6->Text = "Done!!!";
}
public: void UpdateProgressBar(int percent) {
progressBar1->Value = percent;
}
The problem is that , when invoking the progressBar in the ThreadProc function and I pass a value to the UpdateProgressBar method, the compiler throws an error: "expression must be an lvalue or a function designator". How can I solve this, is this even possible in C++? I know C# has no such issues. I appreciate your help. Thanks in advance.

You need to use an array to pass arguments when using Invoke(), like this:
array<System::Object^>^ params = gcnew array<System::Object^>(1);
params[0] = 25;
Invoke(gcnew UpdateProgress(this, &SPP::UpdateProgressBar), params);
But, you cannot use Invoke() in the first place, since UI elements (buttons, labels etc) can be safely modified only from the thread they were created by. Therefore, you have to use BeginInvoke() here. Also, you can use BeginInvoke of the form itself:
public: System::Void ThreadProc() {
BeginInvoke(gcnew UpdateUi(this, &SPP::UiDoSome));
array<System::Object^>^ params = gcnew array<System::Object^>(1);
params[0] = 25;
BeginInvoke(gcnew UpdateProgress(this, &SPP::UpdateProgressBar), params);
BeginInvoke(gcnew UpdateUiDone(this, &SPP::UiDosomeDone));
}

Related

How do I use my own function in a timer class?

The code is taken from here:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.timer?view=windowsdesktop-6.0
private:
static System::Windows::Forms::Timer^ myTimer = gcnew System::Windows::Forms::Timer;
static int alarmCounter = 1;
static bool exitFlag = false;
// This is the method to run when the timer is raised.
static void TimerEventProcessor( Object^ /*myObject*/, EventArgs^ /*myEventArgs*/ )
{
myTimer->Stop();
// Displays a message box asking whether to continue running the timer.
if ( MessageBox::Show( "Continue running?", String::Format( "Count is: {0}", alarmCounter ), MessageBoxButtons::YesNo ) == DialogResult::Yes )
{
// Restarts the timer and increments the counter.
alarmCounter += 1;
myTimer->Enabled = true;
}
else
{
// Stops the timer.
exitFlag = true;
}
}
For example, after the line myTimer->Stop(); I want to use my own method. How do I identify it? E0020 ID "draw 1" is not defined.
System:: Void Practform::MyForm::draw1() {
. . .
}
Please tell me, because I'm a little stalled, since I've never worked with this.
I have a feeling that what you are bumping up against is attempting to invoke an instance method from a static method. To do so, you would need to have an instance of the class which has the method, e.g:
ref struct Foo {
void InstanceMethod() {}
static void StaticMethod() {
auto instance = gcnew Foo();
instance->InstanceMethod();
}
}
called like so:
Foo::StaticMethod();
However, taking the example code, it could be easier (and more appropriate) to change the static methods to instance methods, like so:
using namespace System;
using namespace System::Windows::Forms;
ref struct MyForm : Form {
Timer ^myTimer = gcnew Timer();
MyForm(void) {
myTimer->Tick += gcnew EventHandler(this, &MyForm::TimerEventProcessor);
myTimer->Interval = 5000;
myTimer->Start();
}
void TimerEventProcessor(Object ^, EventArgs ^) {
myTimer->Stop();
draw1();
}
void draw1() {
MessageBox::Show("Done", "Timer is done", MessageBoxButtons::OK);
}
};
called like so:
auto form = gcnew MyForm();
form->Show();
Notes:
I'm assuming that you've added the code from the example into your own class, called MyForm
I've used struct throughout instead of class to make everything public - you should use the appropriate access modifiers to your use case
The most notable change is the use of the EventHandler constructor which takes an instance of the handler as its first argument, and the method to execute as its second.
The advantages of using instance methods and properties are that:
you will have access to this in the draw1() method (given the name of the method, is likely to want to draw using the form instance), and
the Timer instance will be garbage collected as appropriate,

How to add an event handler to button from array in c++ CLI?

I have an array of buttons and I want to add an event handler for them.
My array:
array<Button^>^ buttons = gcnew array<Button^>(10);
There I try to add an event handler:
private: void tasksButtons_Click(System::Object^ sender, System::EventArgs^ e) {
MessageBox::Show("Lol");
}
private: System::Void main_Load(System::Object^ sender, System::EventArgs^ e) {
int horizontal = 0, vertical = 0;
for each(Button^ i in buttons) {
i = gcnew Button();
i->Text = "i";
i->Width = 20;
i->Height = 20;
horizontal += 20;
i->Location = Point(horizontal, vertical);
this->Controls->Add(i);
i->Click += tasksButtons_Click;
}
}
It gives me an error because of i->Click += tasksButtons_Click. What is the correct syntax for this?
Standard warning: While it's certainly possible to write the main body of your application in C++/CLI, or even write the GUI in C++/CLI using WinForms, it is not recommended. C++/CLI is intended for interop scenarios: where C# or other .Net code needs to interface with unmanaged C++, C++/CLI can provide the translation between the two. For primary development, it is recommended to use C# with either WinForms or WPF if you want managed code, or C++ with MFC if you want unmanaged.
In C++/CLI, you need to instantiate the delegate explicitly, use a C++-style reference to the method, and specify the object to call the method on (for non-static methods only)
i->Click += gcnew EventHandler(this, &MyForm::tasksButtons_Click);
// ^^^^^^^^^^^^^^^^^^ instantiate explicitly
// ^^^^ specify the object to use
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ C++-style reference

Diffrernce between BackgroundWorker.ReportProgress() and Control.BeginInvoke()

What is the difference between options 1 and 2 in the following?
private void BGW_DoWork(object sender, DoWorkEventArgs e)
{
for (int i=1; i<=100; i++)
{
string txt = i.ToString();
if (Test_Check.Checked)
//OPTION 1
Test_BackgroundWorker.ReportProgress(i, txt);
else
//OPTION 2
this.BeginInvoke((Action<int, string>)UpdateGUI,
new object[] {i, txt});
}
}
private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
UpdateGUI(e.ProgressPercentage, (string)e.UserState);
}
private void UpdateGUI(int percent, string txt)
{
Test_ProgressBar.Value = percent;
Test_RichTextBox.AppendText(txt + Environment.NewLine);
}
Looking at reflector, the Control.BeginInvoke() appears to use:
this.FindMarshalingControl().MarshaledInvoke(this, method, args, 1);
Which seems to eventually call some native functions like PostMessage(), couldn't exactly figure out the flow from reflector (pesky compiler goto optimizations)
Whereas BackgroundWorker.Invoke() appears to use:
this.asyncOperation.Post(this.progressReporter, args);
Which seems to eventually call ThreadPool.QueueUserWorkItem()
(I'm just guessing these are the relevant function calls for each case.) If I understand correctly, using the ThreadPool would not guarantee execution order whereas using the Post mechanism would. Perhaps that would be a potential difference ? (EDIT - I couldn't synthesize such a situation - call order seems to be preserved in both cases, at least in my simple tests.)
Thanks!
They are both the same. The call you're seeing in BackgroundWorker uses SynchronizationContext. Indeed the default implementation of the Post() method uses the thread pool, but when starting a Windows Forms app, the default synchronization context is replaced by WindowsFormsSynchronizationContext, which actually calls Control.BeginInvoke().
One big difference is that Control.Invoke will block until the UpdateGUI call has been executed and completed, whereas BackgroundWorker.ReportProgress will not block (it will return immediately, before the BackgroundWorker raises the event).
If you want them to behave the same, call Control.BeginInvoke (which doesn't block) instead.
I've found a significant difference. Closing the form while the BGW is running will cause this.Invoke() and this.BeginInvoke() to throw an ObjectDisposedException. The BGW ReportProgress mechanism circumvents that. To enjoy the best of both worlds, the following pattern works nicely
public partial class MyForm : Form
{
private void InvokeViaBgw(Action action)
{
Packing_Worker.ReportProgress(0, action);
}
private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (this.IsDisposed) return; //You are on the UI thread now, so no race condition
var action = (Action)e.UserState;
action();
}
private private void BGW_DoWork(object sender, DoWorkEventArgs e)
{
//Sample usage:
this.InvokeViaBgw(() => MyTextBox.Text = "Foo");
}
}

Winforms StatusStrip - why are there periods where it is blank when I'm updating it?

BACKGROUND: I have a WindowForms v3.5 application with a StatusStrip set to be used as a TooStripStatusLabel. I'm issues quite a lot of updates to it during a task that is running, however there are noticable periods where it is BLANK. There are no points when I am writing a blank to the status strip label either.
QUESTION: Any ideas why I would be seeing period where the status strip label is blank, when I don't expect it to be?
How I update it:
private void UpdateStatusStrip(string text)
{
toolStripStatusLabel1.Text = text;
toolStripStatusLabel1.Invalidate();
this.Update();
}
PS. Calling Application.DoEvents() after the this.Update() does not seem to help. I actually am calling this via the backgroundworker control, so:
(a) I start up the background worker:
private void Sync_Button_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
DisableUpdateButtons();
}
(b) the background worker calls updates:
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
backgroundWorker1.ReportProgress(1, "Example string");
MainForm.MyC.SyncFiles(sender);
}
(c) The MyC business class uses it too, e.g.
public void SyncFiles(object sender)
{
BackgroundWorker bgw = (System.ComponentModel.BackgroundWorker) sender;
bgw.ReportProgress(1, "Starting sync...");
.
.
.
}
(d) This event picks it up:
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
UpdateStatusStrip((string)e.UserState);
}
(e) And again the update status strip
private void UpdateStatusStrip(string text)
{
toolStripStatusLabel1.Text = text;
toolStripStatusLabel1.Invalidate();
this.Update();
}
Does this help?
The reason is possibly in the caller of this function. If you call it from another thread, use Control.BeginInvoke instead of direct call. If you call it from the main application thread during long processing, try Application.DoEvents after UpdateStatusStrip call.

OpenNetCF FTP class multithreading question

Currently, I have something like:
public partial class Form1 : Form
{
delegate void StringDelegate(string value);
private FTP m_ftp;
public Form1()
{
InitializeComponent();
}
private void connect_Click(object sender, EventArgs e)
{
OnResponse("Connecting");
m_ftp = new FTP(server.Text);
m_ftp.ResponseReceived += new FTPResponseHandler(m_ftp_ResponseReceived);
m_ftp.Connected += new FTPConnectedHandler(m_ftp_Connected);
m_ftp.BeginConnect(user.Text, password.Text);
}
void m_ftp_Connected(FTP source)
{
// when this happens we're ready to send command
OnResponse("Connected.");
}
void m_ftp_ResponseReceived(FTP source, FTPResponse Response)
{
OnResponse(Response.Text);
}
private void OnResponse(string response)
{
if (this.InvokeRequired)
{
this.Invoke(new StringDelegate(OnResponse), new object[] { response } );
return;
}
}
private void getFileList_Click(object sender, EventArgs e)
{
FTPFiles files = m_ftp.EnumFiles();
fileList.Items.Clear();
foreach (FTPFile file in files)
{
fileList.Items.Add( new ListViewItem( new string[] { file.Name, file.Size.ToString() } ));
}
tabs.SelectedIndex = 1;
}
private void upload_Click(object sender, EventArgs e)
{
FileStream stream = File.OpenRead("\\My Documents\\My Pictures\\Waterfall.jpg");
m_ftp.SendFile(stream, "waterfall.jpg");
stream.Close();
}
Which works fine - this example was taken from the samples. However, after a recent re-visit I have a question. In this particular case since OnResponse() function doesn't update the UI, it seems to serve no purpose here. I removed it (as well as all the calls to it) and it still works like before. Am I missing something?
After reading up more about multi threading with forms, I came to understand that this mechanism (demonstrated in the code above) is there to make sure the UI is responsive.
So in case when we need to say, update a UI element (such as textbox, label etc) we would have OnResponse implemented as follows:
delegate void StringDelegate(string dummy);
void OnResponse(string dummy)
{
if(!InvokeRequired)
{
button1.Text = dummy;
}
else
Invoke(new StringDelegate(OnResponse),new object[] {enabled});
}
If this function is implemented as:
delegate void StringDelegate(string dummy);
void OnResponse(string dummy)
{
if(InvokeRequired)
{
Invoke(new StringDelegate(OnResponse),new object[] {dummy});
return;
}
}
What's the use to have it at all? Is it absolutely necessary?
And another question: is ftp object running on its own thread here?
The FTP object is definitely running on its own thread. How do I know? This line:
m_ftp.BeginConnect(user.Text, password.Text);
This is an asynchronous method. Once you call this, the FTP component will use a thread from the .NET threadpool to do all of the work. This dedicated thread is the one that is used to "raise" the events. Ultimately a "raised event" is just one or more method calls to all of the delegates added to the event invocation list; it is this dedicated thread spun up by the Begin method that calls these methods. This thread is not the same thread as the thread that runs the UI, hence the need for the Invoke calls.
If you want the FTP component to use the UI thread, you'd use the Connect method instead of the BeginConnect method. This means your events wont work either, nor will your UI respond to interaction - this is completely expected because a thread can only do one thing at a time: it's either servicing the UI, or executing the FTP code. This is why you need a 2nd thread.
Make sense?
-Oisin

Resources