|
I find that in some instances the code in these overrides will cancel the event too often. I discovered that it would cancel when clicking on the plus next to a node, but not all of the time, and only when the form containing the tree is maximized. Odd behavior. Removing the overrides did not seem to have any adverse effects.
Protected Overrides Sub OnBeforeExpand(ByVal e As System.Windows.Forms.TreeViewCancelEventArgs)
' PATCH: if the node is being expanded by a double click at the state image, cancel it
If Not (GetTreeNodeHitAtCheckBoxByScreenPosition(Me.MousePosition.X, Me.MousePosition.Y) Is Nothing) Then
e.Cancel = True
End If
End Sub
Protected Overrides Sub OnBeforeCollapse(ByVal e As System.Windows.Forms.TreeViewCancelEventArgs)
' PATCH: if the node is being collapsed by a double click at the state image, cancel it
If Not (GetTreeNodeHitAtCheckBoxByScreenPosition(Me.MousePosition.X, Me.MousePosition.Y) Is Nothing) Then
e.Cancel = True
End If
End Sub
Joe
|
|
|
|
|
An Click on the checkbox don't fire the afterchange event!?
any ideas?
regards achim
|
|
|
|
|
Try using the AfterCheck and the NodeStateChange events.
Joe
|
|
|
|
|
Hello everyone.
When I call the SetTreeNodeState method, the state of the parent node does not work correctly.
For example: If I have a parent node which children are checked, but I set the state of any one of those children to unchecked (calling SetTreeNodeState) the state of the parent node does not go to Indeterminate.
I think that we has to make a call to SetParentTreeNodeStateRecursively(objTreeNode.Parent) at the end of SetTreeNodeState method.
I have made this modification and it works correctly.
Bye
|
|
|
|
|
I have converted the VB.NET version to a C# version.
While I am at it, I may as well mention a nice little code conversion tool provided by the folks at Telerik that I found on Google's iGoogle home page as a widget. Works like a champ. The only things it did not handle were the API definitions and usage instances (datatypes).
I'd like to post the C# code here but I do not see a way to do so.
Joe
|
|
|
|
|
if you set the treeview.ShowNodeToolTips = True the checkboxes disappear. Is this a bug??
|
|
|
|
|
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
|
|
|
|
|