Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations bkrike on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Custom Treenode to work with Form 1

Status
Not open for further replies.

golcarlad

Programmer
Nov 23, 2004
232
GB
OK - I have made a custom treenode class that inherits Treenode. In one of my methods I will change the background colour of the node to Red. When this happens I want it to disable a particular button on a form.
Does this mean I have to pass a reference to the form everytime I make a new instance of my custom treenode - seems clunky if thats the case?
 
You'll need to have a custom tree view also, you can then add a method which can be called by any treenode. Something like:
Code:
class TreeViewEx : System.Windows.Forms.TreeView
	{
		public event EventHandler TreeNodeDisabled;

		internal void RaiseDisabledEvent()
		{
			if (TreeNodeDisabled != null) TreeNodeDisabled(this, null);
		}
	}
Then, in the custom treenode, call the method:
Code:
((TreeViewEx)this.TreeView).RaiseDisabledEvent();
 
Code:
 internal void RaiseDisabledEvent()

If I change the above to public - it works ok - but I take it internal should work here - is there something I may have missed.

Plus how would how go about changing the "Enable" property of a treeview - I would like to change the colour it applies to the background of the control and some other stuff like changing the cursor.

So two questions there really - hope thats ok
 
Yeah, should have been Public - sorry, typed it without trying it!

You can implement your own Enabled proprty using the "new" modifier to hide the base TreeView.Enabled. However, I think you'll need to implement some custom drawing to provide your own 'disabled' look. The following gives an idea of what would be needed:
Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace WindowsApplication12
{
	class TreeViewEx : System.Windows.Forms.TreeView
	{
		const int WM_PAINT = 15;

		public TreeViewEx()
		{
			this.DrawMode = TreeViewDrawMode.OwnerDrawText;
		}

		private bool enabled = true;

		public new bool Enabled
		{
			get { return enabled; }

			set
			{
				enabled = value;

				this.Invalidate();
			}
		}

		protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
		{
			base.OnBeforeSelect(e);

			e.Cancel = !enabled;
		}

		protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
		{
			base.OnBeforeExpand(e);

			e.Cancel = !enabled;
		}

		protected override void OnBeforeCollapse(TreeViewCancelEventArgs e)
		{
			base.OnBeforeCollapse(e);

			e.Cancel = !enabled;
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			if (!enabled)
			{
				using (System.Drawing.Drawing2D.HatchBrush hb = new System.Drawing.Drawing2D.HatchBrush(System.Drawing.Drawing2D.HatchStyle.ForwardDiagonal, Color.Black, Color.LightGray))
				{
					e.Graphics.FillRectangle(hb, e.ClipRectangle);
				}
			}

			else
			{
				base.OnPaint(e);
			}
		}

		protected override void WndProc(ref Message m)
		{
			base.WndProc(ref m);

			if (m.Msg == WM_PAINT && !enabled)
			{
				using (Graphics g = this.CreateGraphics())
				{
					using (System.Drawing.Drawing2D.HatchBrush hb = new System.Drawing.Drawing2D.HatchBrush(System.Drawing.Drawing2D.HatchStyle.ForwardDiagonal, Color.DarkGray, Color.Transparent))
					{
						g.FillRectangle(hb, this.ClientRectangle );
					}
				}
			}
		}

		protected override void OnDrawNode(DrawTreeNodeEventArgs e)
		{
			if (e.Node.IsVisible)
			{
				e.Graphics.FillRectangle(Brushes.White, e.Bounds);

				using (Pen p = new Pen(Color.DarkBlue))
				{
					if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot && enabled)
					{
						using (SolidBrush br = new SolidBrush(Color.CornflowerBlue))
						{
							e.Graphics.FillRectangle(br, e.Bounds);
						}

						e.Graphics.DrawRectangle(p, new Rectangle(e.Bounds.Location, new Size(e.Bounds.Width - 1, e.Bounds.Height - 1)));
					}

					if (e.Node == e.Node.TreeView.SelectedNode)
					{
						using (SolidBrush br = new SolidBrush(Color.LightBlue))
						{
							e.Graphics.FillRectangle(br, e.Bounds);
						}

						e.Graphics.DrawRectangle(p, new Rectangle(e.Bounds.Location, new Size(e.Bounds.Width, e.Bounds.Height - 1)));
					}
				}

				using (SolidBrush br = new SolidBrush(this.ForeColor))
				{
					e.Graphics.DrawString(e.Node.Text, this.Font, br, e.Bounds.Left, e.Bounds.Top + 1);
				}
			}
		}
	}
}
It's not perfect, but should start you in the right direction.
 
Oops, ignore the OnPaint override - meant to take it out before posting!
 
Hey thanks for that - that should really set me going on making a pretty cool treeview now!
How did you get to know how to do this? is there some reference material on MSDN or something, like

Code:
 const int WM_PAINT = 15;

how would you know to put that down?


Cheers
Simon
 
The WndProc override exposes all the messages that the control receives (search MSDN for a more comprehensive explanation of Windows messages).

.NET usually provides a much more user-friendly way of customising a control by exposing a Paint event. Some controls, however, such as the TreeView do not fully expose all the painting using Paint. That's why my original post had a OnPaint override, as I tried to use the event but found it didn't actually get fired (try remarking out the WndProc override to see that the Paint event doesn't work).

So, the only option is to "listen" for the Windows Message that tells the control to paint (draw) itself, and after allowing it to do the draw (by passing the "m" parameter to the base class) we draw our hatched lines over the top of the control. Allowing the base class to draw first ensures that everything is drawn correctly, such as borders and child controls.

Creating a constant that begins WM_ is the standard way of describing the value of the message that we are listening for.

Hope this helps.
 
Just adopted your code in my prog - and have made the treeview shade to an opaque grey if its disabled - problem is, if the user clicks and keeps clicking on a disabled treeview - it seems to repaint and make it go darker and darker grey - I think it is something to do with re-application of an alpha layer, which eventually makes it go from opaque to solid.

What would be the best way to stop this happening - I have an idea to use a bool variable to check if its enabled or not and then skip the rest of the method if its already disabled??

Heres the code just in case but its no big shakes:

Code:
protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == WM_PAINT && !enabled)
            {
                using (Graphics g = this.CreateGraphics())
                {
                    //using (System.Drawing.Drawing2D.HatchBrush hb = 
                    //    new System.Drawing.Drawing2D.HatchBrush(System.Drawing.Drawing2D.HatchStyle.ForwardDiagonal,
                    //    Color.GhostWhite, Color.Transparent))

                    System.Drawing.Color col = new System.Drawing.Color();
                    col = Color.FromArgb(25, 100, 100, 100);

                    using (SolidBrush SolidBrushRedTrans = new SolidBrush(col))
                            
                    {
                        g.FillRectangle(SolidBrushRedTrans, this.ClientRectangle);
                    }
                }
            }
        }
 
Yes, it is the alpha layer that is causing the problem. As you say, a bool so that it is only painted once is the easiest way that I know of.
 
Good stuff - (sorry to keep on with this by the way)
 
No probs. If you come across a way without another bool then post it - I'd be interested!
 
The bool kind of works - but only on the first refresh of the control - after that it paints the control with default colour i.e. white. Then I lose my nice opaque effect. What I dont understand is what is making the control go white, it must be something to do with a base method perhaps?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top