我遇到了一个树视图的问题,我试图添加一个基于Node.Text索引的子节点(我也尝试过这个基于int索引 - 无济于事)。这在同步运行时非常有效。但是,我运行完全相同的事情Async(backgroundWorker)它抛出一个未处理的ArgumentOutOfRange异常。另一个奇怪的部分是我试图在两个不同的领域中捕获这个例外。见代码:
using (Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
{
int x = 0;
foreach (string subkey_name in key.GetSubKeyNames())
{
using (RegistryKey subkey = key.OpenSubKey(subkey_name))
{
foreach (string a in (string[])subkey.GetValue("Users", ""))
{
User u = new User(a);
usrs.addUser(new User(a));
wgs.addUserToWorkgroup(subkey_name, a);
usrs.AddWorkGroupToUser(subkey_name, a);
int trycount = 0;
TryAgain:
try
{
//here is where the exception occurs
ExecuteSecure(() => treeView1.Nodes[subkey_name].Nodes.Add(a, a));
}
catch (ArgumentOutOfRangeException)//This does not catch it.
{
trycount++;
if (trycount < 100)
{
goto TryAgain; //b/c I cannot catch it this never happens...
}
}
}
}
x++;
//System.Threading.Thread.Sleep(2);
//As you can see I've tried to let the tread sleep to resolve this
//- it will get a little farther but still eventually bomb out.
}
}
这是ExecuteSecure代码(https://stackoverflow.com/a/8021020/1387186)
private void ExecuteSecure(Action a)
{
o = new object();
try
{
if (InvokeRequired)
{
lock (o)
{
BeginInvoke(a);
}
}
else
a();
}
catch (Exception) //again **sigh** this does not catch the error
{ }
答案 0 :(得分:2)
你有几个问题。
Invoke
的新解决方案会导致UI和工作线程来回打乒乓,因此您的工作线程现在变得毫无用处。在这里监视我的答案的任何人都知道我对这些编组技术的感受(如Invoke
)。它可能(通常是)更新UI的最差方法。事实上,它已经过度使用,已经达到了我认为它可以被视为cargo cult programming形式的程度。
您应该做的是让您的工作线程将更新TreeView
所需的数据发布到队列中,然后让您的UI线程通过System.Windows.Forms.Timer
以最适合的时间间隔将其拉出来您。这就是它的样子。
public class YourForm : Form
{
private sealed class YourData
{
public string SubKeyName { get; set; }
public string Value { get; set; }
}
private ConcurrentQueue<YourData> queue = new ConcurrentQueue<YourData>();
private void StartTreeViewUpdate_Click(object sender, EventArgs args)
{
Task.Factory.StartNew(WorkerThread);
TreeViewUpdateTimer.Enabled = true;
}
private void TreeViewUpdateTimer_Tick(object sender, EventArgs args)
{
// Update in batches of 100 (or whatever) so that the UI stays
// responsive.
treeView1.BeginUpdate();
for (int i = 0; i < 100; i++)
{
YourData value = null;
if (queue.TryDequeue(value) && value != null)
{
treeView1.Nodes[value.SubKeyName].Nodes.Add(value.Value, value.Value)
}
else
{
// We're done.
TreeViewUpdateTimer.Enabled = false;
break;
}
}
treeView1.EndUpdate();
}
private void WorkerThread()
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
{
foreach (string subkey_name in key.GetSubKeyNames())
{
using (RegistryKey subkey = key.OpenSubKey(subkey_name))
{
foreach (string a in (string[])subkey.GetValue("Users", ""))
{
User u = new User(a);
usrs.addUser(new User(a));
wgs.addUserToWorkgroup(subkey_name, a);
usrs.AddWorkGroupToUser(subkey_name, a);
var data = new YourData();
data.SubKeyName = subkey_name;
data.Value = a;
queue.Enqueue(data);
}
queue.Enqueue(null); // Indicate that queueing is done.
}
}
}
}
}
答案 1 :(得分:0)
如果评论很明显,请执行以下操作:
var uncapturedSubKey_name = subkey_name;
ExecuteSecure(() => treeView1.Nodes[uncapturedSubKey_name].Nodes.Add(a, a));
lambda正在捕获另一个变量,并且在循环完成之后才会执行(即最后一个subkey_name ...
答案 2 :(得分:0)
我能够解决这个问题:
通过将BeginInvoke()更改为Invoke() 我也改变了O的范围,所以它确实锁定了。
private void ExecuteSecure(Action a)
{
try
{
if (InvokeRequired)
{
lock (o)
{
Invoke(a);
}
}
else
a();
}
catch (Exception) //again **sigh** this does not catch the error
{ }