I have implemented a delay in process after the user stops typing in the textbox
private System.Timers.Timer timer = new System.Timers.Timer(1000);
public SearchItem(){
timer.AutoReset = false;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e){
bindingSource.DataSource = logic.GetData(StockCodeTextBox.Text);
}
private void StockCodeTextBox_TextChanged(object sender, EventArgs e){
timer.Stop();
timer.Start();
if (StockCodeTextBox.Text.Equals("")){
AllItemsGridView.ClearSelection();
return;
}
}
after the user stops typing, why am I getting this error?
An exception of type 'System.InvalidOperationException' occurred
in System.Windows.Forms.dll but was not handled in user code
Additional information: Cross-thread operation not valid: Control
'AllItemsGridView' accessed from a thread other than the thread it was
created on.
Your UI is updated on the WinForms UI Dispatcher thread, while your Timer executes on a background thread. So you can't update the UI from a thread which does not own it. One workaround is to use this extension to update your UI from background thread:
public static class ControlExtension
{
public static void Do<TControl>(this TControl control, Action<TControl> action)
where TControl : Control
{
if (control.InvokeRequired)
control.Invoke(action, control);
else
action(control);
}
}
Sample use:
this.Do(f=>{f.AllItemsGridView.ClearSelection();})
In your code:
private void StockCodeTextBox_TextChanged(object sender, EventArgs e){
timer.Stop();
timer.Start();
this.Do(f=>{
if (f.StockCodeTextBox.Text.Equals("")){
f.AllItemsGridView.ClearSelection();
}
}
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e){
this.Do(f=>{
f.bindingSource.DataSource = logic.GetData(f.StockCodeTextBox.Text);
}
}
If you are free to choose a solution, consider rewriting your app with ReactiveUI and .Net 4.5.2.
https://github.com/AdaptiveConsulting/ReactiveTrader
If you are using System.Windows.Forms, you should use the Timer that comes with it so you don't have cross threading issues.
Instead of System.Timers.Timer use System.Windows.Forms.Timer.
Related
I'm working on a utility to copy a directory to multiple USB sticks. When Form1 loads, I would like for a label to display status "Detecting Disk Drives...", and then call a method to read the drives and populate the form with information. I have it working, except when the form loads it calls the method before displaying the label. Therefore it appears to be hung (the label is actually a white box on a gray background). I have tried timers and threads and everything I can think of, each with a different dead end. I have not yet found a way to have the label update before calling the method to read the drives.
The method getAndDisplayData() is wait 'hangs' my program. I would like for it not to be called until after the form has updated the text of lblDisplayStatus.Text
I also do not want the user to have to interact with the form before calling the method.
Here is my C# code:
private void USB_Utility_Load(object sender, EventArgs e)
{
lblDisplayStatus.Text = "Detecting Disk Drives...";
}
private void tabUSB_Prep_Enter(object sender, EventArgs e)
{
tabUSB_Prep.Controls.Clear();
getAndDisplayData();
}
Any help would be greatly appreciated.
Here is the code that I ended up with:
BackgroundWorker _worker;
private void USB_Utility_Load(object sender, EventArgs e)
{
_worker = new BackgroundWorker(); // Should be a field on the form.
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
lblDisplayStatus.Text = "Detecting Disk Drives...";
_worker.RunWorkerAsync();
}
//Background Worker
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblDisplayStatus.Text = "Done...";
displayData();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
getData();
}
The old-fashioned way would be to use a BackgroundWorker to run the blocking work in getAndDisplayData and then update the label before starting the worker and again when the worker completes.
Now-adays I assume you could also use tasks to get the exact same result, but I haven't actually tried it as WinForms is not often first choice for new projects.
BackgroundWorker _worker;
public void Form_Load(object sender, EventArgs e) {
_worker = new BackgroundWorker(); // Should be a field on the form.
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
lblDisplayStatus.Text = "Detecting Disk Drives...";
_worker.RunWorkerAsync();
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
lblDisplayStatus.Text = "Done...";
}
private void DoWork(object sender, DoWorkEventArgs e) {
getAndDisplayData();
}
About background workers
you can try this
private void USB_Utility_Load(object sender, EventArgs e)
{
lblDisplayStatus.Text = "Detecting Disk Drives...";
}
private void tabUSB_Prep_Enter(object sender, EventArgs e)
{
tabUSB_Prep.Controls.Clear();
Task<List<string>> t = new Task<List<string>>(DetectDrivesMethod());
t.ContinueWith((result)=>DisplayDrives(result.Result),TaskScheduler.FromCurrentSynchronizationContext);
t.Start();
}
You can tweak the code to fit your requirement. In DetectDriveMethod you will have logic to get data in background thread and in continue with, you can have logic to update UI. It is important that you pass syncronization context otherwise you will end up with Cross Thread exceptions.
If you want to use the new(ish) async/await pattern, you need to use the TaskScheduler to update the UI from the original thread. Here's an example:
// clear the form
tabUSB_Prep.Controls.Clear();
// This is just to show crossing a "context" works
string test = "";
// get the UI's current TaskScheduler
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
// This can be used to wrap a method that doesn't
// directly implement async/await
Task.Run(() =>
{
// Your method to GET the data (don't update the UI here)
test = "I can set a variable in this context!";
}).ContinueWith(task =>
{
if (task.Status == TaskStatus.RanToCompletion)
{
// update your UI here
// Again, this is just to show how crossing the context works
MessageBox.Show(test);
}
else
{
// update UI with an error message, or display a MessageBox?
}
}, scheduler);
I have service call in my form application. When my button has been clicked I am calling my service method. Here is my code block:
void listBox_SelectedValueChanged(object sender, EventArgs e)
{
if (listBox.SelectedItem.Equals("Demo"))
{
progressBar1.Visible = true;
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ObservableCollection<FilterInfo> filterInfos = new ObservableCollection<FilterInfo>();
FilterInfo myFilterObj = new FilterInfo("SUPERVISOR", userName);
filterInfos.Add(myFilterObj);
ObservableCollection<DEMOBec> demos = demoSvc.GetDemoByFilter(filterInfos, false);
dt = new Converter<DEMOBec>().ConvertDataTable(demos.ToList());
}
Before calling the service, I make my ProgressBar (Style = Marquee) visible. But I couldn't make it invisible in completed method because of Cross Thread problem.
When I tried to do something with in UI thread in BGWorker's Completed event,
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
}
I am getting an exception :
Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on.
How can I handle this problem?
You need to use Invoke() method
private delegate void ToDoDelegate();
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Invoke(new ToDoDelegate(() => progressBar1.Visible = false));
}
more info for Invoke()
http://msdn.microsoft.com/de-de/library/System.Windows.Forms.Control.Invoke(v=vs.110).aspx
Here's alittle piece of code that I always love to use, I don't remember where I got it from I put it in my Dropbox a long time ago.
public static class ControlExtensions
{
public static TResult InvokeEx<TControl, TResult>(this TControl control,
Func<TControl, TResult> func)
where TControl : Control
{
return control.InvokeRequired
? (TResult)control.Invoke(func, control)
: func(control);
}
public static void InvokeEx<TControl>(this TControl control,
Action<TControl> func)
where TControl : Control
{
control.InvokeEx(c => { func(c); return c; });
}
public static void InvokeEx<TControl>(this TControl control, Action action)
where TControl : Control
{
control.InvokeEx(c => action());
}
}
Usage
this.InvokeEx( x => Progressbar1.Visible = false); //Note 'x' could be any variable
This way you won't have to type all that out each time, and it's so simple to use. Just add this right to the end of your form class (after the last bracket closes). You can use this to preform any cross thread operations safely.
Modify the Visibility of the ProgressBar on the UI thread.
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)
delegate()
{
progressBar1.Visible = false;
});
I think the exception isn't raised in the RunWorkerCompleted event (as in comments said). My suggestion is, that the lines you try to access UI cmponents in the DoWork event fires it.
Try to put the data you need in the DoWork event into DoWorkEventArgs e. Tons of examples how to do it are provided on SO like this discussion.
When the work has finished, provide the "main UI thread" the modified/new data by using event args, too, like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
// do some work
e.Result = ... ; // modified/new data
}
and retrieve it in the following way:
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
var newData = (CastToWhatever) e.Result;
}
Hope it helps =)
EDIT
Your code in backgroundWorker1_RunWorkerCompleted() is definitly not the source of the exception. It built a little and simple example to demonstrate my argment:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync(textBox1.Text);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var num = Int32.Parse(e.Argument.ToString()) + 1;
backgroundWorker1.ReportProgress(num);
e.Result = num;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = (int)e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
textBox1.Text = e.Result.ToString();
}
What it does? On the WinForm there is a TextBox with a number, a ProgressBar and a Button to start a BackGroundWorker.
The worker gets the number from the TextBox (UI thread), DoWork increases it by 1 and saves it as result (new thread). Additionally it reports the increased number to the ProgressBar (cross-thread).
Finally RunWorkerCompleted reads out the result and stores the new value in the TextBox (UI thread).
So, please re-check your code ;-)
I am using Windows Form application for my thread demo. When I click on button1 ,It will start the thread and recursively doing a work.
Here the Form will not hang as I expected. I want to Stop the currently running thread when I click on Button2. However this won't work.
private void button1_Click(object sender, EventArgs e)
{
t = new Thread(doWork); // Kick off a new thread
t.Start();
}
private void button2_Click(object sender, EventArgs e)
{
t.Abort();
}
static void doWork()
{
while (true)
{
//My work
}
}
}
.When Im debugging, the button2_Click method won't hit the pointer. I think because Thread is keep busy.
Please correct me if I going wrong somewhere.
You can't kill thread like this. The reason is to avoid situations where you add lock in thread and then kill it before lock is released.
You can create global variable and control your thread using it.
Simple sample:
private volatile bool m_StopThread;
private void button1_Click(object sender, EventArgs e)
{
t = new Thread(doWork); // Kick off a new thread
t.Start();
}
private void button2_Click(object sender, EventArgs e)
{
m_StopThread = true;
}
static void doWork()
{
while (!m_StopThread)
{
//My work
}
}
This code allows the form to load before the data is loaded but some of the components on the form such as buttons and the datagridview itself are "invisible" until the data is loaded.
How do I fix this problem?
private void Form1_Load(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(delegate()
{
this.Invoke(new MyDelegate(delegate()
{
ReadXml(path);
Bind();
}));
}));
t.Start();
}
private void Bind()
{
dataGridView1.DataSource = table;
}
I also have this other code which works better, but requires that I use 2 new threads. This can't be the best way to do this, can it?
private void Form1_Load(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(delegate()
{
this.Invoke(new InvokeDelegate(delegate()
{
Thread t2 = new Thread(new ThreadStart(delegate()
{
ReadXml(path);
}));
t2.Start();
t2.Join();
Bind();
}));
}));
t.Start();
}
A form timer (not system timer) will let all other messages process before it fires.
Just give it an interval of like 100-250 milliseconds;
Set it to enabled=false in the designer;
Set it to enabled=true in the form_load event.
In the timer_tick event make the first line timer.enabled = false.
After that (still in the tick event) load your grid.
If you use BeginInvoke() instead of an Invoke() the code in the delegate will be executed on the current UI thread but it wont happen until after all the current UI work pending finishes (like the current Form1_Load invocation). Invoke is a synchronous call so that's why you needed the thread.
void Form1_Load(object sender, EventArgs e)
{
this.BeginInvoke(new MyDelegate(delegate()
{
ReadXml(path);
Bind();
}));
}
Currently I'm moving from java to c# and I'm full of crazy questions.
I'm trying new things on a windows form application and now,I would like to create a loop wich is executing a code every 1 minute,the problem is that I have no idea where to put this code.
For example,the form structure is like:
using System;
namespace Tray_Icon
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
notifyIcon1.ShowBalloonTip(5000);
}
private void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
{
label1.Text = "Baloon clicked!";
}
private void notifyIcon1_BalloonTipClosed(object sender, EventArgs e)
{
label1.Text = "baloon closed!";
}
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
}
private void option1ToolStripMenuItem_Click(object sender, EventArgs e)
{
//some code here
}
private void option2ToolStripMenuItem_Click(object sender, EventArgs e)
{
//some code here
}
private void option3ToolStripMenuItem_Click(object sender, EventArgs e)
{
label1.Text = "Option 3 clicked!";
}
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
option1ToolStripMenuItem_Click(this, null);
}
private void closeToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void btnWrite_Click(object sender, EventArgs e)
{
//code here
}
}
}
Where should I put the loop code? :(
Thanks in advance for ANY replay!!!
Add a Timer to your form:
set its Interval property to 60000 (one minute in milliseconds) and Enabled to True:
and attach an event handler to the Timer.Tick event, e.g. by double-clicking the timer in the Forms designer:
private void timer1_Tick(object sender, EventArgs e)
{
// do something here. It will be executed every 60 seconds
}
You would have to add a timer, and set the interval to 1000 miliseconds, and in the OnTick event you add the code with your loop
Timer tmr = null;
private void StartTimer()
{
tmr = new Timer();
tmr.Interval = 1000;
tmr.Tick += new EventHandler<EventArgs>(tmr_Tick);
tmr.Enabled = true;
}
void tmr_Tick(object sender, EventArgs e)
{
// Code with your loop here
}
You can't put any loop code in here.
In your designer look for the Timer control. When you have that, configure it to run every minute and place your code in the Timer_Tick event.
Or create a timer manually in code and respond to the event :) But for starters, doing it by the designer is easier!
Drag a Timer component on the Form and doubleclick it. There you go with the code.
The Timer component runs in the main thread so you can modify UI components without worrying.
Alternatively You could create a System.Timers.Timer, which has it's own thread and has some advantages, but possible caveats when modifying UI components. See http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx
Try to use Background Worker and put the code in the backgroundWorker.DoWork or use a Timer
Use System.Timers.Timer:
System.Timers.Timer aTimer;
{
aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = 60000;
aTimer.Enabled = true;
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
for using Timer see this tutorial: C# Timer
How you do it in Java platform?
I think Java should be the same with .net.
In fact, a form program is just normal program which contains a event dispatcher. The event dispatcher listen to the UI events and dispatch them to the event handlers. I think all the UI mode should like this, no matter Java or .net platform.
So generally speaking, you have 2 options:
Start the loop at beginning. In this case, you should insert your
code in the constructor of the Form.
Start the loop when user
click the button. In this case, you should insert your code in the
event handler function.
Yes, as others mentioned, you should use the timer. But this should after you know where your code should locate. You also can use a endless loop with a sleep call. But timer is a better solution.
Idea of timer is more better. But If you want to use threads. Then Follow this
Let me assume that You want to do it right from the start of program
You can write in body of function (event in fact) named Form1_Load as
Your actual code is just within while loop other code only to guide
I can guide if you don't know the use of threads in C#
bool button2Clicked = false;
private void Form1_Load(object sender, EventArgs e)
{
// A good Way to call Thread
System.Threading.Thread t1 = new System.Threading.Thread(delegate()
{
while (!button2Clicked)
{
// Do Any Stuff;
System.Threading.Thread.Sleep(60000); //60000 Millieconds=1M
}
});
t1.IsBackground = true; // With above statement Thread Will automatically
// be Aborted on Application Exit
t1.Start();
}