我正在处理服务器-客户端应用程序。问题与WindowsFroms客户端异常有关,我不知道为什么会发生。
上下文:服务器存储车道警报。每个通道都有一个laneID,每个通道可以“生成”更多警报。每个警报都有唯一的ID(GUID)。通常,客户端仅从服务器请求警报的向导,并且仅完全获取客户端不存在的那些警报。
我想有效地跟踪DataGridView控件中客户端的服务器警报。但是有时候当我试图从DataGridView中删除一行时,我遇到了'System.ArgumentOutOfRangeException',但我不知道为什么会这样,因为我在那之前找到了该行的那个元素。
我只共享客户端代码。第一个是计时器滴答事件处理程序方法。简而言之,这从用户收集了LaneID,然后从服务器请求属于给定通道之一的所有Guid。将我们的指南与新指南进行比较,并请求新警报。最后,将每个元素添加到控件行。
Task task;
private ConcurrentDictionary<string, string> alarmGuidLaneIdDict;
//Windows.Forms.Timer's Tick method with 300 msec interval
private void AlarmRequestTimer_Tick(object sender, EventArgs e)
{
//collecting the id-s
List<string> laneids = new List<string>();
foreach (LaneView item in layoutPanel.Controls)
{
if (item.laneInfo != null)
{
laneids.Add(item.laneInfo.Id);
}
}
if (task == null || task.IsCompleted)
{
task = new Task(() =>
{
//should not do anything if no id present
if (laneids.Count < 1)
return;
//getting all existing guids from server for given id-s
ConcurrentDictionary<string, string> allGuids = Program.webClient.GetAllAlarmGuids(laneids);
//should select only guids which I dont own
ConcurrentDictionary<string, string> requiredAlarms = new ConcurrentDictionary<string, string>();
//check each of my guids whether it exists in the collection returned by the server
foreach (var item in alarmGuidLaneIdDict)
{
if (!allGuids.ContainsKey(item.Key))
{
//if not exists at serverside then I remove it from my collection
alarmGuidLaneIdDict.TryRemove(item.Key, out string tempLaneId);
Debug.WriteLine("Item removed from list, because it is not present on the server.");
//now lets find the given nonexisting element in my DataGridWiew
int rowIdx = 0; int foundIdx = -1; bool found = false;
while (rowIdx < myDataGridView.RowCount && !found)
{
if (found = (myDataGridView.Rows[rowIdx].Cells[6].Value as NetworkChannel.HttpMessages.FullSpeedInfo).guid == item.Key)
{
foundIdx = rowIdx;
}
rowIdx++;
}
if (found)
{
myDataGridView.BeginInvoke(new MethodInvoker(() =>
{
//sometimes argument out of range exception, eg foundIdx = 2 when myDataGridView.Rows.Count = 2
myDataGridView.Rows.RemoveAt(foundIdx);
}));
}
}
}
//for each element that exists at serverside but not at clientside
foreach (var responseAlarm in allGuids)
{
if (!alarmGuidLaneIdDict.ContainsKey(responseAlarm.Key))
{
requiredAlarms.TryAdd(responseAlarm.Key, responseAlarm.Value);
alarmGuidLaneIdDict.TryAdd(responseAlarm.Key, responseAlarm.Value);
Debug.WriteLine("Item added: " + responseAlarm.Key);
}
}
//completely getting only elements which exists at serverside
RefreshAlarms(Program.webClient.GetAlarms(requiredAlarms));
});
task.Start();
}
}
和其他方法:
private void RefreshAlarms(List<NetworkChannel.HttpMessages.FullSpeedInfo> alarms)
{
if (myDataGridView.InvokeRequired)
{
myDataGridView.BeginInvoke(new MethodInvoker(() => { RefreshAlarms(alarms); }));
}
else
{
foreach (NetworkChannel.HttpMessages.FullSpeedInfo alarm in alarms)
{
//do nothing yet
OnSpeedingEvent(alarm);
}
if (alarms == null)
return;
//adding each new alarm to the DataGridView, the last cell is the object itself
for (int i = 0; i < alarms.Count; i++)
{
if (alarms[i].alarmInfo == null || string.IsNullOrEmpty(alarms[i].guid))
continue;
myDataGridView.Rows.Add("Play", alarms[i].guid, alarms[i].timeStamp.ToString(), ((int)alarms[i].speed).ToString() + " km/h", alarms[i].laneInfo.location, alarms[i].GetPriority(), alarms[i]);
int rowCount = myDataGridView.Rows.Count;
if (alarms[i].GetPriority() == "Significant")
myDataGridView.Rows[rowCount - 1].DefaultCellStyle.BackColor = Color.Orange;
else if (alarms[i].GetPriority() == "Important")
myDataGridView.Rows[rowCount - 1].DefaultCellStyle.BackColor = Color.OrangeRed;
else if (alarms[i].GetPriority() == "Warning")
myDataGridView.Rows[rowCount - 1].DefaultCellStyle.BackColor = Color.Yellow;
}
}
}
我已经尝试清除每个刻度中的整个DataGridView,然后立即添加每个警报,但是随后闪烁并重置滚动条,使其无用。
任何帮助我解决异常的建议或使DataGridView保持最新状态的其他概念都将受到赞赏。
其他信息:警报对象包含图像数据。服务器中存储的对象数量各不相同,可以每分钟增加+5新警报到+100警报/分钟,几乎是随机的。客户端可以从DataGridView中删除任何选定的项目。
答案 0 :(得分:0)
在使用AUDIT
然后使用Task.Start
删除项目时,这些控件上有许多多个线程在运行,这也使用了单独的线程。
因此,很难判断代码中所发生的事情是否将按预期的顺序发生。您可能会在第3个位置找到要删除的项目,但是在删除之前,另一个项目也已删除,现在您要删除的项目在第2个位置,第3个位置没有任何内容,这将为您提供{ {1}}。
当发生此类线程问题时,它们不一致发生的情况非常普遍。这可以解释为什么错误有时“发生”。
解决此问题的一种方法是确保您不会同时做这三件事:
您可以使用BeginInvoke
解决此问题,以确保一次仅执行其中一项操作。
此外,与其在发现要删除的行时删除行,不如建立行列表,并在完成后立即删除所有行,请注意始终以降序删除它们。否则,如果您尝试删除第10行,然后删除第11行,则在删除第10行之后,现在位于11的行现在位于10。如果删除11,则删除的是位于12的行。否则就没有11并且您会再次得到例外。
答案 1 :(得分:0)
增加“刻度”间隔并没有帮助我。但, 我找到了异常的确切原因和解决方案。 当我打电话
myDataGridView.BeginInvoke(new MethodInvoker(() =>
{
myDataGridView.Rows.RemoveAt(foundIdx);
}));
多次,则它们的执行顺序和时间不确定,因为它们是异步调用。在将myDataGridView.BeginInvoke(...)替换为myDataGridView.Invoke(...)之后,不再发生异常。
感谢您指出正确的方向:)