将DataGridView绑定到DataSource - 引发CurrencyError IndexOutOfRangeException

时间:2011-06-02 09:48:32

标签: c# datagridview currencymanager

我已经困扰了这一天了几天,它让我受到了很大的打击,但老实说我并不是那么经验,而且我遇到了DataGridView的麻烦 - 这似乎是一个常见的话题。

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上触发(新的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;
            } 

这可以防止它!

1 个答案:

答案 0 :(得分:1)

问题似乎是刷新间隔为1秒,当用户滚动或尝试访问数据字段时,数据会不断被删除和读取。 这导致绑定调用try调用它认为可能仍然存在的值,但不是,这就是为什么你让索引超出范围异常

我建议做的第一件事是你的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中的值是否为double,您可以使用try-parse:

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));