I have win form which represents simple wcf client app. This client consumes wcf service over http.
Inside form there is loadingLabel.Text property where I want to display loading ... text. When wcf service returns data other property labelAllBooksNr.Text should be populated.
Service will return integer in allBooksNumber property.
private void Form1_Load(object sender, EventArgs e)
{
int allBooksNumber = BookAgent.CountAllBooks();
}
Since I do not have any experience in using threads I'm asking to someone provide the best pattern I should follow.
the best pattern you can use is the BackgroundWorker as Executes an operation on a separate thread and offers many methods
from MSDN
When you want a responsive UI and you are faced with long delays
associated with such operations, the BackgroundWorker class provides a
convenient solution.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int allBooksNumber = BookAgent.CountAllBooks();
e.Result = allBooksNumber;
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
label1.Text = "Loading....";
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = e.Result.ToString;
}
}
}'
Hope this help
Related
It doesn't show me anything inside the logBox, it just stays blank
namespace Clipboard_Logger
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
logBox.SelectionStart = logBox.TextLength;
logBox.ScrollToCaret();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
if (Clipboard.ContainsText(TextDataFormat.Text))
logBox.Text = logBox.Text + Clipboard.GetText(TextDataFormat.Text) + "\r\n";
}
}
}
}
You are using a background thread ( BackGroundWorker.DoWork ) to access controls on the UI thread. Controls can only be accessed from the UI thread.
Try adding a BackGroundWorker.ProgressChanged event and access your control from that. ProgressChanged runs from the UI thread.
Edit from your comment:
No, that's not what I meant, your're creating a new backgroundworker, you should use the existing one, like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ReportProgress(1);
}
Also, you need to copy text to the clipboard.
On .NET Windows form, I have Background worker component that works fine. I have 5 forms, that has basically same Background worker on it with same code.
Can I extract this code to other class and somehow use it, considering this is an event? This is code I have on form. It takes 20 lines of code, and it would be nice if this can be refactored. Note: as you can see, I have already put it to other class BackgroundWorkerHelper, but can I also somehow refactor this events on Background worker, so that it is in other class as well, this way code is less and reused.
private void RunBackgroundWorker(string infoLabelText, int imageIndex)
{
BackgroundWorkerHelper.Run(backgroundWorker, progressBar, infoLabelText, imageIndex);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorkerHelper.DoWork(backgroundWorker);
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BackgroundWorkerHelper.ProgressChanged(sender, e, progressBar);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BackgroundWorkerHelper.RunWorkerCompleted(sender, e, progressBar);
}
Note: for now I would like to avoid using user control. I know I could do it, but then you have code that handles placing user control and so on. I am still not very good in it.
Here is solution, thanks to rory who gave me idea how to do it. First, I made this class:
public class BackgroundWorkerHelper
{
private static string _infoLabelText = string.Empty;
public BackgroundWorker _BackgroundWorker;
private BarEditItem _marqueeInfo;//this is marquee progress bar
public BackgroundWorkerHelper(BarEditItem marqueeInfo)
{
_marqueeInfo = marqueeInfo;
_BackgroundWorker = new BackgroundWorker();
_BackgroundWorker.WorkerReportsProgress = true;
_BackgroundWorker.WorkerSupportsCancellation = true;
_BackgroundWorker.DoWork += backgroundWorker_DoWork;
_BackgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
_BackgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
}
public void Run(string labelText, int imageIndex)
{
_marqueeInfo.Caption = labelText;
_marqueeInfo.ImageIndex = imageIndex;
if (!_BackgroundWorker.IsBusy)
_BackgroundWorker.RunWorkerAsync();
else
_marqueeInfo.Caption = "Busy processing saving data, please wait...";
}
public void DoWork()
{
for (int i = 0; i <= 5; i++)
{
_BackgroundWorker.ReportProgress(i); // call backgroundWorker_ProgressChanged event and pass i (which is e argument e.ProgressPercentage) to update UI controls
Thread.Sleep(250);
}
}
public void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
_marqueeInfo.Visibility = BarItemVisibility.Always;
}
public void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_marqueeInfo.Visibility = BarItemVisibility.Never;
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DoWork();
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressChanged(sender, e);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
RunWorkerCompleted(sender, e);
}
then in FORM, in class level above constructor place
private readonly BackgroundWorkerHelper _backgroundWorkerHelper;
then in Form Constructor instantiate class
_backgroundWorkerHelper = new BackgroundWorkerHelper(marqueeInfo);
and then I just call it in my form
_backgroundWorkerHelper.Run("Saving", 14);
I'm new with .Net Threads. I understand that we can't work with WinForm GUI out of the main thread.
I want one of my method that update the WinForm GUI to run in the main thread right after a 2nd thread ends.
Here is a part of my code:
public class FormGApp : Form
{
private Thread m_LoginThread;
private void buttonLogin_Click(object sender, EventArgs e)
{
m_LoginThread = new Thread(new ThreadStart(this.login));
m_LoginThread.Start();
}
private void login()
{
LoginResult result = loginToServer();
this.User = result.LoggedInUser;
}
private void successfullyLogin()
{
// Update the WinForn GUI here...
// This method must run in the main thread!!!
}
}
How can I run the method successfullyLogin() when m_LoginThread ends?
You have a couple of options:
As #ScottChamberlain said in the comment, use a BackgroundWorker and use its Completed event to update the GUI
Use TPL Library in the following way:
Task.Run(() =>
{
//do work
}).ContinueWith(() =>
{
//do continuation
}, TaskScheduler.FromCurrentSynchronizationContext);
Use Application.Current.BeginInvoke or Application.Current.Invoke from your background thread
If you are using .Net 4.5, you can use async/await
async private void buttonLogin_Click(object sender, EventArgs e)
{
await Task.Run(() => login());
successfullyLogin();
}
Thanks you all to inspire my to use BackgroundWorker, it indeed solved this issue.
Here is my solution:
public partial class FormGApp : Form
{
private BackgroundWorker m_LoginBackgroundWorker;
// ctor:
public FormGApp()
{
// init thread:
this.m_LoginBackgroundWorker = new BackgroundWorker();
this.m_LoginBackgroundWorker.DoWork += this.LoginBackgroundWorker_DoWork;
this.m_LoginBackgroundWorker.RunWorkerCompleted += this.LoginBackgroundWorker_RunWorkerCompleted;
}
private void buttonLogin_Click(object sender, EventArgs e)
{
// start thread:
this.m_LoginBackgroundWorker.RunWorkerAsync();
}
private void LoginBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
this.login();
}
private void LoginBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.successfullyLogin();
}
private void login()
{
// code that take long time that executed in a separate thread...
}
private void successfullyLogin()
{
// Gui WinForm update code here...
}
I've run into some problems while using progress bars in Windows Forms. Say I have an algorithm with ten parts that runs on a button click. After each part, I'd want to update a progress bar on the form to 10% further along. However, when code is running, the Windows Form will not respond or update.
What is the correct do show progress on a form while code is running?
You need to use a BackgroundWorker.
A nice example can be found here: http://www.dotnetperls.com/progressbar
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e)
{
// Start the BackgroundWorker.
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
// Wait 100 milliseconds.
Thread.Sleep(100);
// Report progress.
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
// Set the text.
this.Text = e.ProgressPercentage.ToString();
}
}
}
Or you can use something like:
private void StartButtonClick(object sender, EventArgs e)
{
var t1 = new Thread(() => ProgressBar(value));
t1.Start();
}
private void ProgressBar(value1)
{
ProgressBar.BeginInvoke(new MethodInvoker(delegate
{
ProgresBar.Value++
}));
}
I suggest use TPL for such operations as more standardized, lightweight, robust and extendable
See for ex.: http://blogs.msdn.com/b/pfxteam/archive/2010/10/15/10076552.aspx
I have 2 buttons on page, where button1.visible = false.
Page code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
Class1.ShowButtonEvent += new Class1.ShowButton(Show);
}
}
public void Show()
{
Button1.Visible = true;
}
protected void Button2_Click(object sender, EventArgs e)
{
Class1.EventIT();
}
Class1:
public static class Class1
{
public delegate void ShowButton();
public static event ShowButton ShowButtonEvent;
public static void EventIT()
{
if (ShowButtonEvent != null)
ShowButtonEvent();
}
}
This is not working, how to fix it?
UPDATE:
No answers?
Ok, I will try to be more specific and explain what I am trying to do.
I start a time consuming job in a thread within static class.
I update the status (jquery progressbar) on UI using webmethods and javascript.
But, when thread is finished I need to preform some server side operations that it's not possible to do via javascript.
So the task is to invoke server side methods when thread finishes it's work.
You need to bind the event (Class1.ShowButtonEvent += new Class1.ShowButton(Show);
) on every postback.