1

几天来我一直在为这个问题感到困惑,这让我很受打击,但老实说,我还没有那么有经验,而且我在使用 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 index) 在 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;
            } 

这可以防止它!

4

1 回答 1

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 catch 异常是昂贵的,所以不要:

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

因为您知道如果有一个值它将是一个双精度值,您可以将其更改为:

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

现在,如果您不知道 t_percentage 中的值是否为双精度值,您可以使用 try-parse:

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

这样可以避免异常引起的开销。这可能是微优化,如果它实际上不会导致问题,并不总是必要的,但考虑到您可以有数百个作业,每个作业每秒刷新大约 10 个属性,即使是其中的 2 个,事情实际上也会变得明显变慢10 个属性引发异常。

还有一件事,在您的后台工作人员完成后,在方法中:dgvQueue_Update()您调用ResetBindings(true);this 会导致您的整个数据网格刷新,而不仅仅是单个项目。

尝试将其更改为:

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));
于 2011-06-04T15:15:52.150 回答