This question already has answers here:
Communicate between two windows forms in C#
(12 answers)
Closed 6 years ago.
I have a form with a label, also an external class. In my class, I have a for loop of 1 to 1000. How can I show the value of 1 to 1000 from my class to my form label?
//external class
public class TestClass
{
public void myLoop()
{
for (int i = 1; i <= 1000; i++)
{
// show value of i to label
}
}
}
Assuming you have a reference to your form as form1, and that form has a label named label1 that is public/accessible to TestClass:
public class TestClass
{
public void myLoop()
{
for (int i = 1; i <= 1000; i++)
{
// show value of i to label
form1.label1.Text = i.ToString();
// allow message pumping to redraw the label
Application.DoEvents();
// pause long enough to see it before the next one happens
System.Threading.Thread.Sleep(100);
}
}
}
I wouldn't recommend using Application.DoEvents for production code normally; but if you are running the UI thread and not using async code, this would be the "hacky" way to get all the window events pumping (mostly WM_PAINT to get the label to redraw itself) during your loop.
A better way is to use events:
public class TestClass {
public class ProgressEventArgs : EventArgs {
public int Value { get; set; }
}
public event EventHandler<ProgressEventArgs> Progress;
public void myLoop() {
for (int i = 0; i <= 1000; ++i) {
var evt = Progress;
if (evt != null) {
evt.Invoke(this, new ProgressEventArgs() { Value = i; });
}
}
}
}
and handle that event in the form:
public class TestForm : Form {
private somevent_click(object sender, EventArgs evt) {
var test = new TestClass();
test.Progress += test_Progress;
test.myLoop();
}
private void test_Progress(object sender, TestClass.ProgressEventArgs evt) {
label1.Text = evt.Value;
}
}
Note that these will happen in the same thread, so depending on what else you do in your loop, you may not get message pumping. Consider using a background thread or async code instead.
Related
how can i update a second Window (It's a simple WPF Window with a Progressbar to show a progress) from within my MainWindow inside a loop?
Well, i am loading a couple of Files which can cost about 10 Seconds. While loading i want my ProgressWindow to show how many Files have been loaded.
Here is my MainThread:
private void loadFiles(String[] paths) {
// Show Progress Window
MyProgressWindow progWindow = new MyProgressWindow();
progWindow.ShowProgressWindow(0, paths.Length);
//Globals.MyProgressController.ShowProgressWindow(0, paths.Length);
foreach (String path in paths) {
// Load the file
loadFile(path);
// Refresh the Table
refreshTableAsync();
// Update Progress Window
progWindow.UpdateProgressWindow(1);
//Globals.MyProgressController.UpdateProgressWindow(1);
}
// Close Progress Window
progWindow.CloseProgressWindow();
//Globals.MyProgressController.CloseProgressWindow();
}
Here is the ProgressWindowController
public class MyProgressWindow {
private ProgressWindow progressWindow;
public void ShowProgressWindow(int startIndex, int maxIndex) {
progressWindow = new ProgressWindow();
progressWindow.Initialize(startIndex, maxIndex);
progressWindow.Show();
}
public void UpdateProgressWindow(int value) {
progressWindow.Update(value);
//progressWindow.Update();
}
public void CloseProgressWindow() {
progressWindow.Close();
}
}
And finally the ProgressWindow itself:
public partial class ProgressWindow : Window {
private int actualValue = 0;
private int maxValue = 0;
public ProgressWindow() {
InitializeComponent();
}
public void Initialize(int startValue, int maxValue) {
this.actualValue = startValue;
this.maxValue = maxValue;
}
public void Update(int value) {
actualValue += value;
lblPercentage.Content = actualValue + " / " + maxValue;
pbProgress.Value = ((double)value / maxValue) * 100;
}
}
I have read thati should do it via threading, but i cannot get it to work, and all code I've found which helped other people, doesn't do what i need.
Thanks for your help
Your question is posed incorrectly. You are updating the second window as per your question, however the suggested advice of using a separate thread is what you're really asking about.
I suggest reading about the BackgroundWorker class Here which even explains progress updates.
i have app with gui
I put function checkproxy() in Form1.cs it works correctly and i want move function checkproxy() to other class but if i put checkproxy() in other class it will error with Invoke and richTextBox3
namespace test3
{
public partial class Form1 : Form
{
public bool continueThreads = false;
string[] proxyList = null;
List<Thread> threadList = new List<Thread>();
int proxynum = 0;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int n = (int)numericUpDown1.Value;
Thread[] tl = new Thread[n + 1];
threadList = tl.ToList();
for (int i = 0; i <= n; i++)
{
threadList[i] = new Thread(new ThreadStart(checkproxy));
}
for (int i = 0; i <= n; i++)
{
threadList[i].Start();
}
continueThreads = true;
proxyList = richTextBox1.Lines;
}
public void checkproxy()
{
while (continueThreads)
{
if (proxynum >= proxyList.Length)
{
continueThreads = false;
}
if (proxynum < proxyList.Length)
{
string proxy = proxyList[proxynum];
proxynum += 1;
string info = "";
try
{
Thread.Sleep(1000);
info += "Live || " + proxy + Environment.NewLine;
this.Invoke(new Action(() => richTextBox3.Text += info));
}
catch
{
}
}
}
}
}
}
this is screenshot error
Your method checkproxy uses Form1 class members (continueThreads, proxynum and others) directly.
If you really want do move it outside of this class (I'm not sure it is good idea since this method looks very closely related to your class) - you need to refactor this method and pass all class members it uses as method input parameters like
public void checkproxy(bool continueThreads.....)
Because this is a System.Windows.Forms.Form in original context.
To be able to Invoke interface update from another thread/async task, you need to use it (as you did correctly in your original code).
But once you move the function into separate class, there is no more notion of a Conntrol or Form there, so this is a class itself, which does not have Invoke implementation.
One possible solution: you need to refactor your method in a way, that he is able to call Form's function, that internally calls Invoke.
I have my progressbar in form1. and i have another class called process.cs
In the main form I have these two functions...
public void SetProgressMax(int max)
{
uiProgressBar.Value = 0;
uiProgressBar.Minimum = 0;
uiProgressBar.Maximum = max;
}
public void IncrementProgress()
{
uiProgressBar.Increment(1);
}
How can I call these functions from my process.cs class?
You're creating a "tightly coupled" solution which requires the process class to have a reference to the Form (I'll use Form1 in this example).
So in your process class, you need to create a variable to store the reference to the form, and allow a way to pass that reference in. One way is to use the constructor of the class:
public class process
{
private Form1 f1 = null;
public process(Form1 f1)
{
this.f1 = f1;
}
public void Foo()
{
if (f1 != null && !f1.IsDisposed)
{
f1.SetProgressMax(10);
f1.IncrementProgress();
f1.IncrementProgress();
f1.IncrementProgress();
}
}
}
Here's an example of creating the process class from within Form1 and passing the reference in:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
process p = new process(this);
p.Foo();
}
public void SetProgressMax(int max)
{
uiProgressBar.Value = 0;
uiProgressBar.Minimum = 0;
uiProgressBar.Maximum = max;
}
public void IncrementProgress()
{
uiProgressBar.Increment(1);
}
}
--- EDIT ---
Here's a boiled down version of the "loosely coupled" events approach (ignoring multi-threading issues for simplicity):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
process p = new process();
p.Progress += p_Progress;
p.Foo();
}
void p_Progress(int value)
{
uiProgressBar.Value = value;
}
}
public class process
{
public delegate void dlgProgress(int value);
public event dlgProgress Progress;
public void Foo()
{
// ... some code ...
// calcuate the current progress position somehow:
int i = (int)((double)3 / (double)10 * (double)100); // 30% complete
// raise the event if there are subscribers:
if (Progress != null)
{
Progress(i);
}
}
}
Note that in this approach the process class has no reference to the form and has no idea what is being done with the progress value. It simply reports the progress and the subscriber (the form in this case) decides what to do with that information.
After reading online tutorials regarding events , I think I almost have an idea of whats going on. I developed the following extremely simple code to trigger an event in case a value is greater than 5.I know the code is pretty useless but I am using it to get my point across. (Instead of a main I just used a button event to trigger the code.)
//declare the delegate
public delegate void MyDelegate(string str);
public class SomeClass
{
public event MyDelegate MyEventFromDelegate;
private int i;
public int I
{
get
{ return i; }
set
{
if (value > 5)
{
MyEventFromDelegate("Value Greater than 5");
i = 0;
}
else
{
i = value;
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{ InitializeComponent(); }
public void Method_To_Call(String rx)
{ MessageBox.Show("This method will be called if greater than 5");}
private void button1_Click(object sender, EventArgs e)
{
SomeClass a = new SomeClass();
a.MyEventFromDelegate +=new MyDelegate(Method_To_Call);
a.I = 12;
}
}
The only concern I have here is when we want to raise an event with the statement
MyEventFromDelegate("Value Greater than 5");
What point is passing a parameters to the event is at this point if later (at button click event) we are actually going to assign it a function to call every time an event is triggered.
In your very simple example - there is no point, because SomeClass instance "a" is very short-lived, and because you are not using rx parameter passed to Method_To_Call.
Your form method button1_Click is connected to the button's Click event through a delegate. Button does not know what code will execute when it is clicked. All it has to do is to signal that is has been clicked. That signal is implemented using a delegate.
Your could have defined your delegate as having an integer parameter where the checked value is passed. Then although the event method would be invoked only when value is greater than 5, inside the event method you could do things differently depending on the actual value.
//declare the delegate
public delegate void MyDelegate(int aValue);
public class SomeClass
{
public event MyDelegate MyEventFromDelegate;
private int i;
public int I
{
get
{ return i; }
set
{
if (value > 5)
{
MyEventFromDelegate(value);
i = 0;
}
else
{
i = value;
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void Method_To_Call(int aValue)
{
MessageBox.Show("This method signals that value is greater than 5. Value=" + aValue.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
SomeClass a = new SomeClass();
a.MyEventFromDelegate +=new MyDelegate(Method_To_Call);
a.I = 12;
}
}
I have a library with an Interface.
Public Interface Progress
{
int ProgressValue{get;set;},
string ProgressText{get;set;},
}
Library has a method Create (dummy code):
Public Class TestLibrary
{
Progress _progress;
Public void Create()
{
foreach(var n in TestList)
{
// Do Something
_progress.ProgressValue = GetIndex(n);
_progress.ProgressText = "Updating..." + n;
}
}
}
I have a project that references this library and calls Create method. It even Implements Interface Progress.
Public Class TestProject : Progress
{
public int ProgressValue
{
get{return progressBar1.Value;}
set{progressBar1.Value = value;}
}
public int ProgressText
{
get{return label1.Text;}
set{label1.Text = value;}
}
}
Now when I run the application, Progress Bar behaves properly and shows the progress correctly, but the Text of label1 does not change at all. But it do change in the end of for loop and shows the last item in loop. Can anyone help me out in this?
Note: All these codes are written directly without testing as I don't have my application now with me. Sorry for any syntax errors.
It sounds like all your work is being done on the UI thread. Don't do that.
Instead, run the loop itself in a background thread, and use Control.Invoke or Control.BeginInvoke (probably in the Progress implementation) to marshal a call across to the UI thread just to update the UI. This will leave your UI responsive (and able to update labels etc) while it's still processing.
Used a Label instead of ProgressBar. You can try this code [using BackGroundWorker] -
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
private BackgroundWorker _worker;
BusinessClass _biz = new BusinessClass();
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
_worker.RunWorkerAsync();
}
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
int junk = 0;
for (i = 0; i <= 199990000; i++)
{
int result = _biz.MyFunction(junk);
junk++;
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// note I can pass the business class result also and display the same in the LABEL
_worker.ReportProgress(percentComplete, result);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
}
else if (e.Error != null)
{
// Do something with the error
}
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
}
public class BusinessClass
{
public int MyFunction(int input)
{
return input+10;
}
}
}
Posted the same a few days ago here
The code you posted uses one thread. That means every operation is excuted in sequence right after the previous one finished.
Since you can update GUI elements right away, I suppose the code to be run from main thread (a.k.a "GUI thread"). Blocking the GUI thread results in the GUI ("Graphical User Interface") not updating until there is some idle time for it.
Use following example to refresh your label every iteration so that it updates your UI.
label1.Text = i.ToString();
label1.Refresh();