korrawit
korrawit

Reputation: 1000

Datagridview with System.Threading.Timer

This problem caused the program to run a few minutes. Before I used System.Timers.Timer and not found problem ,until I changed to System.Threading.Timer.

I would like to know that ...

What is a root cause of this problem ?

How to solve this problem ?

A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
    System.Transactions Critical: 0 : <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Critical"><TraceIdentifier>http://msdn.microsoft.com/TraceCodes/System/ActivityTracing/2004/07/Reliability/Exception/Unhandled</TraceIdentifier><Description>Unhandled exception</Description><AppDomain>InternetCafe.vshost.exe</AppDomain><Exception><ExceptionType>System.InvalidOperationException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>BindingSource cannot be its own data source. Do not set the DataSource and DataMember properties to values that refer back to BindingSource.</Message><StackTrace>   at System.Windows.Forms.BindingSource.get_Count()
       at System.Windows.Forms.CurrencyManager.get_Count()
       at System.Windows.Forms.DataGridView.DataGridViewDataConnection.currencyManager_ListChanged(Object sender, ListChangedEventArgs e)
       at System.Windows.Forms.CurrencyManager.OnListChanged(ListChangedEventArgs e)
       at System.Windows.Forms.CurrencyManager.List_ListChanged(Object sender, ListChangedEventArgs e)
       at System.Windows.Forms.BindingSource.OnListChanged(ListChangedEventArgs e)
       at System.Windows.Forms.BindingSource.InnerList_ListChanged(Object sender, ListChangedEventArgs e)
       at System.ComponentModel.BindingList`1.OnListChanged(ListChangedEventArgs e)
       at System.ComponentModel.BindingList`1.Child_PropertyChanged(Object sender, PropertyChangedEventArgs e)
       at InternetCafe.Computer.NotifyPropertyChanged(String info) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\Computer.cs:line 163
       at InternetCafe.Computer.UsedTimeValueChanged(Object sender, PropertyChangedEventArgs e) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\Computer.cs:line 149
       at InternetCafe.TimerManager.OnTimeChanged(PropertyChangedEventArgs e) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\TimerManager.cs:line 62
       at InternetCafe.TimerManager.set_TimeSinceStartTime(TimeSpan value) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\TimerManager.cs:line 30
       at InternetCafe.TimerManager.tmrThreadingTimer_TimerCallback(Object state) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\TimerManager.cs:line 50
       at System.Threading._TimerCallback.TimerCallback_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading._TimerCallback.PerformTimerCallback(Object state)</StackTrace><ExceptionString>System.InvalidOperationException: BindingSource cannot be its own data source. Do not set the DataSource and DataMember properties to values that refer back to BindingSource.
       at System.Windows.Forms.BindingSource.get_Count()
       at System.Windows.Forms.CurrencyManager.get_Count()
       at System.Windows.Forms.DataGridView.DataGridViewDataConnection.currencyManager_ListChanged(Object sender, ListChangedEventArgs e)
       at System.Windows.Forms.CurrencyManager.OnListChanged(ListChangedEventArgs e)
       at System.Windows.Forms.CurrencyManager.List_ListChanged(Object sender, ListChangedEventArgs e)
       at System.Windows.Forms.BindingSource.OnListChanged(ListChangedEventArgs e)
       at System.Windows.Forms.BindingSource.InnerList_ListChanged(Object sender, ListChangedEventArgs e)
       at System.ComponentModel.BindingList`1.OnListChanged(ListChangedEventArgs e)
       at System.ComponentModel.BindingList`1.Child_PropertyChanged(Object sender, PropertyChangedEventArgs e)
       at InternetCafe.Computer.NotifyPropertyChanged(String info) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\Computer.cs:line 163
       at InternetCafe.Computer.UsedTimeValueChanged(Object sender, PropertyChangedEventArgs e) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\Computer.cs:line 149
       at InternetCafe.TimerManager.OnTimeChanged(PropertyChangedEventArgs e) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\TimerManager.cs:line 62
       at InternetCafe.TimerManager.set_TimeSinceStartTime(TimeSpan value) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\TimerManager.cs:line 30
       at InternetCafe.TimerManager.tmrThreadingTimer_TimerCallback(Object state) in C:\Users\at0m\Documents\Visual Studio 2010\Projects\InternetCafe\InternetCafe\TimerManager.cs:line 50
       at System.Threading._TimerCallback.TimerCallback_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading._TimerCallback.PerformTimerCallback(Object state)</ExceptionString></Exception></TraceRecord>

TimerManager Class

Property TimeSinceStartTime

public TimeSpan TimeSinceStartTime
    {
        get { return new TimeSpan(timeSinceStartTime.Hours, timeSinceStartTime.Minutes, timeSinceStartTime.Seconds); }
        set
        {
            if (timeSinceStartTime != value)
            {
                timeSinceStartTime = value;
                OnTimeChanged(new PropertyChangedEventArgs("TimeSinceStartTime"));
            }
        }
    }

TimerManager Constructor

 public TimerManager(frmMainServer main)
    {
        _timer = new System.Threading.Timer(new
                   TimerCallback(tmrThreadingTimer_TimerCallback),
                   null, System.Threading.Timeout.Infinite, 1000);
        _main = main;
    }

OnTimeChanged

private void OnTimeChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, e);
        }
    }

Timer callback

private void tmrThreadingTimer_TimerCallback(object state)
    {
        TimeSinceStartTime += TimeSpan.FromSeconds(1);
    }

Computer Class

Important Property

public decimal TotalMoney
    {
        get
        {
            return this.totalMoney;
        }

        set
        {
            if (value != this.totalMoney)
            {
                this.totalMoney = value;
                NotifyPropertyChanged("TotalMoney");
            }
        }
    }

    public TimerManager UsedTime
    {
        get
        {
            return this.usedTimeValue;

        }

        set
        {
            if (value != this.usedTimeValue)
            {
                this.usedTimeValue = value;
                NotifyPropertyChanged("UsedTimeValue");
                usedTimeValue.PropertyChanged += new PropertyChangedEventHandler(UsedTimeValueChanged);       
            }
        }
    }

If time changed calculate fee, notify Usedtime and TotalMoney.

 private void UsedTimeValueChanged(object sender, PropertyChangedEventArgs e)
   {
       NotifyPropertyChanged("UsedTime");
       this.totalMoney = (decimal)(this.UsedTime.TimeSinceStartTime.Hours * this.session.Member.MemberTariff + this.UsedTime.TimeSinceStartTime.Minutes * (this.session.Member.MemberTariff / 60));
       NotifyPropertyChanged("TotalMoney");
   }

Main Form

Set CompList gridview datasource with dbBindingSource and set dbBindingSource datasource with Computers binding list.

public BindingList<Computer> computers = new BindingList<Computer>();
.
.
.
compList.DataSource = dbBindingSource;
dbBindingSource.DataSource = computers;

Upvotes: 0

Views: 1674

Answers (1)

Scott Lerch
Scott Lerch

Reputation: 2638

The root cause is that you're changing a data source in a non-UI thread bound to a UI control. The easiest solution is to use a System.Windows.Forms.Timer which will always execute on the UI thread, however, it's not very high resolution or accurate. Your next option is to make sure your PropertyChanged are raised on the UI thread using a SynchronizationContext or by using Invoke/BeginInvoke.

Upvotes: 2

Related Questions