将 DataGridView 绑定到 DataSource - 引发CurrencyError IndexOutOfRangeException
我已经为这个问题困惑了几天,它让我很沮丧,但说实话,我还没有那么有经验,而且我在使用 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: Index没有价值。 在 System.Windows.Forms.CurrencyManager.get_Item(Int32 索引) 在 System.Windows.Forms.DataGridViewDataConnection.GetError(Int32boundColumnIndex, 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;
}
这可以阻止它!
I've been puzzling over this one for a few days now and it's got me pretty beaten, but to be honest I'm not all that experienced yet and I'm having trouble with DataGridView - which seems a common topic.
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.
}
Queue Class:
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);
}
}
Job class: In reality it holds around 30 values of varying types, but they're all in the same format:
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;
}
}
When interacting with the DataGridView I get the error:
The following exception occured in the DataGridView:
System.IndexOutOfRangeException: Index does not have a value.
at System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
at System.Windows.Forms.DataGridViewDataConnection.GetError(Int32 boundColumnIndex, Int32 columnIndex, Int32 rowIndex)
And when entering the debugger it's triggered on the initial Application.Run(new frmMain(). What on earth am I doing wrong? The program still functions and updates normally but I can't even handle the event to suppress the default error message!
Edit - Answer!
Instead of clearing the list and re-creating it, just updating the values within it works better. For the moment I have this code:
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;
}
Which prevents it!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题似乎是,刷新间隔为 1 秒,当用户滚动或尝试访问数据字段时,数据会不断被删除和读取。
这会导致绑定到调用 try 调用一个它认为可能仍然存在但实际上并不存在的值,这就是为什么您会遇到索引超出范围异常的原因
我建议做的第一件事也是在您的 updateJobs 方法中与像队列列表一样刷新的所有其他列表一样,不是每次都清除列表,而是首先检查 xml 中的作业是否存在于当前作业列表中,如果存在,那么您可以更改当前值(如果值)已经改变,否则什么也不做。
要检查作业是否存在,很简单:
如果作业不存在,这将返回 null,我假设文件名可能不是唯一的,因此可能需要更改它以使其确实是唯一的,即
您会做的一件事现在需要考虑的是旧作业不会自动删除,因此每当添加新的历史作业时,首先检查队列中是否存在它,然后将其删除,然后再将其添加到历史列表中。
因此,将您的属性更改为:
而不是:
另一件事是 try catch 异常的成本很高,因此不要使用:
因为您知道如果有一个值它将是一个双精度值,您可以将其更改为:
现在如果您不知道 t_percentage 中的值是否为双精度,您可以使用 try-parse:
这样可以避免异常引起的开销。这可能是微观优化,如果它实际上不会引起问题,则并不总是必要的,但考虑到您可以有数百个作业,每个作业每秒刷新 10 个左右的属性,如果甚至有 2 个作业,事情实际上会变得明显更慢10 个属性抛出异常。
另一件事是,在后台工作程序完成后,在方法:
dgvQueue_Update()
中,您正在调用ResetBindings(true);
,这会导致整个数据网格刷新,而不仅仅是刷新个别项目。尝试将其更改为:
区别在于:
与您说 ResetBindings 时相比:
The problem seems to be that with a refresh interval of say 1 second, while the user is scrolling or trying to access the fields of the data the data is constantly being removed and readded.
This causes the binding to to call try call a value that it thinks might still be there, but isn't and that is why you are getting the index out of range exception
The first thing I would suggest doing is in your updateJobs method as well as all the other lists that are refreshed like the queue list, instead of clearing the list everytime is first checking to see if the job from the xml exists in the current job list, if it does then you can change the current values if the value has changed otherwise do nothing.
To check if the job exists it is as easy as:
This will return a null if the job does not exist, I would assume filename might not be unique, so might want to change it so that it really is unique i.e.
One thing you will now have to cater for is that old jobs won't be automatically removed, so whenever a new history job is added, first check to see if it exists in the queue and then remove it there before adding it to the history list.
so change your properties to be:
instead of:
The other thing is that try catch exceptions are expensive, so instead of having:
because you know that if there is a value it is going to be a double, you can change it to:
now if you don't know if the value in t_percentage is going to be a double you can use a try-parse:
This way you avoid the overhead caused by an exception. This might be micro-optimizing and is not always neccessary if it doesn't actually cause a problem, but given that you can have hundreds of jobs, each with 10 or so properties refreshing everysecond, things can actually get noticeably slower if even 2 of the 10 properties throws an exception.
One more thing, after your backgroundworker is completed, in the method:
dgvQueue_Update()
you are calling theResetBindings(true);
this causes your whole datagrid to refresh instead of just the individual items.try changing it to:
The difference is this:
compared to when you say ResetBindings: