Error in Treeview with Checkboxes and Vista

  • Thread starter Thread starter Dieter Pelz
  • Start date Start date
D

Dieter Pelz

Hallo,

there is an error in the TreeView control when checkboxes are enabled.
When the checkbox is double clicked the node is visibly not checked, but the
Checked property is still true;

I'm using Visual Studio 2005.
Windows Vista und Vista 64 has this error.
Windows XP works fine.

Is there any workaround for this Error?
An example project is available.

Thanks,
Dieter Pelz
 
Hi Dieter,

I performed a test based on your description and did reproduce the problem
on my side.

This problem is caused by the Vista visual styles. If you disable the
visual styles for the application, the problem doesn't exist.

FYI, to disable the visual styles for a WinForm application, if you're
using C#, open the Program.cs file and comment out the line of code
'Application.EnableVisualStyles();' in the static Main method; if you're
using VB.NET, right click the project in the Solution Explorer and choose
Properties, in the Project Designer, switch to Application tab and clear
the checkbox before the option 'Enable XP visual styles'.

So, the easiest workaround of this problem is to disable the Vista visual
styles for the application.

A more complicated workaround is to draw the TreeView by ourselves so that
we can ensure the displayed check state of the treenode is always correct.
To do this, we need to set the DrawMode of the TreeView to OwnerDrawAll and
handle the DrawNode event of the TreeView. In addition, we need to add two
bmp files to the application. One bmp file represents the plus sign used by
the TreeView and the other represents the minus sign.

The following is a sample to do this:

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.treeView1.DrawMode = TreeViewDrawMode.OwnerDrawAll;
this.treeView1.DrawNode += new
DrawTreeNodeEventHandler(treeView1_DrawNode);
}

void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
int left = e.Bounds.X;
int top = e.Bounds.Y;
int height = e.Bounds.Height;

int nodeleft = e.Node.Bounds.X;
int nodetop = e.Node.Bounds.Y;
int nodeheight = e.Node.Bounds.Height;
int nodewidth = e.Node.Bounds.Width;

int linelength =10;
int checkboxwidth = 13;
int checknodespace = 2;
using (Pen p = new Pen(Color.Gray))
{
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
int lineleft =
nodeleft-checknodespace-checkboxwidth-linelength;
//draw horizontal dot line
e.Graphics.DrawLine(p, lineleft, top + height / 2, lineleft
+ linelength, top + height / 2);
// draw the up half vertical dot line
if (e.Node.PrevNode != null || e.Node.Parent != null)
{
e.Graphics.DrawLine(p, lineleft, top, lineleft, top +
height / 2);
}
// draw the down half vertical dot line
if (e.Node.NextNode != null)
{
e.Graphics.DrawLine(p, lineleft, top + height / 2,
lineleft, e.Node.NextNode.Bounds.Top);
}
// draw plus/minus image
if (e.Node.Nodes.Count > 0)
{
if (!e.Node.IsExpanded)
{
e.Graphics.DrawImage(Properties.Resources.plus,
lineleft - Properties.Resources.plus.Width / 2, top + (height -
Properties.Resources.plus.Height) / 2);
}
else
{
e.Graphics.DrawImage(Properties.Resources.minus,
lineleft - Properties.Resources.minus.Width / 2, top + (height -
Properties.Resources.minus.Height) / 2);
}
}
// draw the vertical dot line for the parent nodes if
necessary
TreeNode parentNode = e.Node.Parent;
int parentNodeLeftDistance = checknodespace + checkboxwidth
+ linelength;
while (parentNode != null)
{
if (parentNode.NextNode != null)
{
e.Graphics.DrawLine(p, parentNode.Bounds.X -
parentNodeLeftDistance, top, parentNode.Bounds.X - parentNodeLeftDistance,
top + height);
}
parentNode = parentNode.Parent;
}
}
// draw checkbox
if (e.Node.Checked)
{
ControlPaint.DrawCheckBox(e.Graphics, new
Rectangle(nodeleft - checkboxwidth - checknodespace, top + (height -
checkboxwidth) / 2, checkboxwidth, checkboxwidth), ButtonState.Checked);
}
else
{
ControlPaint.DrawCheckBox(e.Graphics, new
Rectangle(nodeleft - checkboxwidth - checknodespace, top + (height -
checkboxwidth) / 2, checkboxwidth, checkboxwidth), ButtonState.Normal);
}
// erase the previous node text
using (Brush b = new SolidBrush(this.treeView1.BackColor))
{
e.Graphics.DrawString(e.Node.Text, this.treeView1.Font, b,
nodeleft, nodetop);
}
// draw node text and highlight rectangle
if (e.Node.IsSelected)
{
ControlPaint.DrawFocusRectangle(e.Graphics, e.Node.Bounds);

e.Graphics.FillRectangle(Brushes.LightSkyBlue, new
Rectangle(nodeleft + 1, nodetop + 1, nodewidth - 2, nodeheight - 2));
using (Brush b = new SolidBrush(Color.White))
{
e.Graphics.DrawString(e.Node.Text, this.treeView1.Font,
b, nodeleft, nodetop);
}
}
else
{
using (Brush b = new SolidBrush(this.treeView1.ForeColor))
{
e.Graphics.DrawString(e.Node.Text, this.treeView1.Font,
b, nodeleft, nodetop);
}
}
}
}

Hope this helps.
If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
Linda,

Do you possibly know how to "fix" the Vista msstyles and XP VisualStyles
incompatibility possibly? For example below in the code snippet... It's
possible now to handle both XP visual styles and pre XP styles... But with
the advent of Vista things are now not as backward compatible as it were? I
understand that it is possible using the Win32 API to possibly make the code
below look as it does in Vista; but there has to be an easier way through VS
to do this rather than the SDK? (I'm using VS2005 and 2008 Beta2).

Thanks,
Brendon

<code>
public class ComboTreeButton : Button
{
#region Fields

private ButtonState buttonState;
private ComboBoxState comboBoxState;

#endregion

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (!ComboBoxRenderer.IsSupported)
{
ControlPaint.DrawComboButton(e.Graphics,
this.ClientRectangle, buttonState );
}
else
{
ComboBoxRenderer.DrawDropDownButton(e.Graphics,
this.ClientRectangle, comboBoxState);
}
}
}
</code>
 
Hi Brendon,
Do you possibly know how to "fix" the Vista msstyles and XP VisualStyles
incompatibility possibly?

Sorry that I don't understand your question. You sample code works fine on
my Vista machine.

Could you please explain more about your question?

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Linda,

Put that code in a class and build the application - then add the control
created onto a form and put a combo box onto the same form... Now UNDER
VISTA run the application and look at the form... What do you notice
different? While the button is rendered correctly yes... It is not the
visual style of Vista... When I've done OwnerDrawn controls under XP to
ensure conformity in the UI I've used that block for example to ensure that
dependant on the user system and theme selected the controls look and feel
the same... Vista to XP and before this is not the way now...

Brendon
 
Hi Brendon,

In fact, the Vista visual style of a ComboBox drop down button has three
appearances. Initially, the drop down button only shows a black triangle
sign. If you hover the mouse over the drop down button, the drop down
button appears activated. If you click on it, it appears pressed.

You can mimic the above appearances by handling the related events of the
ComboBox and pass ComboBoxState.Hot or ComboBoxState.Pressed to the
ComboBoxRenderer.DrawDropDownButton method.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Hi Dieter,

How about your problem now?

If you have any question, please feel free to let me know.

Thank you for using our MSDN Managed Newsgroup Support Service!

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Hallo,

sorry for answering so late.

Will this error in the framework on Vista be fixed?
Because it seemed for me that the state of the W32 TreeView Item and the
..Net TreeView Item are not in sync!
I used Reflector to look inside the problem and it seemed to be that the
TreeItem will cache the state, but the state of the TreeViewItem in W32 is
not the same.

Disabling Visual Styles ist not the right solution for me because it is a
dialog inside a library that is used from an C++/CLI application.

Sincerely,
Dieter Pelz
 
Hi Dieter,

Thank you for your reply!

The problem you entered has been reproduced, but our evaluation determined
that it does not meet the criteria to be addressed in this release. This
evaluation is carefully done and considers many aspects including fix cost,
breaking changes, globalization, performance, etc.

Additionally, as part of planning and analysis of future versions this bug
will be considered again for possible inclusion.
Disabling Visual Styles ist not the right solution for me because it is a
dialog inside a library that is used from an C++/CLI application.

How about the other solution of drawing the TreeView by ourselves that I
provided in my first reply?

If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Hallo,

Linda Liu said:
Hi Dieter,

Thank you for your reply!

The problem you entered has been reproduced, but our evaluation determined
that it does not meet the criteria to be addressed in this release. This
evaluation is carefully done and considers many aspects including fix
cost,
breaking changes, globalization, performance, etc.

But this meens a WinForm App with an TreeView with Checkboxes is not usable
under Vista.
I think checkboxes will normally be used to select functions that will run
on the nodes.
In our app the checkbox will delete and change main configuration entries in
a database.
When an node is visibly not check but the state in Framework says it ist
checked the functions will run.
This makes TreeView with Checkboxes in every WinForm application not usable.

This brings me to another question:
Does this means error in the .Net Framework will not be fixed because of the
costs?
The error will not affect breaking changes and globalization aspects.
Performance could be affected because an TreeViewItem State must be asked
when a mouse button is pressed.

Additionally, as part of planning and analysis of future versions this bug
will be considered again for possible inclusion.

dialog inside a library that is used from an C++/CLI application.

How about the other solution of drawing the TreeView by ourselves that I
provided in my first reply?

ImageLists are not attended and the look is not the same as in Vista.
What must be change to get the same look as in Vista?
What about Visual Styles?
What about the property HideSelection?

I could send you an image from a test app and an test Project.

I adopted your code in a new class TreeViewVista inherited from TreeView.
On the left side I used the new class TreeViewVista.
On the right side I used the original class TreeView.
Under every control there are labels. They will be filled with the state of
the first related TreeNode.


Here is the Code of my TreeViewVista class:
Where will I get the original bitmaps for plus and minus?
What must be added to get the same look as the original TreeView?

File: TreeViewErrorVista.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace TreeViewErrorVista
{
public class TreeViewVista : TreeView
{
public TreeViewVista()
{
if (Environment.OSVersion.Version.Major >= 6)
{
DrawMode = TreeViewDrawMode.OwnerDrawAll;
DrawNode += new DrawTreeNodeEventHandler(treeView1_DrawNode);
}
}



private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
int left = e.Bounds.X;
int top = e.Bounds.Y;
int height = e.Bounds.Height;

int nodeleft = e.Node.Bounds.X;
int nodetop = e.Node.Bounds.Y;
int nodeheight = e.Node.Bounds.Height;
int nodewidth = e.Node.Bounds.Width;

int linelength =10;
int checkboxwidth = 13;
int checknodespace = 2;
using (Pen p = new Pen(Color.Gray))
{
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
int lineleft = nodeleft-checknodespace-checkboxwidth-linelength;
//draw horizontal dot line
e.Graphics.DrawLine(p, lineleft, top + height / 2, lineleft +
linelength, top + height / 2);
// draw the up half vertical dot line
if (e.Node.PrevNode != null || e.Node.Parent != null)
{
e.Graphics.DrawLine(p, lineleft, top, lineleft, top + height / 2);
}
// draw the down half vertical dot line
if (e.Node.NextNode != null)
{
e.Graphics.DrawLine(p, lineleft, top + height / 2,
lineleft, e.Node.NextNode.Bounds.Top);
}
// draw plus/minus image
if (e.Node.Nodes.Count > 0)
{
if (!e.Node.IsExpanded)
{
e.Graphics.DrawImage(Properties.Resources.plus,
lineleft - Properties.Resources.plus.Width / 2, top +
(height -
Properties.Resources.plus.Height) / 2);
}
else
{
e.Graphics.DrawImage(Properties.Resources.minus,
lineleft - Properties.Resources.minus.Width / 2, top +
(height -
Properties.Resources.minus.Height) / 2);
}
}
// draw the vertical dot line for the parent nodes if necessary
TreeNode parentNode = e.Node.Parent;
int parentNodeLeftDistance = checknodespace + checkboxwidth +
linelength;
while (parentNode != null)
{
if (parentNode.NextNode != null)
{
e.Graphics.DrawLine(p, parentNode.Bounds.X -
parentNodeLeftDistance,
top, parentNode.Bounds.X - parentNodeLeftDistance, top +
height);
}
parentNode = parentNode.Parent;
}
}
// draw checkbox
if (e.Node.Checked)
{
ControlPaint.DrawCheckBox(e.Graphics, new Rectangle(nodeleft -
checkboxwidth - checknodespace,
top + (height - checkboxwidth) / 2, checkboxwidth, checkboxwidth),
ButtonState.Checked);
}
else
{
ControlPaint.DrawCheckBox(e.Graphics, new Rectangle(nodeleft -
checkboxwidth - checknodespace, top + (height - checkboxwidth) / 2,
checkboxwidth, checkboxwidth), ButtonState.Normal);
}
// erase the previous node text
using (Brush b = new SolidBrush(BackColor))
{
e.Graphics.DrawString(e.Node.Text,Font, b, nodeleft, nodetop);
}
// draw node text and highlight rectangle
if (e.Node.IsSelected)
{
ControlPaint.DrawFocusRectangle(e.Graphics, e.Node.Bounds);

e.Graphics.FillRectangle(Brushes.LightSkyBlue, new
Rectangle(nodeleft + 1, nodetop + 1, nodewidth - 2, nodeheight - 2));
using (Brush b = new SolidBrush(Color.White))
{
e.Graphics.DrawString(e.Node.Text, Font, b, nodeleft, nodetop);
}
}
else
{
using (Brush b = new SolidBrush(ForeColor))
{
e.Graphics.DrawString(e.Node.Text, Font, b, nodeleft, nodetop);
}
}
}

}
}

File: form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace TreeViewErrorVista
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void timer1_Tick(object sender, EventArgs e)
{
if (treeViewVista1.Nodes[0].Checked)
{
lbtreeViewVista1.Text = "Checked";
}
else
{
lbtreeViewVista1.Text = "Not checked";
}

if (treeView1.Nodes[0].Checked)
{
lbtreeView1.Text = "Checked";
}
else
{
lbtreeView1.Text = "Not checked";
}

if (treeViewVista2.Nodes[0].Checked)
{
lbtreeViewVista2.Text = "Checked";
}
else
{
lbtreeViewVista2.Text = "Not checked";
}

if (treeView2.Nodes[0].Checked)
{
lbtreeView2.Text = "Checked";
}
else
{
lbtreeView2.Text = "Not checked";
}

}

private void Form1_Load(object sender, EventArgs e)
{

foreach (TreeView tv in new TreeView[] { treeView1, treeView2,
treeViewVista1, treeViewVista2 })
{
TreeNode myNode = new TreeNode("TestNode", 0, 0);
tv.Nodes.Add(myNode);
myNode.Nodes.Add("SubNode2");
myNode.Nodes.Add("SubNode3");
tv.Nodes.Add(new TreeNode("TestNode2", 0, 0));
tv.Nodes.Add(new TreeNode("TestNode3", 0, 0));
}
}

}
}
If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

Sincerely.
Dieter Pelz
 
Hi Dieter,

Thank you for feedback!

I understand your concern and I will forward your concern to our product
team.

As for the workaround of custom drawing the TreeView by ourselves, I must
say that it may be difficult to make the custom drawn TreeView look exactly
the same as the standard TreeView, because the TreeView has so many
properties that would influence the appearance and behavior of it. But we
can do our best to make the custom drawn TreeView look more like a standard
TreeView.

The following is the modified code of the custom drawn TreeView:

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Windows.Forms.VisualStyles;

public class TreeViewVista : TreeView
{
public TreeViewVista()
{
if (Environment.OSVersion.Version.Major >= 6)
{
DrawMode = TreeViewDrawMode.OwnerDrawAll;
DrawNode += new DrawTreeNodeEventHandler(treeView1_DrawNode);
}
}

int linelength = 0;
private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs
e)
{
int left = e.Bounds.X;
int top = e.Bounds.Y;
int height = e.Bounds.Height;
int nodeleft = e.Node.Bounds.X;
int nodetop = e.Node.Bounds.Y;
int nodeheight = e.Node.Bounds.Height;
int nodewidth = e.Node.Bounds.Width;
int checkboxwidth = 13;
int checknodespace = 2;
if (e.Node.Level == 0 && e.Node.Index == 0)
{
linelength = nodeleft - checknodespace - checkboxwidth - left;
if (this.ImageList != null)
{
linelength -= this.ImageList.ImageSize.Width;
}
linelength = linelength / 2;
}

int lineleft = nodeleft - checknodespace - checkboxwidth -
linelength;
if (this.ImageList != null)
{
lineleft -= this.ImageList.ImageSize.Width;
}

using (Pen p = new Pen(Color.Gray))
{
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

//draw horizontal dot line
e.Graphics.DrawLine(p, lineleft, top + height / 2, lineleft +
linelength, top + height / 2);
// draw the up half vertical dot line
if (e.Node.PrevNode != null || e.Node.Parent != null)
{
e.Graphics.DrawLine(p, lineleft, top, lineleft, top +
height / 2);
}
// draw the down half vertical dot line
if (e.Node.NextNode != null)
{
e.Graphics.DrawLine(p, lineleft, top + height / 2,
lineleft, e.Node.NextNode.Bounds.Top);
}
// draw plus/minus image
if (e.Node.Nodes.Count > 0)
{
if (!e.Node.IsExpanded)
{
e.Graphics.DrawImage(Properties.Resources.plus,
lineleft - Properties.Resources.plus.Width / 2, top
+ (height -
Properties.Resources.plus.Height) / 2);
}
else
{
e.Graphics.DrawImage(Properties.Resources.minus,
lineleft - Properties.Resources.minus.Width / 2,
top + (height -
Properties.Resources.minus.Height) / 2);
}
}
// draw the vertical dot line for the parent nodes if
necessary
TreeNode parentNode = e.Node.Parent;
int parentNodeLeftDistance = checknodespace + checkboxwidth +
linelength;
if (this.ImageList != null)
{
parentNodeLeftDistance += this.ImageList.ImageSize.Width;
}
while (parentNode != null)
{
if (parentNode.NextNode != null)
{
e.Graphics.DrawLine(p, parentNode.Bounds.X -
parentNodeLeftDistance,
top, parentNode.Bounds.X - parentNodeLeftDistance,
top + height);
}
parentNode = parentNode.Parent;
}
}
// draw checkbox
if (e.Node.Checked)
{
CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point(lineleft
+ linelength,
top + (height - checkboxwidth) / 2),
CheckBoxState.CheckedNormal);
}
else
{
CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point(lineleft
+ linelength,
top + (height - checkboxwidth) / 2),
CheckBoxState.UncheckedNormal);
}

// draw image if any
if (this.ImageList != null && e.Node.ImageIndex >= 0)
{

e.Graphics.DrawImage(this.ImageList.Images[e.Node.ImageIndex], new
Rectangle(lineleft + linelength + checkboxwidth, top -
(this.ImageList.ImageSize.Height - height) / 2,
this.ImageList.ImageSize.Width, this.ImageList.ImageSize.Height));
}
// erase the previous node text and highlight background
using (Brush b = new SolidBrush(BackColor))
{
e.Graphics.DrawString(e.Node.Text, Font, b, nodeleft, nodetop
+ (nodeheight - this.Font.Height) / 2);
e.Graphics.FillRectangle(b, new Rectangle(nodeleft + 1,
nodetop + 1, nodewidth - 2, nodeheight - 2));
}
// draw node text
if (this.Focused && e.Node.IsSelected)
{
// draw highlight background
e.Graphics.FillRectangle(SystemBrushes.Highlight, new
Rectangle(nodeleft + 1, nodetop + 1, nodewidth - 2, nodeheight - 2));
}
else if (!this.Focused && !this.HideSelection &&
e.Node.IsSelected)
{
// draw gray background
e.Graphics.FillRectangle(SystemBrushes.ControlLight, new
Rectangle(nodeleft + 1, nodetop + 1, nodewidth - 2, nodeheight - 2));
}
// draw highlight rectangle
if (e.Node.IsSelected && this.Focused)
{
using (Brush b = new SolidBrush(Color.White))
{
e.Graphics.DrawString(e.Node.Text, Font, b, nodeleft,
nodetop + (nodeheight - this.Font.Height) / 2);
}
}
else
{
using (Brush b = new SolidBrush(ForeColor))
{
e.Graphics.DrawString(e.Node.Text, Font, b, nodeleft,
nodetop + (nodeheight - this.Font.Height) / 2);
}
}

}
}
Please try the above code in your project to see if there's any problem in
it and let me know the result.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Hi Dieter,

I have forwarded your concern to our product team.

How about the modified sample I gave you in my previous reply? Is there any
problem?

If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Hallo Linda,

thanks for your sample.

I changed the recognition of the ImageList entry and now is looks like
nearly the same.

How could I recognize if Visual Styles are enable to switch on the OwnerDraw
function only with visual styles?

I recognized a 1 Pixel difference in horizontal axis between the OwnerDraw
function an the real function. This could be a dependency on the bitmaps for
plus and minus. What size must they have?

Thanks,
Dieter Pelz

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Drawing;

namespace TreeViewErrorVista
{
public class TreeViewVista : TreeView
{
public TreeViewVista()
{
if (Environment.OSVersion.Version.Major >= 6)
{
DrawMode = TreeViewDrawMode.OwnerDrawAll;
DrawNode += new DrawTreeNodeEventHandler(treeView1_DrawNode);
}
}

int linelength = 0;
private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs
e)
{
int left = e.Bounds.X;
int top = e.Bounds.Y;
int height = e.Bounds.Height;
int nodeleft = e.Node.Bounds.X;
int nodetop = e.Node.Bounds.Y;
int nodeheight = e.Node.Bounds.Height;
int nodewidth = e.Node.Bounds.Width;
int checkboxwidth = 13;
int checknodespace = 2;
if (e.Node.Level == 0 && e.Node.Index == 0)
{
linelength = nodeleft - checknodespace - checkboxwidth - left;
if (this.ImageList != null)
{
linelength -= this.ImageList.ImageSize.Width;
}
linelength = linelength / 2;
}

int lineleft = nodeleft - checknodespace - checkboxwidth -
linelength;
if (this.ImageList != null)
{
lineleft -= this.ImageList.ImageSize.Width;
}

using (Pen p = new Pen(Color.Gray))
{
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

//draw horizontal dot line
e.Graphics.DrawLine(p, lineleft, top + height / 2, lineleft +
linelength, top + height / 2);
// draw the up half vertical dot line
if (e.Node.PrevNode != null || e.Node.Parent != null)
{
e.Graphics.DrawLine(p, lineleft, top, lineleft, top +
height / 2);
}
// draw the down half vertical dot line
if (e.Node.NextNode != null)
{
e.Graphics.DrawLine(p, lineleft, top + height / 2,
lineleft, e.Node.NextNode.Bounds.Top);
}
// draw plus/minus image
if (e.Node.Nodes.Count > 0)
{
if (!e.Node.IsExpanded)
{
e.Graphics.DrawImage(Properties.Resources.plus,
lineleft - Properties.Resources.plus.Width / 2, top + (height -
Properties.Resources.plus.Height) / 2);
}
else
{
e.Graphics.DrawImage(Properties.Resources.minus,
lineleft - Properties.Resources.minus.Width / 2, top
+ (height - Properties.Resources.minus.Height) / 2);
}
}
// draw the vertical dot line for the parent nodes if
necessary
TreeNode parentNode = e.Node.Parent;
int parentNodeLeftDistance = checknodespace + checkboxwidth +
linelength;
if (this.ImageList != null)
{
parentNodeLeftDistance += this.ImageList.ImageSize.Width;
}
while (parentNode != null)
{
if (parentNode.NextNode != null)
{
e.Graphics.DrawLine(p, parentNode.Bounds.X -
parentNodeLeftDistance,
top, parentNode.Bounds.X - parentNodeLeftDistance,
top + height);
}
parentNode = parentNode.Parent;
}
}
// draw checkbox
if (e.Node.Checked)
{
CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point(lineleft +
linelength,
top + (height - checkboxwidth) / 2),
CheckBoxState.CheckedNormal);
}
else
{
CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point(lineleft +
linelength,
top + (height - checkboxwidth) / 2),
CheckBoxState.UncheckedNormal);
}

// draw image if any
// if (this.ImageList != null && e.Node.ImageIndex >= 0)
if (this.ImageList != null)
{
int imageIndex = e.Node.ImageIndex;
if (e.Node.ImageIndex < 0)
imageIndex = 0;

e.Graphics.DrawImage(this.ImageList.Images[imageIndex],
new Rectangle(lineleft + linelength + checkboxwidth, top -
(this.ImageList.ImageSize.Height - height) / 2,
this.ImageList.ImageSize.Width,
this.ImageList.ImageSize.Height));
}
// erase the previous node text and highlight background
using (Brush b = new SolidBrush(BackColor))
{
e.Graphics.DrawString(e.Node.Text, Font, b, nodeleft, nodetop
+ (nodeheight - this.Font.Height) / 2);
e.Graphics.FillRectangle(b, new Rectangle(nodeleft + 1,
nodetop + 1, nodewidth - 2, nodeheight - 2));
}
// draw node text
if (this.Focused && e.Node.IsSelected)
{
// draw highlight background
e.Graphics.FillRectangle(SystemBrushes.Highlight, new
Rectangle(nodeleft + 1, nodetop + 1, nodewidth - 2, nodeheight - 2));
}
else if (!this.Focused && !this.HideSelection &&
e.Node.IsSelected)
{
// draw gray background
e.Graphics.FillRectangle(SystemBrushes.ControlLight, new
Rectangle(nodeleft + 1, nodetop + 1, nodewidth - 2, nodeheight - 2));
}
// draw highlight rectangle
if (e.Node.IsSelected && this.Focused)
{
using (Brush b = new SolidBrush(Color.White))
{
e.Graphics.DrawString(e.Node.Text, Font, b, nodeleft,
nodetop + (nodeheight - this.Font.Height) / 2);
}
}
else
{
using (Brush b = new SolidBrush(ForeColor))
{
e.Graphics.DrawString(e.Node.Text, Font, b, nodeleft,
nodetop + (nodeheight - this.Font.Height) / 2);
}
}

}
}

}
 
Hi Dieter,

Thank you for your feedback!
How could I recognize if Visual Styles are enable to switch on the
OwnerDraw function only with visual styles?

You can use the Application.RenderWithVisualStyles property to recognize if
Visual Styles are enabled.
I recognized a 1 Pixel difference in horizontal axis between the
OwnerDraw function an the real function. This could be a dependency on the
bitmaps for plus and minus. What size must they have?

Do you mean the size of the bitmaps for the plus and minus? In my sample
project, I set both the width and height of the two bitmaps to 9.

I made a small modification in the custom draw TreeView to ensure that the
TreeView control is custom drawn only when the CheckBox property is true
and the OS version is Vista and Visual Styles is enabled.

In addition, I implemented the ISupportInitialize interface in the derived
TreeView class so as to set DrawMode property to OwnerDrawAll when the
above condition is met at run time.

FYI, I use the Control.DesignMode property to recognize whether a control
is at design time. However, it's too early to use this property in the
constructor because the DesignMode property relies on the Site property(the
Site property is set by the designer, if the Site property is not null, the
DesignMode property returns true; otherwise, the DesignMode returns false)
and the key point is that the control has not finished the construction in
the constructor so the Site property has not been set and the DesignMode
always returns false.

In the other hand, if a control implements the ISupportInitialize
interface, the ISupportInitialize.BeginEdit and ISupportInitialize.EndEdit
methods will be serialized in the InitializeComponent method of a form when
we drag&drop this control onto a form.

Note that both the ISupportInitialize.BeginEdit and the
ISupportInitialize.EndEdit method are serialized after the control is
created, so we can use the DesignMode property to recognize if the control
is at design time within either the ISupportInitialize.BeginEdit method or
the ISupportInitialize.EndEdit method.

I will send my modified sample project to your email address. If there's
any question in the sample project, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Hi Dieter,

Thank you for your reply!
The only thing in the geometry that is not chown like the original is an
1 pixel offset. What must be change to figure this out?

Actually, I calculate the location of each element such as checkbox, image
and horizontal line based on some assumptions, e.g. the distance between
the checkbox and the node text is 2 pixels and the horizontal line starts
form the half of the bound left and checkbox. Obviously, it may not very
accurate.

We can adjust the value of the local variable 'checknodespace' defined in
derived TreeView's DrawNode event handler to 3. In addition, I find that if
the TreeView doesn't have a image list, the distance between checkbox and
the node text seems to be 0.

So we can modify the sample code as follows to figure this problem out:

private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
....
int checknodespace = 0;
if (this.ImageList != null)
{
checknodespace = 3;
}
....
}
The Color.White is used selected and focused nodes. Is this the right
Color, or is there an system color?

I find a system color called HightlightText, which is just the right color
for selected and focused nodes.
The Brush SystemBrushes.ControlLight seemed not to be the right color

I agree with you. Unfortunately, I don't find the exactly same color as the
one used in the standard TreeView. IMO, the color of ControlLight is the
most similar one.

If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Hi Linda,

thanks for your answer.

I adapted your changes.

There are some more problems:

I changed the Font to "Courier New, 10pt" and added longer text in the
nodes, especially "wwwwwWWWWW". I added Traces when the text ist drawn.
1.) The vertical line is not drawn right when the node is expanded.
2.) The text is drawn longer than the selection rectangle. This means there
is a problem with the font.
3.) When a node is first expanded the text of the childnodes is draw on
position 0, -8


This brings me up the my main question:
Is this solution practically?
My opinion is that fixing this error in the Vista .Net Framework 2.0 will be
the right solution, because this is an error in the Framework 2.0 running
under Vista! The cached state of the TreeNode item is not handled right.
There have to be an mechanism to reread the state of the TreeNode from the
underlaying Win32 TreeView-Control.

Sincerely,
Dieter Pelz
 
Linda Liu said:
Hi Dieter, ....

I will send my modified sample project to your email address. If there's
any question in the sample project, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

Any chance you can send me the modified sample Linda? Or, could you please
post the current code.

Thanks much
 
Evening Linda,

Any chance you could send me a copy of the project you sent to Dieter?

Many thanks,
Brendon Bezuidenhout
 
Back
Top