0

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.

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Rjmr
  • 19
  • 9
  • You are using WPF right? And you are getting the error in the timer_Elapsed method? – Jens Mar 03 '15 at 13:21
  • possible duplicate of [WPF and cross thread operations](http://stackoverflow.com/questions/2172229/wpf-and-cross-thread-operations) –  Mar 03 '15 at 13:28
  • yes. i am getting error in the timer_Elapsed method. – Rjmr Mar 03 '15 at 13:30

2 Answers2

3

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

Darek
  • 4,687
  • 31
  • 47
  • 1
    It would be nice if you add a reason why to execute the action on the UI-thread :) Reading the question indicates that the OP doesn't know anything regarding this issue ... –  Mar 03 '15 at 13:27
  • It's magic, @Rjmr ... :) Kidding aside, the extension simply extends any `Control` to have an extra method, wonderful isn't it? A `Form` derives from a `Control` so every form gets a new method, `Do`, which checks if the operation passed into it, the stuff in-between the curly brackets, should execute on the thread it arrived on, or if it should be redirected to the UI thread. Makes sense? – Darek Mar 03 '15 at 13:34
  • 1
    i think in his code it should be: `this.Do(f=>{ bindingSource.DataSource = logic.GetData(StockCodeTextBox.Text); }` – Jens Mar 03 '15 at 13:37
  • thanks for letting me understand. But, i still have few questions on my mind. where would i put this line of code? `bindingSource.DataSource = logic.GetData(StockCodeTextBox.Text);` – Rjmr Mar 03 '15 at 13:38
  • Updated with a sample, but you should really, really consider ReactiveUI. Makes this whole thing soooo much simpler. – Darek Mar 03 '15 at 13:40
  • 1
    thank you so much guys for helping me with this one. if you have any suggestions on what topic/s should i study more, please comment it here. thanks again. – Rjmr Mar 03 '15 at 13:52
0

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.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • that's not the problem. it's a problem of wpf updating the UI. He needs to use the Dispatcher in the `bindingSource.DataSource = logic.GetData(StockCodeTextBox.Text);` line i think. – Jens Mar 03 '15 at 13:27
  • 2
    @JensHorstmann Why do you assume WPF when the error message clearly says it's WinForms? – nvoigt Mar 03 '15 at 13:29
  • because i had this exception with the same problem in wpf - that's why. – Jens Mar 03 '15 at 13:31
  • i may be wrong with wpf, but it seems to be the same with Win Forms ;) – Jens Mar 03 '15 at 13:32
  • i am getting error in the timer_Elapsed method sir. – Rjmr Mar 03 '15 at 13:32