 |
|
|
 |
|
 |
hello!
I felt free to write a three-state-Treeview, because I needed one.
So - in a way - its the (real late) release of this article part 2
You may take a look at Three State Treeview - Part 2[^]
|
|
|
|
 |
|
 |
Hi,
First of all thanks for the code ... its really helpful. Next I have a couple of questions.
1) How do you know which checkbox ( or boxes are checked ) at a particular time?
2) I created the nodes from entries in my database at form load event. But when I check (or Uncheck) the nodes, the name I have assigned to the nodes changes to checked ( or unchecked), I dont want to change the name of the nodes on check box click or unclick events. How can I do that ?
Thanks once again.
Saania
|
|
|
|
 |
|
 |
Its good article...
how can i use it in MFC?
|
|
|
|
 |
|
|
 |
|
 |
Where you able to find a solutions for this?
Thanks in advance!
Edgar
1
|
|
|
|
 |
|
 |
Is there a copy of this converted to vb.net?
|
|
|
|
 |
|
 |
Thanks for the nice job. I've tested your component and it works fine.
Just wondering... how do you plan to do part 2? I mean... how do you plan to get into the paint event and change how the checkbox is being painted?
The reason I'm asking is because since you don't have the time I'm planning to implement it myself for my own use until you can show up with your solution.
Thanks again,
Alexandre.
-- modified at 12:57 Friday 25th August, 2006
|
|
|
|
 |
|
 |
Alexandre,
Thanks looking at this article.
I did not have anything concrete in place with regards as to how I was going to do the painting. I had done some experimentation with it at the time this article was written, but I was having some trouble with the painting, i.e. way too much flickering.
My time is looking even more booked with work these days, so, Part 2 is looking even farther off than I had promised. A few new articles have popped up regarding painting and TreeView controls, an excellent one is on CodeProject. I was planning on using those projects as references (giving them complete credit for pointing me in the right direction for any help they may provide) to help with getting the painting done in an acceptable manner.
If you find your solution to work well for you, please write an article on you solution.
Thanks,
-ben
|
|
|
|
 |
|
 |
When will part 2 be available ?
|
|
|
|
 |
|
 |
I am hoping by the beginning of September or at the latest October 1st. I started a new job recently and have not had the extra time to devote to completing the solution.
-ben
|
|
|
|
 |
|
 |
Have you had a chance yet to tackle Part 2?
This control is exactly what I need for one of my current projects.
Thanks for part A.
|
|
|
|
 |
|
|
 |
|
 |
I replaced the ThreeStateTreeView code with the snippet below, and it worked for me allowing me to draw nodes in the Indeterminate state. I'm not using the automatic state updating code demonstarted in ThreeStateTreeNode, so I don't have the flashing problem. The code that draws the indeterminate checkbox isn't independent of platform or theme. If someone knows a better way to do this, please let me know. using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace WindowsApplication1 { public class ThreeStateTreeView : TreeView { private const int WM_PAINT = 0x000F; private Bitmap m_bmpChecked; private Bitmap m_bmpIndterminate; private Bitmap m_bmpUnchecked; public ThreeStateTreeView() : base() { Size szBox; Point ptCheck; Graphics g; #if false szBox = new Size (11, 11); #else szBox = new Size (9, 9); #endif ptCheck = new Point((szBox.Width - 7) / 2, (szBox.Height - 7) / 2); m_bmpChecked = new Bitmap(szBox.Width, szBox.Height); m_bmpIndterminate = new Bitmap(szBox.Width, szBox.Height); m_bmpUnchecked = new Bitmap(szBox.Width, szBox.Height); g = Graphics.FromImage(m_bmpChecked); DrawCheck(g, SystemColors.ControlText, ptCheck); g.Dispose(); g = Graphics.FromImage(m_bmpIndterminate); g.FillRectangle(new HatchBrush(HatchStyle.Percent50, SystemColors.Control, BackColor), new Rectangle(new Point(0,0), szBox)); DrawCheck(g, SystemColors.ControlDark, ptCheck); g.Dispose(); g = Graphics.FromImage(m_bmpUnchecked); g.Clear(BackColor); g.Dispose(); } protected void DrawCheck(Graphics g, Color col, Point ptCheck) { Pen pen = new Pen(col); g.DrawLine (pen, ptCheck.X + 0, ptCheck.Y + 2, ptCheck.X + 0, ptCheck.Y + 4); g.DrawLine (pen, ptCheck.X + 1, ptCheck.Y + 3, ptCheck.X + 1, ptCheck.Y + 5); for (int i = 0; i < 5; i++) g.DrawLine(pen, ptCheck.X + 2 + i, ptCheck.Y + 4 - i, ptCheck.X + 2 + i, ptCheck.Y + 6 - i); } protected override void DefWndProc(ref Message m) { base.DefWndProc (ref m); if (m.Msg == WM_PAINT) { int Y = 0; Graphics g = CreateGraphics(); if (Nodes.Count > 0) PaintNodes(Nodes[0], g, 0, ref Y); g.Dispose(); } } protected void PaintNodes(TreeNode node, Graphics g, int x, ref int y) { if (node is ThreeStateTreeNode) { ThreeStateTreeNode nodeThree = node as ThreeStateTreeNode; Point ptTL = new Point(x + Indent + 4, y + 4); switch (nodeThree.CheckState) { case CheckState.Checked: g.DrawImageUnscaled(m_bmpChecked, ptTL); break; case CheckState.Indeterminate: g.DrawImageUnscaled(m_bmpIndterminate, ptTL); break; case CheckState.Unchecked: g.DrawImageUnscaled(m_bmpUnchecked, ptTL); break; } } if (node.IsExpanded) { y += ItemHeight; PaintNodes(node.FirstNode, g, x + Indent, ref y); } if (node.NextNode != null) { y += ItemHeight; PaintNodes(node.NextNode, g, x, ref y); } } } }
|
|
|
|
 |
|
 |
I got your code change to show the indeterminate check state. Though, I just drew the bitmap for the indeterminate state and not the others.
The problem that I am now running into is that the bitmaps are not redrawn when scrolling the treeview. I'm not sure how to resolve that.
|
|
|
|
 |
|
 |
Instead of using
Point ptTL = new Point(x + Indent + 4, y + 4);
try
Point ptTL = new Point(node.Bounds.X - Indent +7, node.Bounds.Y + 4);
you'll get the relative coordinate of the check box
For me it works...
Ciao
Carlo
|
|
|
|
 |
|
 |
This isn't a good solution. First, you redraw all nodes icons each time repaint is needed. Second, you haven't completely solved the flickering problem, because check icons are drawn twice: once by standard handler and once by you.
Here is a better solution:
1. Set CheckBoxes to false
2. Set StateImages to an imagelist with 3 images. The 3rd picture would mean Indeterminate.
3. Write MouseClick event handler to change the TreeNode.StateImageIndex when the check image is clicked.
In the step "3." use treeView1.GetNodeAt(e.Location) to find the clicked node.
The only problem is to determine X coordinate of checkbox. You can do it in the same manner: the X coordinate of check image is 3 + (number of parent nodes)*Indent. Note#1 - add 1 to the number of parent nodes if ShowRootLines is true. Note#2 - with big fonts (in 120DPI mode) this formula may be inaccurate.
If the check box was clicked, call BeforeCheck, change the state and call AfterCheck.
And don't forget that you should also call those events and change the state when space key is pressed.
Unfortunately, .NET framework is poor compared to Win32 API. In Win API you wouldn't have to calculate X coordinate of check box. Instead you would simply post WM_HITTEST message and get all information you want.
|
|
|
|
 |
|
 |
If I run your demo sample, and expand all the nodes, and uncheck all the nodes, and then click on a leaf node, the state of the leaf node becomes checked as expected, but its parent also is set to checked.
Is this intended behavior ?
best, Bill
"The greater the social and cultural distances between people, the more magical the light that can spring from their contact." Milan Kundera in Testaments Trahis
|
|
|
|
 |
|
 |
Bill,
Thanks for looking at the demo. I follow exactly what you are doing. However, I believe what you are seeing is actually the correct behavior. Here are the steps I followed:
1 - expand all the nodes
2 - uncheck all the nodes
3 - check on a leaf node
Here are the results:
The state of the parent will be checked if that parent only has one child node. (This is what I suspect you saw)
If the parent has more than one child, then the state would be indeterminate.
Ben
-ben
|
|
|
|
 |
|
 |
Thanks, Ben,
I kept thinking there could be a data-modeling scenario in which you would want a leaf node checked that was a single leaf node, while the parent could be indeterminate or unchecked, but damn if I could come up with one
This is a very nice piece of work !
best, Bill
"The greater the social and cultural distances between people, the more magical the light that can spring from their contact." Milan Kundera in Testaments Trahis
|
|
|
|
 |
|
 |
Depending on what you want to use the treeView for, the functionality might be convinient...
However its easy added to the code provided...
Ive created a simple enum with a few ways the treeview could react...
public enum ThreeStateChangeBehavior
{
ChangeSingle = 0x00,
ChangeChilds = 0x01,
ChangeParents = 0x02,
ChangeAll = 0x03
}
Added 2 new Toggle:
public void Toggle( Enumerations.CheckBoxState fromState, ThreeStateChangeBehavior behavior )
{
switch ( fromState )
{
case Enumerations.CheckBoxState.Unchecked:
{
this.State = Enumerations.CheckBoxState.Checked;
break;
}
case Enumerations.CheckBoxState.Checked:
case Enumerations.CheckBoxState.Indeterminate:
default:
{
this.State = Enumerations.CheckBoxState.Unchecked;
break;
}
}
this.UpdateStateOfRelatedNodes( behavior );
}
public void Toggle( ThreeStateChangeBehavior behavior )
{
this.Toggle( this.State, behavior );
}
Added a new UpdateStateOfRelatedNodes
public void UpdateStateOfRelatedNodes( ThreeStateChangeBehavior behavior )
{
ThreeStateTreeView tv = this.TreeView as ThreeStateTreeView;
if ( ( tv != null ) && tv.CheckBoxes && tv.UseThreeStateCheckBoxes )
{
tv.BeginUpdate();
// If want to cascade checkbox state changes to child nodes of this node and
// if the current state is not intermediate, update the state of child nodes.
if ( this.State != Enumerations.CheckBoxState.Indeterminate )
if ( ( behavior & ThreeStateChangeBehavior.ChangeChilds ) == ThreeStateChangeBehavior.ChangeChilds )
this.UpdateChildNodeState();
if ( ( behavior & ThreeStateChangeBehavior.ChangeParents ) == ThreeStateChangeBehavior.ChangeParents )
this.UpdateParentNodeState( true );
tv.EndUpdate();
}
}
and ofc a property on the TreeViewControll it self.
private ThreeStateChangeBehavior mChangeBehavior = ThreeStateChangeBehavior.ChangeSingle;
[Category( "Three State TreeView" ),
Description( "Dertermence how the change behavior reacts when a node changes state." ),
DefaultValue( true ),
TypeConverter( typeof( ThreeStateChangeBehavior ) ),
Editor( "Ascentium.Research.Windows.Forms.Components.ThreeStateChangeBehavior", typeof( ThreeStateChangeBehavior ) )]
public ThreeStateChangeBehavior ChangeBehavior
{
get { return this.mChangeBehavior; }
set { this.mChangeBehavior = value; }
}
and in the overwritten method OnAfterCheck
call the new Toggle...
tn.Toggle(this.mChangeBehavior);
Thats about it... and you can then select how the treeview reacts to changes...
|
|
|
|
 |
|
 |
Hi,I like your control and thanks for sharing.
However in the real world I would probably load the treeview using the expand event .Suppose I have a structure Country-City-Hotel.
When I first Initialise the tree I only load countries.Click on country and load city.
Now suppose I havent load Cities yet.I check Country.Will you control handle that ,fire some sort of event and check all the cities underneath? Or How would you handle that
Thanks a lot
|
|
|
|
 |
|
 |
Hi,
Thanks for the question. I did not test the case you are describing. This demonstration made the ssumption all the nodes would be loaded prior to any user interaction. I will take a look at it though.
My "guess" (without looking into code all right now) is when loading the child nodes, their state must reflect the parent state when loaded "dynamically" as you describe...but again, I have not verified this in code..
Ben
-ben
-- modified at 1:52 Monday 5th June, 2006
|
|
|
|
 |
|
 |
I recently wrote a similar TreeView control and ran into the same issue regarding dynamic loading. It looks like a simple fix is out of reach if one doesn't want to rewrite a massive amount of code in derived classes. The problem is that there don't exist events to detect the addition or removal of nodes. Using an attempt to capture the TVM_INSERTITEMA and TVM_INSERTITEMW messages from Windows, I ran into severe ugliness when translating and manipulating the involved data structures. I prefer keyhole surgery on derrived controls, so if anyone has an elegant solution, I'd be interested in it, too )
|
|
|
|
 |