2011-06-02 91 views
1

我一直在困惑這一個幾天現在,它讓我非常毆打,但說實話我並不是那麼有經驗,我遇到了麻煩DataGridView - 這似乎是一個常見的話題。將DataGridView綁定到DataSource - 引發CurrencyError IndexOutOfRangeException

public partial class frmMain : Form 
{ 
    ServerConnection sabCom; 
    private BindingSource jobSource = new BindingSource(); 
    private void timer1_Tick(object sender, EventArgs e) 
    { 
      if (bgUpdateThread.IsBusy == false) 
      { 
       bgUpdateThread.RunWorkerAsync(sabCom); 
      } 
     } 
    } 

    private void frmMain_Load(object sender, EventArgs e) 
    { 
     timer1.Interval = 3000; 
     timer1.Start(); 
    } 

    private void bgUpdateThread_DoWork(object sender, DoWorkEventArgs e) 
    { 
     ServerConnection s = e.Argument as ServerConnection; 
     s.update(); 
     e.Result = s; 
    } 

    private void bgUpdateThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     this.sabCom = e.Result as ServerConnection; 

     if (dgvQueueFormatted == false) 
     { 
      dgvQueue_Init(); //Applies formatting and loads column width. Inits data sources. 
     } 
     else 
     { 
      dgvQueue_Update(); 

     } 
    } 

    private void dgvQueue_Update() 
    { 
     dgvQueue.Refresh(); 
    } 

    private void dgvQueue_Init() 
    { 
     try 
     { 
      jobSource.DataSource = sabCom.queue.jobs; 
      dgvQueue.DataSource = jobSource; 
      try 
      { 
       //Apply saved column spacing to the dgvQueue 
       //Uses reflection to set dgvQueue to DoubleBuffer 
      } 
      catch 
      { } 
     } 
     catch 
     { } 
    } 

    private void frmMain_FormClosing(object sender, FormClosingEventArgs e) 
    { 
     //Saves information about the dgvQueue on shutdown. 
    } 

隊列類:

public class Queue 
{ 
    private string _status; 
    public string status { get { return _status; } set { _status = value; } } 
    private string _eta; 
    public string eta { get { return _eta; } set { _eta = value; } } 
    private List<Job> _jobs; 
    public List<Job> jobs 
    { 
     get 
     { 
      return _jobs; 
     } 
     set 
     { 
      _jobs = value; 
     } 
    } 
    private List<string> _categories; 
    public List<string> categories { get { return _categories; } set { _categories = value; } } 

    private XmlDocument xmld; 
    private ServerConnection m_parent; 
    private XmlNodeList _xmljobs; 

    public Queue(ServerConnection srvConn) 
    { 
     //fetch the Queue xml 
     m_parent = srvConn; 
     xmld = new XmlDocument(); 
     _jobs = new List<Job>(); 
    } 

    public void update() 
    { 
     updateXml(); 
     updateQueue(); 
     updateJobs(); 
    } 

    private void updateXml() 
    { 
     //Loads xml file into xmld 
    } 

    private void updateQueue() 
    { 
     XmlNodeList elements = xmld.SelectNodes("queue"); 

     foreach (XmlNode element in elements) 
     { 
      _status = element.SelectSingleNode("status").InnerText; 
      _eta = element.SelectSingleNode("eta").InnerText; 
     } 
    } 

    private void updateJobs() 
    { 
     _xmljobs = xmld.SelectNodes("queue/job"); 
     jobs.Clear(); 

     foreach (XmlNode xmljob in _xmljobs) 
     { 
      Job t_job; 

      _status = xmljob.SelectSingleNode("status").InnerText; 
      _eta = xmljob.SelectSingleNode("eta").InnerText; 

      //Create temp job to match against list. 
      t_job = new Job(_status, _eta); 
      jobs.Add(t_job); 
     } 
    } 

工作類:在現實中擁有約30種不同的值,但他們都以相同的格式:

public class Job 
{ 
    private int _status; 
    public int status { get { return _status; } set { _status = value; } } 
    private string _eta; 
    public string eta { get { return _eta; } set { _eta = value; } } 


    public Job(string status, string eta) 
    { 
     _status = status; 
     _eta = eta; 
    } 
} 

交互時與DataGridView我得到錯誤:

以下異常發生在DataGridView中:

System.IndexOutOfRangeException:索引沒有值。 在System.Windows.Forms.CurrencyManager.get_Item(的Int32索引) 在System.Windows.Forms.DataGridViewDataConnection.GetError(的Int32 boundColumnIndex,的Int32 columnIndex,的Int32的rowIndex)

並進入調試程序時,它的觸發初始Application.Run(new frmMain()。我在做什麼錯了?程序仍然正常運行和更新,但我甚至無法處理該事件以抑制默認錯誤消息!

編輯 - 答覆! 取而代之清除列表並重新創建它,只需更新其中的值就可以更好地工作。目前,我有此代碼:

  t_job = _jobs.FirstOrDefault(c => c.nzo_id == t_nzo_id); 
      if (t_job == null) //Job not in list, insert 
      { 
       t_job = new Job(t_status, i_index, t_eta, i_timeLeft, t_age, i_mbleft, i_mb, t_filename, i_priority, t_category, i_percentage, t_nzo_id, this); 
       jobs.Add(t_job); 
      } 
      else //update object in current list 
      { 
       jobs[t_job.Index].status = t_status; 
       jobs[t_job.Index].priority = i_priority; 
       jobs[t_job.Index].category = t_category; 
       jobs[t_job.Index].percentage = i_percentage; 
       jobs[t_job.Index].timeleft = i_timeLeft; 
       jobs[t_job.Index].mbleft = i_mbleft; 
      } 

這可以防止它!

+0

它看起來是有約束力的問題,在一個Job類的屬性。 datagridview列是如何定義的,它是否設置爲自動生成,或者您是否明確設置了列及其綁定。另外,請您可以添加Job類以及這是綁定出錯的地方 – 2011-06-02 10:09:44

+0

將該類添加到OP中,我認爲這也是一個約束性問題。我自動生成列,因爲我認爲當我經歷初始測試時,它會更容易。有沒有什麼特別的方法可以解決這個問題?最終,我想實現一種方式讓用戶指定可見的colums和他們的順序....但現在,顯示數據會很好:-P – HeWhoWas 2011-06-02 10:21:20

+0

有沒有簡單的方法讓我告訴你什麼是錯的,把以下行上的斷點jobSource.DataSource = sabCom.queue.jobs;然後瀏覽sabCom.queue.jobs中的所有對象,並查看它們是否爲null,如果有任何爲空的應該是您的問題,但回報並且我們可以看看 – 2011-06-02 10:42:11

回答

1

問題似乎是,當用戶滾動或嘗試訪問數據字段時,刷新間隔爲1秒,數據不斷被刪除和讀取。 這導致綁定到調用嘗試調用它認爲可能仍然存在的值,但不是,這就是爲什麼你得到的索引超出範圍異常

我會建議做的第一件事是你的updateJobs方法以及像隊列一樣刷新的所有其他列表,而不是每次清除列表首先​​檢查當前作業列表中是否存在來自xml的作業,如果存在,則可以更改當前值如果值已經改變,否則不做任何事情。

要檢查作業存在,它是那麼容易,因爲:

t_job = _jobs.FirstOrDefault(c => c.Filename == t_filename); 

這將返回NULL,如果作業不存在,我會假設文件名可能不是唯一的,所以可能要改變它這樣它確實是獨一無二的,即

t_job = _jobs.FirstOrDefault(c => c.Filename == t_filename && c.nzo_id == t_nzo_id); 

一件事,你現在必須滿足的是,原來的工作不會被自動刪除,所以無論何時添加了新的歷史任務,首先查看是否存在在隊列中,然後在將其添加到歷史記錄列表中之前將其刪除。

因此改變你的屬性是:

public int Index 
     { 
      get { return _index; } 
      set 
      { 
       if (_index != value) 
        _index = value; 
      } 
     } 

代替:

public int Index { get { return _index; } set { _index = value; } } 

的另一件事是,嘗試捕捉異常是昂貴的,所以不是具有:

try { i_percentage = double.Parse(t_percentage); } catch { } 

,因爲你知道如果有一個值是雙倍的,你可以將它改爲:

if (!string.IsNullOrEmpty(t_percentage)) 
    i_percentage = double.Parse(t_percentage); 

現在,如果你不知道的話,t_percentage值將是雙重你可以使用try-解析:

if (!string.IsNullOrEmpty(t_percentage)) 
    double.TryParse(t_percentage,out i_percentage); 

這樣你避免因異常的開銷。這可能是微觀優化,並不總是必要的,如果它實際上不會造成問題,但考慮到您可以有數百個作業,每個作業有10個左右的屬性刷新每秒,事實上可以明顯慢得多,即使2這10個屬性會引發異常。

還有一件事,在您的背景工作完成後,在方法中:dgvQueue_Update()您打電話給ResetBindings(true);,這會導致您的整個數據網格刷新而不是單個項目。

嘗試將其更改爲:

for (int i = 0; i < jobSource.List.Count; i++) 
    jobSource.ResetItem(i); 

的區別是:

this.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, itemIndex)); 

相比,當你說ResetBindings到:

this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); 
+0

我一直在進行徹底的測試,它似乎已經解決了這個問題。我把所有的try/catch塊都改成了你推薦的,並且它一夜之間沒有發生任何錯誤:-) – HeWhoWas 2011-06-05 01:22:22

+0

很酷的男人,祝你好運! – 2011-06-05 07:54:17