Slow TreeNode with CheckBoxes [large quantity]
문제
Tree View control's AfterCheck event checks all child nodes below it and enables the Run button if something is checked.
1346 void TreeNode_AfterCheck(object sender, TreeViewEventArgs e) {
1347 if (!e.Node.Checked) return;
1348 foreach (TreeNode sub in e.Node.Nodes) {
1349 sub.Checked = e.Node.Checked;
1350 }
1351 RunButton.Enabled = IsANodeChecked();
1352 }
1429 static bool IsANodeChecked(TreeNode node) {
1430 if (node.Checked) return true;
1431 foreach (TreeNode sub in node.Nodes) {
1432 if (IsANodeChecked(sub)) {
1433 return true;
1434 }
1435 }
1436 return false;
1437 }
Checking the root node when there are 4881 sub nodes will hang the GUI for about 7 seconds.
I only need to call IsANodeChecked (on Line 1351) once, but I don't know how to disable it until after all of the tree nodes have been processed.
And I do not want to have a timer on my form devoted to monitoring this.
Does anyone see a simple/obvious solution?
해결책 3
These ideas where helpful, but I used something different that worked by adding a single boolean variable:
bool _treeNodeFirst = false;
...and a Before Checked event that temporarily modifies the Back Color on the control to serve as a flag for the control that started the chain of events:
1273 void TreeNode_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
1274 if (!_treeNodeFirst) {
1275 _treeNodeFirst = true;
1276 e.Node.BackColor = Color.Silver;
1277 }
1278 }
1346 void TreeNode_AfterCheck(object sender, TreeViewEventArgs e) {
1347 if (e.Node.Checked) {
1348 foreach (TreeNode sub in e.Node.Nodes) {
1349 sub.Checked = e.Node.Checked;
1350 }
1351 }
1352 if (e.Node.BackColor == Color.Silver) {
1353 e.Node.BackColor = Color.Empty;
1354 RunButton.Enabled = IsANodeChecked();
1355 _treeNodeFirst = false;
1356 }
1357 }
1429 static bool IsANodeChecked(TreeNode node) {
1430 if (node.Checked) return true;
1431 foreach (TreeNode sub in node.Nodes) {
1432 if (IsANodeChecked(sub)) {
1433 return true;
1434 }
1435 }
1436 return false;
1437 }
This seems to be the best way (that I can see right now) to ensure that IsANodeChecked(TreeNode)
is only run once when a group of nodes is selected all at once.
I do, however, really like Jimmy Hoffa's idea of using a count, though. I will probably add that to my code.
Thanks to all! ~Joe
다른 팁
Put an event handler on your checkboxes that enables or disables the RunButton as opposed to having something that iterates over the whole thing to find out.
Add the checkbox to a list of checked checkboxes when it get's checked first so you don't disable the RunButton until the list of checked checkboxes is empty. Remove it from the list when it's unchecked, etc.
Here's kind of how I would write it out, this is just winging it so sorry if I miss something:
private int _checkedCheckboxes;
void AddCheckBox()
{
if (_checkedCheckBoxes++ == 1) RunButton.Enabled = true;
}
void RemoveCheckBox()
{
if (_checkedCheckBoxes-- == 0) RunButton.Enabled = false;
}
void TreeNode_AfterCheck(object sender, TreeViewEventArgs e)
{
if (e.Node.Checked)
{
AddCheckBox();
return;
}
RemoveCheckBox();
}
I sometimes use a Timer
to handle such cases. Add a timer and set up the Tick
event handler to call IsANodeChecked
and enable/disable the button. Give it a short interval (~100 ms perhaps), and leave it disabled. Then, you call Stop
followed by Start
on the timer in your AfterCheck
event handler. This will cause the timer to be restarted for each call to AfterCheck
, but the Tick
event handler will be invoked only when a certain time has elapsed after the Start
call, which means that it will not be invoked until after the last call to AfterCheck
.
100 ms is a very long time for the computer to work, but will seem immediate for the user.
You can see similar behavior in the Windows Explorer. If you use the keyboard to quickly navigate around in the folder tree, the right hand pane with the folder contents will not update unless you stay on a folder in the tree for a brief moment.