|
It sure looks like a bug to me.
Allow me to provide a more complete description of the anomalous behavior.
When ShowNodeTooltips is set to true, the tooltips do indeed show, but the state icons do not. When ShowNodeTooltips is set to the node tooltips do not show and the state icons also show as expected.
Joe
|
|
|
|
|
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
namespace Pdb.BIPortal.UserAdmin.Controls.ImageTriStateTreeView
{
public enum CheckBoxState
{
None = 0,
Unchecked = 1,
Checked = 2,
Indeterminate = 3
}
public class ImageTriStateTreeView : System.Windows.Forms.TreeView
{
[StructLayout(LayoutKind.Sequential)]
private struct TVITEM
{
public int mask;
public IntPtr hItem;
public int state;
public int stateMask;
public int pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public int lParam;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINTAPI
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct TVHITTESTINFO
{
public POINTAPI pt;
public int flags;
public IntPtr hItem;
}
// Messages
private const int TV_FIRST = 4352;
private const int TVM_SETIMAGELIST = TV_FIRST + 9;
private const int TVM_GETITEM = TV_FIRST + 12;
private const int TVM_SETITEM = TV_FIRST + 13;
private const int TVM_HITTEST = TV_FIRST + 17;
// TVM_SETIMAGELIST image list kind
private const int TVSIL_STATE = 2;
//TVITEM.mask flags
private const int TVIF_STATE = 8;
private const int TVIF_HANDLE = 16;
//TVITEM.state flags
public const int TVIS_STATEIMAGEMASK = 61440;
//TVHITTESTINFO.flags flags
public const int TVHT_ONITEMSTATEICON = 64;
// ImageList Images Indexes
private const int m_IMG_CHECKBOX_NONE = 0;
private const int m_IMG_CHECKBOX_UNCHECKED = 1;
private const int m_IMG_CHECKBOX_CHECKED = 2;
private const int m_IMG_CHECKBOX_INDETERMINATE = 3;
[DllImport("user32", EntryPoint = "SendMessageA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, IntPtr lParam);
[DllImport("user32", EntryPoint = "SendMessageA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, ref TVITEM lParam);
[DllImport("user32", EntryPoint = "SendMessageA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, ref TVHITTESTINFO lParam);
public ImageTriStateTreeView(ImageList ctlStateImageList)
{
int iResult;
// Set the state image list
iResult = SendMessage(this.Handle, TVM_SETIMAGELIST, TVSIL_STATE, ctlStateImageList.Handle);
}
private void SetTreeNodeAndChildrenStateRecursively(TreeNode objTreeNode, CheckBoxState eCheckBoxState)
{
if ((objTreeNode != null)) {
SetTreeNodeState(objTreeNode, eCheckBoxState);
foreach (TreeNode objChildTreeNode in objTreeNode.Nodes)
{
SetTreeNodeAndChildrenStateRecursively(objChildTreeNode, eCheckBoxState);
}
}
}
private void SetParentTreeNodeStateRecursively(TreeNode objParentTreeNode)
{
CheckBoxState eCheckBoxState;
bool bAllChildrenChecked = true;
bool bAllChildrenUnchecked = true;
if ((objParentTreeNode != null)) {
if (GetTreeNodeState(objParentTreeNode) != CheckBoxState.None) {
foreach (TreeNode objTreeNode in objParentTreeNode.Nodes)
{
eCheckBoxState = GetTreeNodeState(objTreeNode);
switch (eCheckBoxState) {
case CheckBoxState.Checked:
bAllChildrenUnchecked = false;
break;
case CheckBoxState.Indeterminate:
bAllChildrenUnchecked = false;
bAllChildrenChecked = false;
break;
case CheckBoxState.Unchecked:
bAllChildrenChecked = false;
break;
}
if (bAllChildrenChecked == false & bAllChildrenUnchecked == false) {
// This is an optimization
break; // TODO: might not be correct. Was : Exit For
}
}
if (bAllChildrenChecked) {
SetTreeNodeState(objParentTreeNode, CheckBoxState.Checked);
}
else if (bAllChildrenUnchecked) {
SetTreeNodeState(objParentTreeNode, CheckBoxState.Unchecked);
}
else {
SetTreeNodeState(objParentTreeNode, CheckBoxState.Indeterminate);
}
// Enter in recursion
if ((objParentTreeNode.Parent != null)) {
SetParentTreeNodeStateRecursively(objParentTreeNode.Parent);
}
}
}
}
internal CheckBoxState GetTreeNodeState(TreeNode objTreeNode)
{
CheckBoxState eCheckBoxState = CheckBoxState.None;
TVITEM tTVITEM = new TVITEM();
int iState;
int iResult;
tTVITEM.mask = TVIF_HANDLE | TVIF_STATE;
tTVITEM.hItem = objTreeNode.Handle;
tTVITEM.stateMask = TVIS_STATEIMAGEMASK;
tTVITEM.state = 0;
iResult = SendMessage(this.Handle, TVM_GETITEM, 0, ref tTVITEM);
if (iResult != 0)
{
iState = tTVITEM.state;
// State image index in bits 12..15
iState = iState / 4095;
switch (iState)
{
case m_IMG_CHECKBOX_NONE:
eCheckBoxState = CheckBoxState.None;
break;
case m_IMG_CHECKBOX_UNCHECKED:
eCheckBoxState = CheckBoxState.Unchecked;
break;
case m_IMG_CHECKBOX_CHECKED:
eCheckBoxState = CheckBoxState.Checked;
break;
case m_IMG_CHECKBOX_INDETERMINATE:
eCheckBoxState = CheckBoxState.Indeterminate;
break;
}
}
return eCheckBoxState;
}
internal void SetTreeNodeState(TreeNode objTreeNode, CheckBoxState eCheckBoxState)
{
int iImageIndex=0;
TVITEM tTVITEM = new TVITEM();
int iState;
int iResult;
if ((objTreeNode != null))
{
switch (eCheckBoxState)
{
case CheckBoxState.None:
iImageIndex = m_IMG_CHECKBOX_NONE;
break;
case CheckBoxState.Unchecked:
iImageIndex = m_IMG_CHECKBOX_UNCHECKED;
break;
case CheckBoxState.Checked:
iImageIndex = m_IMG_CHECKBOX_CHECKED;
break;
case CheckBoxState.Indeterminate:
iImageIndex = m_IMG_CHECKBOX_INDETERMINATE;
break;
}
tTVITEM.mask = TVIF_HANDLE | TVIF_STATE;
tTVITEM.hItem = objTreeNode.Handle;
tTVITEM.stateMask = TVIS_STATEIMAGEMASK;
// State image index in bits 12..15
tTVITEM.state = iImageIndex * 4096;
iResult = SendMessage(this.Handle, TVM_SETITEM, 0, ref tTVITEM);
}
}
public void ToggleTreeNodeState(TreeNode objTreeNode)
{
CheckBoxState eCheckBoxState;
eCheckBoxState = GetTreeNodeState(objTreeNode);
this.BeginUpdate();
switch (eCheckBoxState)
{
case CheckBoxState.Unchecked:
SetTreeNodeAndChildrenStateRecursively(objTreeNode, CheckBoxState.Checked);
SetParentTreeNodeStateRecursively(objTreeNode.Parent);
break;
case CheckBoxState.Checked:
case CheckBoxState.Indeterminate:
SetTreeNodeAndChildrenStateRecursively(objTreeNode, CheckBoxState.Unchecked);
SetParentTreeNodeStateRecursively(objTreeNode.Parent);
break;
}
this.EndUpdate();
}
private TreeNode GetTreeNodeHitAtCheckBoxByScreenPosition(int iXScreenPos, int iYScreenPos)
{
Point objClientPoint;
TreeNode objTreeNode;
objClientPoint = this.PointToClient(new Point(iXScreenPos, iYScreenPos));
objTreeNode = GetTreeNodeHitAtCheckBoxByClientPosition(objClientPoint.X, objClientPoint.Y);
return objTreeNode;
}
private TreeNode GetTreeNodeHitAtCheckBoxByClientPosition(int iXClientPos, int iYClientPos)
{
TreeNode objTreeNode = null;
int iTreeNodeHandle;
TVHITTESTINFO tTVHITTESTINFO = new TVHITTESTINFO();
// Get the hit info
tTVHITTESTINFO.pt.x = iXClientPos;
tTVHITTESTINFO.pt.y = iYClientPos;
iTreeNodeHandle = SendMessage(this.Handle, TVM_HITTEST, 0, ref tTVHITTESTINFO);
// Check if it has clicked on an item
if (iTreeNodeHandle != 0)
{
// Check if it has clicked on the state image of the item
if ((tTVHITTESTINFO.flags & TVHT_ONITEMSTATEICON) != 0)
{
objTreeNode = TreeNode.FromHandle(this, new IntPtr(iTreeNodeHandle));
}
}
return objTreeNode;
}
protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs e)
{
TreeNode objTreeNode;
base.OnMouseUp(e);
objTreeNode = GetTreeNodeHitAtCheckBoxByClientPosition(e.X, e.Y);
if ((objTreeNode != null))
{
ToggleTreeNodeState(objTreeNode);
}
}
protected override void OnKeyUp(System.Windows.Forms.KeyEventArgs e)
{
base.OnKeyUp(e);
if (e.KeyCode == Keys.Space)
{
if ((this.SelectedNode != null))
{
ToggleTreeNodeState(this.SelectedNode);
}
}
}
protected override void OnBeforeExpand(System.Windows.Forms.TreeViewCancelEventArgs e)
{
// PATCH: if the node is being expanded by a double click at the state image, cancel it
if ((GetTreeNodeHitAtCheckBoxByScreenPosition(MousePosition.X, MousePosition.Y) != null))
{
e.Cancel = true;
}
}
protected override void OnBeforeCollapse(System.Windows.Forms.TreeViewCancelEventArgs e)
{
// PATCH: if the node is being collapsed by a double click at the state image, cancel it
if ((GetTreeNodeHitAtCheckBoxByScreenPosition(MousePosition.X, MousePosition.Y) != null))
{
e.Cancel = true;
}
}
public TreeNode AddTreeNode(TreeNodeCollection colNodes, string sNodeText, int iImageIndex, CheckBoxState eCheckBoxState)
{
TreeNode objTreeNode;
objTreeNode = new TreeNode(sNodeText);
objTreeNode.ImageIndex = iImageIndex;
objTreeNode.SelectedImageIndex = iImageIndex;
colNodes.Add(objTreeNode);
this.SetTreeNodeState(objTreeNode, eCheckBoxState);
return objTreeNode;
}
public TreeNode AddTreeNode(TreeNodeCollection colNodes, TreeNode objTreeNode, CheckBoxState eCheckBoxState)
{
colNodes.Add(objTreeNode);
this.SetTreeNodeState(objTreeNode, eCheckBoxState);
return objTreeNode;
}
/// <summary>
/// Return a list of the tag data for all of the checked items in the tree
/// </summary>
/// <returns></returns>
public ArrayList GetCheckedTagData()
{
ArrayList list = new ArrayList();
foreach (TreeNode node in Nodes)
BuildTagDataList(node, list);
return list;
}
/// <summary>
/// Gets all checked nodes as a generic list.
/// </summary>
/// <typeparam name="T">Generic type</typeparam>
/// <returns>Generic list</returns>
public List<T> GetCheckedTagData<T>()
{
List<T> list = new List<T>();
foreach (TreeNode node in Nodes)
BuildTagDataList(node, list);
return list;
}
/// <summary>
/// Gets the checked state of a node
/// </summary>
/// <param name="node">Node</param>
/// <returns>The checked state</returns>
public ArrayList GetCheckedNodes()
{
ArrayList list = new ArrayList();
foreach (TreeNode node in Nodes)
{
BuildNodeDataList(node, list);
}
return list;
}
/// <summary>
/// Build a list of all of the checked nodes in the tree.
/// </summary>
/// <param name="node"></param>
/// <param name="list"></param>
private void BuildNodeDataList(TreeNode node, ArrayList list)
{
if (GetTreeNodeState(node) == CheckBoxState.Checked)
list.Add(node);
foreach (TreeNode child in node.Nodes)
BuildNodeDataList(child, list);
}
/// <summary>
/// Build a list of all of the tag data for checked items in the tree.
/// </summary>
/// <param name="node"></param>
/// <param name="list"></param>
private void BuildTagDataList(TreeNode node, ArrayList list)
{
if (GetTreeNodeState(node) == CheckBoxState.Checked && node.Tag != null)
list.Add(node.Tag);
foreach (TreeNode child in node.Nodes)
BuildTagDataList(child, list);
}
/// <summary>
/// Build a list of all of the tag data for checked items in the tree.
/// </summary>
/// <typeparam name="T">The type of the list</typeparam>
/// <param name="node">Current node</param>
/// <param name="list">List to fill with tags</param>
private void BuildTagDataList<T>(TreeNode node, List<T> list)
{
if (GetTreeNodeState(node) == CheckBoxState.Checked && node.Tag != null)
list.Add((T)node.Tag);
foreach (TreeNode child in node.Nodes)
BuildTagDataList(child, list);
}
}
}
|
|
|
|
|
Thanks for porting this to c#!
Much Appreciated.
|
|
|
|
|
Hi, thanks for the great, helpful and needed class, but...
I have converted the vbcode into c# with SharpDeveloper. I added the converted classes FormTreeView and TriStateTreeView into a brand new c# windows application solution and replaced MainForm with FormTreeView. A few minor code modifications regarding compiler differences and the c# tree view runs perfectly as the original vb tree view. But...
Now I added the TriStateTreeView class into my project and the CheckBoxes don't show. I'm doing it exactly as in the example in FormTreeView. As far as I can tell I have tried everything. I'm not new to programming but I am new to c# and .NET. Can anyone confirm this behaviour or maybe even have a solution? I would be so very greatful!
Kind Regards
Tom
|
|
|
|
|
Hi again,
I found the couse. Nothing important.
If someone is interested:
I populate and expand my node in the nodeMouseDoubleClick event. When I double click the check box the node is populated. It dousn't get expanded because the overridden onBeforeExpand prevents it. But to prevent the node from being populated I added and override for onNodeMouseDoubleClick to do nothing in case of GetTreeNodeHitAtCheckBoxByScreenPosition.
protected override void OnNodeMouseDoubleClick(System.Windows.Forms.TreeNodeMouseClickEventArgs e) {
if ((GetTreeNodeHitAtCheckBoxByScreenPosition(MousePosition.X, MousePosition.Y) != null))
{
} else
base.OnNodeMouseDoubleClick(e);
}
Thanks again for that very nice tree view.
|
|
|
|
|
Would you be interested in posting your C# version of the code? If so, thanks very much in advance!
Kim
|
|
|
|
|
Hi,
I really like this tristate class, but I have found a number of issues that I cannot work around.
1) If you call the form containing the class more than once, the checkboxes disappear. I tried this with the sample application by simply calling the FormTreeView.vb form twice, and it happened there as well.
2) If I set up two or three levels of items and set the items at the lowest levels to be checked. Unfortunately, this does not affect FOLDER level items above them to be partially or fully checked. Is this normal?
Any ideas?
|
|
|
|
|
OK - I've managed to work around point issue above.
You need to set if to unchecked, and then toggle it. This cannot take place within the class definition for some reason, but does work in the form manipulating the control as follows:
m_ctlTriStateTreeView.SetTreeNodeState(tnNode, CheckBoxState.Unchecked)
m_ctlTriStateTreeView.ToggleTreeNodeState(tnNode)
Issue 2 still remains, and I can't understand why it should.
|
|
|
|
|
1) Is seemes to be a problem with caching / memory? i finally solved this problem by disposing the form on close:
Private Sub MyForm_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
Me.Dispose(True)
End Sub
When i open the form with TriStateTreeView once again, the checkboxes are there
I hope it helped
MadMax
|
|
|
|
|
how can i add it into a smartdevice project for pocket pc in vb.net 2003????
|
|
|
|
|
The BeforeCheck Event is never raised and So we can't Cancel any state.
|
|
|
|
|
Hi, nice work!!!
I’m newbie, and I have been trying to populate this treeview with my windows route:
C:\
C:\windows
C:\documents and settings\[user profile]\.....
C:\program files\app 01\xx.txt
Butte I don’t have success….
Is it possible to populate windows route in this treeview, and on close form get the folders / files paths from checked boxes?
Regards
Mpires – mpires@astralpool.pt
-- modified at 18:52 Monday 19th February, 2007
mpires
|
|
|
|
|
Hi,
I am using your treeview control after converting it in C#.
It worked fine for an year when I used it in .NET 2003.
Now I have shifted to .NET 2005 and I have one problem.
If I want to get the state of the checkbox, it always returns false even if it is checked.
After running it in both 2003 and 2005, I found the difference in the line
iResult = SendMessage(this.Handle, TVM_SETITEM, 0, ref tTVITEM);
in the method SetTreeNodeState(TreeNode objTreeNode, CheckBoxState eCheckBoxState).
I put a watch on the objTreeNode.Checked expression and in 2003 it turns to "true" after the code excution passes the SendMessage line. In 2005, nothing happens.
Can you help me get rid of this problem ?
Thanks in advance
Achintya
|
|
|
|
|
I also faced to this problem. I got soulution for it. The problem was 'GetCheck' method not work after converting.
There for I used 'GetCheckedNodes()' method on class. It used in my form like this
arrayList Checked = new ArrayList();
Checked = m_ctlTriStateTreeView.GetCheckedNodes();
foreach (TreeNode n in Checked)..................
|
|
|
|
|
Hi,
Again thanks for your code.It really does what I was looking for.
I have noticed that you have overriden the beforeExpand Event and collapse event.
This was to prevent expansion if doubleClick on the icon.
However if you want to load lots of nodes on demand by using the expand even you cant.Any reason why not i should not comment it out?
thanks a lot
|
|
|
|
|
Hi,thanks for your control.
I am trying to drag and drop your control from the toolbox to the form but i get an error.
It would make life a lot easier.Are you meant to drag and drop the control onto a form?
Thanks A lot
vbinfo@devnet247.com
|
|
|
|
|
Hi,
No, it is a class, not a control for the toolbox. Read the article to see the usage. You can transform it into a control if you want.
Carlos J. Quintero
|
|
|
|
|
Hi there,
Tried to transform into a control but having problem with apicall.
Basically I am included the stateImageList into the componect and tried moving the call to sendMessage to Public new but i get an error.
Any advice ?
thanks a lot
|
|
|
|
|
I need this too please need help
|
|
|
|
|
Carlos,
Great job with the control. I've been interested in something like this.
Cheers,
Paul
|
|
|
|
|
Hi,
seems your Exchange server removed files when you mailed the zip to codeproject:
"Antigen for Exchange removed CodeProjectArticle.zip->TriStateTreeview.zip->TriStateTreeView.exe since it
was found to match the FILE FILTER= *.exe file filter."
I'd like to try your control .dll, but I haven't installed VB in VS and I don't have the CDs around so I can't compile it myself... Would it be possible for you to post the zip through another server?
Thanks,
Jonas
|
|
|
|
|
Hi Jonas,
You are right. I have submitted again the zip file, I don´t know how long it will take to appear. Meantine, you can contact me through my web site www.mztools.com and I will send you the zip file directly.
Best regards,
Carlos Quintero
|
|
|
|
|
Thanks a lot,
that's just what i was looking for.
Just a little remark, when i when replace my standard treeview by your class, i forgot to remove the 'Me.TreeView.CheckBoxes = True attribute.
It has for effect that you can't uncheck the checkboxes anymore and other disfonctionnement (the parent checkboxe disapear when you double click on a node)
now it works fine
Jérôme - FRANCE
|
|
|
|
|
hello everyone:
now i have want to add a treeview control with checkboxes to my web pages,
however,there is neither treeview nor checkbox controls in my toolbox.
what can i do now ?any thing is appreciated,thanX in advance!!!!!!!!!
|
|
|
|
|
Excellent job and nice bit of coding. Unfortunatly, I needed something like this in C++.
I ended up spending the better part of a day porting this over to Managed C++, and got it to work quit nicely there as well (with the default State images internalized itto the TriStateTreeView class. The user can supply an optional set by setting a "StateImage" property after "new"ing an instance of the control.
I've seen all the ways to "fake it" using the Icon images, but was nice to finally find one that actually got ahold of the actual checkbox image and update it as necessary (which is exactly what I wanted, as I was not really impressed with any of the "fake it" versions).
The big mystery is why the heck did MS not supply something like this as part of the framework in the first place? I've seen some of thier own friggan programs make use of a tri-state type tree, such as the back-up program that came with Windows 2000.
Again, an excellent job on this. I can only imagine what a pain it must've been to figure out all the internal messaging and the like to get ahold of, as well as set, those state images and the states.
|
|
|
|
|