|
Great example! Is there an easy way to set the Background Image?
Tim Alvord
|
|
|
|
|
Hi,
Great job with the control! But I see one issue that no one seems to have noticed. If you run the demo project and then re-size the form, you will notice a lot of flicker in the control and also a lot of black spots showing up in place of the checkbox icons.
Could you please look into this? Also anyone out there who ran into this issue and found a fix, please reply to this post. I would greatly appreciate that!
Thanks!
Surya
|
|
|
|
|
Hello, can I use this control in a x64 OS? Or do I need to set my target platform of my project to be x86? Can I leave it as "Any CPU"? Thanks.
Regards,
TungLeh
Regards,
Cody
|
|
|
|
|
Hello!
Unfortunalety i only own one Laptop, with x32 OS, and cannot make tryals on other systems. But if you can check it out, would you please let me know your results?
Or maybe someone else can check it out?
thanx,
eckard ahlers
|
|
|
|
|
Hello,
I have tested this control on a Windows Server 2008 R2 x64 and Windows 7 x64, it works alright.
Regards,
Cody
Regards,
Cody
|
|
|
|
|
|
I haven't try this one. But I have tried the one you mentioned. It works pretty well on x86 OS, but not x64 OS (it crashes).
[Update]
I have tested this control, and it can work in x64 OS. I build it with "Any CPU" as the target, and it can run in x64 OS (if you look in the Task Manager, there is no *32 suffix for that process). The control you mentioned will crash in x64 OS. But I saw that the one you mentioned does not has the bug this control has (the double-click issue), maybe the author can check it out and tell us any difference or improve this control to fix this issue? Hehe... just my suggestion.
Regards,
Cody
modified on Monday, October 4, 2010 2:43 AM
|
|
|
|
|
Great control, any more updates about it ?? thanks.
AE
|
|
|
|
|
This works if initially all the leaf nodes are checked or unchecked. It blows up when one is trying to represent a tree view that saves state between program runs. The logic falls apart when some leaf nodes are checked and others aren't.
Here is an example change to the demonstration form class that illustrates the point:
public partial class frmThreeStateTreeview : Form
{
bool odd = true;
public frmThreeStateTreeview()
{
InitializeComponent();
threeStateTreeview1.ExpandAll();
threeStateTreeview1.AfterCheck += threeStateTreeview1_AfterCheckOrSelect;
threeStateTreeview1.AfterSelect += threeStateTreeview1_AfterCheckOrSelect;
foreach (TreeNode topNode in threeStateTreeview1.Nodes)
CheckLeafNodes(topNode);
}
void threeStateTreeview1_AfterCheckOrSelect( object sender, TreeViewEventArgs e )
{
var tv = (ThreeStateTreeview)sender;
var nd = tv.SelectedNode;
if (nd == null) return;
lbSelectedNode.Text = string.Concat(nd.Text, ": Checkstate.", tv.GetState(nd), ", Checked=", nd.Checked);
}
void CheckLeafNodes( TreeNode tn )
{
if (tn.Nodes.Count == 0)
{
tn.Checked = odd;
odd = !odd;
}
else
foreach (TreeNode child in tn.Nodes)
CheckLeafNodes(child);
}
}
|
|
|
|
|
OK, I figured it out. This all boils down to to the trick you used in ThreeStateTreeView.OnAfterCheck() with the line:
int state = nd.StateImageIndex == 0 ? -1 : 0;
This line assumes that some user is clicking on the checkbox, and causes the ThreeStateTreeview to toggle the checkbox to checked if the state was unchecked or indeterminate. If one initializes a node to node.Checked=false when the StateImageIndex is indeterminate, the checkbox toggles to checked rather than unchecked.
When initializing the node Checked states in a ThreeStateTreeview without first setting the StateImageIndex, all will show up as checked because all initially have a StateImageIndex of -1. The trick is that when nodes are added to a treeview, if the node is to be initially unchecked one must set the TreeNode.StateImageIndex to 0 for to indicate checked just before setting tn.Checked = false;
To extend my initial example above, all one needs to do is change CheckLeaveNodes() to read as follows, and everything works fine:
void CheckLeafNodes( TreeNode tn )
{
if (tn.Nodes.Count == 0)
{
tn.StateImageIndex = odd ? -1 : 0; // if setting to UNchecked, must initialize image state to checked.
tn.Checked = odd;
odd = !odd;
}
else
foreach (TreeNode child in tn.Nodes)
CheckLeafNodes(child);
}
By initially setting the tn.StateImageIndex to 0, for checked if the node is not to be initially checked, all the other logic works out. Once this initialization is done properly, the logic you implemented works great in handling user input, and for further programmatic setting of leaf nodes to .Checked = true or .Checked = false;
Considering how much less code you needed to get the job done, and how your code avoids adding a bunch of extra event and invalidate overhead, relative to other approaches I've seen for the three state checkboxes in tree views, I think you did a great job, and I'm going to use this.
People just have to know that when initializing nodes in an instance of your ThreeStateTreeview, or adding nodes, they need to set the StateImageIndex of the nodes to 0 if the node is to be unchecked initially just before setting .Checked = false. The code in your class handles everything just fine from then on. As this is all a matter of leaf nodes, and they should never have an indeterminate state, it makes sense that one would have to initialize away from indeterminate when adding a node.
Also, one needs to add the tree nodes in parents-down-to-children hierarchical order and initalize the leaf nodes after all parents have actually been added to the tree view for the check states to all work out properly back up the parent chain. There is nothing forcing one to add a parent node to the treeview before adding nodes to it in general treeview use.
modified on Monday, May 17, 2010 8:15 PM
|
|
|
|
|
Thank you very much for reporting that bug (it was a hard one). I'm very glad to see, that still people have a good look at the introduced code.
I've sent my bugfix to the editor, and hope, it will be uploaded soon. Furthermore i've mentioned you in the history, which now mixes up "Points of interest" and "credits", but see yourself:
- 1st April, 2009: Initial post
- 18.st May, 2010: Bugfix: Christo667 reported a well hidden bug, when programmatical set a nodes
Checked -property to the same value, it had before (see on Message-board). The bug-reason was, that in that case the common TreeView raises redundant Before-/After-Checked-Events, and ThreeStateTreeview toggled the nodes appearance, although it shouldn't.
Now ThreeStateTreeview suppresses those redundant Events. That may be a bug-workaround for the common TreeView as well.
Thank you, Christo!
(I hope, it's ok for you to be mentioned)
The Fix itself only took 2 lines of code in the ThreeStateTreeview-class, to suppress the redundant check-Events:
protected override void OnBeforeCheck(System.Windows.Forms.TreeViewCancelEventArgs e) {
if(_skipCheckEvents) return;
if((e.Node.StateImageIndex == 0) == e.Node.Checked) return;
base.OnBeforeCheck(e);
}
protected override void OnAfterCheck(TreeViewEventArgs e) {
if(_skipCheckEvents) return;
_skipCheckEvents = true;
try {
TreeNode nd = e.Node;
int state = nd.StateImageIndex == 0 ? -1 : 0;
if((state == 0) != nd.Checked) return;
InheritCheckstate(nd, state);
nd = nd.Parent;
while(nd != null) {
if(state != 1) {
foreach(TreeNode ndChild in nd.Nodes) {
if(ndChild.StateImageIndex != state) {
state = 1;
break;
}
}
}
AssignState(nd, state);
nd = nd.Parent;
}
base.OnAfterCheck(e);
} finally { _skipCheckEvents = false; }
}
|
|
|
|
|
Hey, Mr "Poor English", Thank You. I was able to have nice looking three state tree view checkboxes in a project in a couple hours based on your code, and including putting in my own bitmaps, and even supporting transparency (non-square checkboxes.) The other solutions I looked at were bloated, required a change of logic from regular check-box tree views, and half just plain didn't work. Yours is by far the least required code approach -- very streamlined. I just implemented proper initialization when adding nodes, and the "bug" is no issue. I'll look into grabbing your fixed code and testing it when I get a chance. Thanks again!
Since "Knoten" is German for Node or Nodes (?) I assume you are a native German speaker? Your English is a lot better than my German, that's for sure! Maybe I should change my nickname here to "HerrGrottigDeutsche?"
|
|
|
|
|
"HerrGrottigDeutsche" is wonderful. A one-word-poem. Didn't expect to meet "grottig" on this english-speaking Website at all
|
|
|
|
|
It seems to get out of consistent state on the images
|
|
|
|
|
sorry, i couldn't fix my threestatetreeview-bug yet.
Maybe it depends to an general treeview-bug dealing with doubleclicks: take a normal treeview, and doubleclick on a node-checkbox. then click the close-button of the form-window. it will not close.
|
|
|
|
|
yes, it is a bug of standard-treeview:
if you doubleclick a standard-treenode it will toggle its checkbox-display.
but it will not toggle its checked-Property!
try this with a standard-treeview:
treeView1.AfterCheck += Treeview1_AfterCheckOrSelect;
treeView1.AfterSelect += Treeview1_AfterCheckOrSelect;
void Treeview1_AfterCheckOrSelect(object sender, TreeViewEventArgs e) {
var tv = (TreeView)sender;
var nd = tv.SelectedNode;
if(nd == null) return;
lbSelectedNode.Text = string.Concat(nd.Text, ", Checked=", nd.Checked);
}
|
|
|
|
|
BTW: I love everything TreeView. If you vere girl, I would asked you for your hand, or at least nominated you for miss universe (supposing that there are no aliens out there to insult with such nomination). Next problem is still missing sixth rating-radio. I'll read this completely and leave some more serious remark
|
|
|
|
|
Yep, very nice work. BTW I'm pretty surprised that you suffice with capturing Graphics object in OnHandleCreated. I got some serious errors in another controls extensions, when trying to use Graphics captured in Layout() (I'm not sure if right in this event, but after resize it gave me errors when trying to draw). I was made to capture graphics also in OnResize. I'll try this there.
BTW2: it's shame that .NET treeview is so little friendly for extensions and customizing. I have similar experience with replacing behavior of treeview with some more friendly (almost similar to which you can see in FolderSelectDialog/s of WinAmp for example), where you must keep continual track of four integer values from WndProc in order to obtain some more information on node click/keypress event, and react optimally.
It's understandable that WebBrowser control provides almost nothing for extension or advanced usage, but for TreeView it's quite question, what is this limitation good for.
Also I hope, once there will be same ownerdraw paradise for all controls, as it already is for DataGridView (e.DrawBorder, e.DrawContent ...).
BTW3: another excellent article
|
|
|
|
|
replace
_graphics.DrawImage(_imgIndeterminate, GetCheckRect(nd).Location);
with
CheckBoxRenderer.DrawCheckBox(_graphics, GetCheckRect(nd).Location, System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal);
|
|
|
|
|
Hey, thanks for the tip, Mathias! I'd missed that and wasted time making my own bitmap for it.
-----------------------------------------------------------------------------------------------------------
This C# stuff sure seems weenie, but the compiler works and I'm sure glad to be (almost) done with COM!
|
|
|
|
|
Adding a bit of code to WndProc to trap the double click event appears to fix the double click node bug.
protected override void WndProc(ref Message m) {
const int WM_Paint = 15;
int WM_LBUTTONDBLCLK = 0x0203;
if (m.Msg == WM_LBUTTONDBLCLK)
{ return; }
base.WndProc(ref m);
if(m.Msg == WM_Paint) {
foreach(TreeNode nd in _indeterminateds) {
_graphics.DrawImage(_imgIndeterminate, GetCheckRect(nd).Location);
}
_indeterminateds.Clear();
}
}
|
|
|
|
|
suppressing the DoubleClick in general also suppresses the standard-behavior:
Doubleclicking on a treenodes text usually toggles expanded/collapsed.
but thanks for the hint - now my solution looks like:
protected override void WndProc(ref Message m) {
const int WM_Paint = 15;
const int WM_LBUTTONDBLCLK = 0x0203;
if(m.Msg == WM_LBUTTONDBLCLK) {
var lp = m.LParam.ToInt32();
var ht = this.HitTest(lp & ushort.MaxValue, lp >> 16);
if(ht.Location == TreeViewHitTestLocations.StateImage) return;
}
base.WndProc(ref m);
if(m.Msg == WM_Paint) {
foreach(TreeNode nd in _indeterminateds) {
_graphics.DrawImage(_imgIndeterminate, GetCheckRect(nd).Location);
}
_indeterminateds.Clear();
}
}
modified on Wednesday, August 31, 2011 6:21 AM
|
|
|
|
|
Excellent works except one bug. Would you please fix the bug?
Steps to reproduce:
Check a node(one of the child node).
Expand it (first time).
Now unchecked one of the child node. It would not unchecked first time.Though parents goes to indeterminate state.
Thanks.
|
|
|
|
|
sorry, i cant reproduce the bug.
I just downloaded both solutions and tried to follow your instructions, but all behaved properly.
Can you give me a instruction step by step? like "expand 'Knoten1', check 'Knoten5', expand 'Knoten5', uncheck 'Knoten14' ?
|
|
|
|
|
Its my apology that it can't be reproduce using your exact code. It may reproduce using
your code if you add tree node runtime after expand a node not during load time.
I used windows shell Item as a node. Anyway I fixed it.
Thanks.
|
|
|
|
|