Click here to Skip to main content
15,891,431 members
Articles / Programming Languages / C++/CLI

MaChat - a chat with a browser for LANs

Rate me:
Please Sign up or sign in to vote.
4.97/5 (27 votes)
30 Jul 200211 min read 582.5K   12.4K   119  
This article shows how to create a Chat for Local Area Networks which uses the WebBrowser control to display the messages.
//*****************************************************************************
//
//  (c) Crownwood Consulting Limited 2002
//  All rights reserved. The software and associated documentation
//  supplied hereunder are the proprietary information of Crownwood Consulting
//	Limited, Haxey, North Lincolnshire, England and are supplied subject to
//	licence terms.
//
//  Magic Version 1.6	www.dotnetmagic.com
// *****************************************************************************

using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.ComponentModel;
using Microsoft.Win32;
using Crownwood.Magic.Win32;
using Crownwood.Magic.Common;
using Crownwood.Magic.Controls;
using Crownwood.Magic.Collections;

namespace Crownwood.Magic.Docking
{
    [ToolboxItem(false)]
    public class TabStub : UserControl
    {
		private class DrawTab
		{
			protected int _index;
			protected Rectangle _drawRect;
            protected Crownwood.Magic.Controls.TabPage _tabPage;

			public DrawTab(Crownwood.Magic.Controls.TabPage tabPage, Rectangle drawRect, int index)
			{
				_index = index;
				_tabPage = tabPage;
				_drawRect = drawRect;
			}

			public Crownwood.Magic.Controls.TabPage TabPage  { get { return _tabPage; } }
            public Rectangle DrawRect                        { get { return _drawRect; } }
			public int Index                                 { get { return _index; } }
		}

        // Class constants
		protected static int _imageGap = 3;
		protected static int _imageGaps = 6;
        protected static int _imageVector = 16;
        protected static int _beginGap = 2;
        protected static int _endGap = 8;
        protected static int _sideGap = 2;
		protected static int _hoverInterval = 500;

		// Instance fields
		protected Edge _edge;
		protected int _hoverOver;
		protected int _hoverItem;
		protected int _selectedIndex;
    	protected bool _defaultFont;
		protected bool _defaultColor;
		protected Color _backIDE;
        protected Timer _hoverTimer;
        protected TabPageCollection _tabPages;
		protected WindowContentTabbed _wct;
		protected ArrayList _drawTabs;
        protected VisualStyle _style;

        public delegate void TabStubIndexHandler(TabStub sender, int pageIndex);
        public delegate void TabStubHandler(TabStub sender);

        // Exposed events
        public event TabStubIndexHandler PageClicked;
        public event TabStubIndexHandler PageOver;
        public event TabStubHandler PagesLeave;

		public TabStub(VisualStyle style)
		{
			// Default state
			_wct = null;
			_style = style;
            _hoverOver = -1;
            _hoverItem = -1;
            _selectedIndex = -1;
            _defaultFont = true;
			_defaultColor = true;
			_edge = Edge.None;
			_drawTabs = new ArrayList();
            _tabPages = new TabPageCollection();
            base.Font = SystemInformation.MenuFont;

            // Hookup to collection events
            _tabPages.Cleared += new CollectionClear(OnClearedPages);
            _tabPages.Inserted += new CollectionChange(OnInsertedPage);
            _tabPages.Removing += new CollectionChange(OnRemovingPage);
            _tabPages.Removed += new CollectionChange(OnRemovedPage);

            // Need notification when the MenuFont is changed
            Microsoft.Win32.SystemEvents.UserPreferenceChanged += new 
                UserPreferenceChangedEventHandler(OnPreferenceChanged);

			// Default default colors
			DefineBackColor(SystemColors.Control);

			// Create the Timer for handling hovering over items
			_hoverTimer = new Timer();
			_hoverTimer.Interval = _hoverInterval;
			_hoverTimer.Tick += new EventHandler(OnTimerExpire);
		}

        public TabPageCollection TabPages
        {
            get { return _tabPages; }

            set
            {
                _tabPages.Clear();
                _tabPages = value;
            }
        }

		public Edge Edging
		{
			get { return _edge; }

			set
			{
				if (value != _edge)
				{
					_edge = value;
					ResizeControl();
					Recalculate();
					Invalidate();
				}
		    }
		}

		public int SelectedIndex
		{
			get { return _selectedIndex; }

			set
			{
				if (value != _selectedIndex)
				{
					_selectedIndex = value;
					Recalculate();
					Invalidate();
				}
			}
		}

        public override Font Font
        {
            get { return base.Font; }

            set
            {
				if (value != null)
				{
					if (value != base.Font)
					{
						_defaultFont = (value == SystemInformation.MenuFont);

						base.Font = value;
						ResizeControl();
						Recalculate();
						Invalidate();
					}
				}
            }
        }

        public override Color BackColor
        {
            get { return base.BackColor; }

            set
            {
                if (this.BackColor != value)
                {
                    _defaultColor = (value == SystemColors.Control);
					DefineBackColor(value);
                    Invalidate();
                }
            }
        }

        public WindowContentTabbed WindowContentTabbed
        {
            get { return _wct; }
            set { _wct = value; }
        }
        
		public void OnPageClicked(int pageIndex)
		{
            // Has anyone registered for the event?
			if (PageClicked != null)
				PageClicked(this, pageIndex);
		}

		public void OnPageOver(int pageIndex)
		{
            // Has anyone registered for the event?
			if (PageOver != null)
				PageOver(this, pageIndex);
		}

        public void OnPagesLeave()
        {
            // Has anyone registered for the event?
            if (PagesLeave != null)
                PagesLeave(this);
        }
        
        public void PropogateNameValue(PropogateName name, object value)
        {
            switch(name)
            {
                case PropogateName.BackColor:
                    this.BackColor = (Color)value;
                    Invalidate();
                    break;
                case PropogateName.InactiveTextColor:
                    this.ForeColor = (Color)value;
                    Invalidate();
                    break;
                case PropogateName.CaptionFont:
                    this.Font = (Font)value;
                    break;
            }
            
            // Pass onto the contained WCT
            _wct.PropogateNameValue(name, value);
        }
                
        protected void DefineBackColor(Color backColor)
		{
			base.BackColor = backColor;
			
            _backIDE = ColorHelper.TabBackgroundFromBaseColor(backColor);
		}

		protected void OnPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
		{
			// Are we using the default menu or a user defined value?
			if (_defaultFont)
			{
				base.Font = SystemInformation.MenuFont;
				ResizeControl();
				Recalculate();
				Invalidate();
			}
		}

		protected override void OnSystemColorsChanged(EventArgs e)
		{
			// If still using the Default color when we were created
			if (_defaultColor)
			{
				this.BackColor = SystemColors.Control;
				Invalidate();
			}

			base.OnSystemColorsChanged(e);
		}

        protected void OnClearedPages()
        {
            // Cancel any hover selection
            CancelHoverItem();

			// Cancel any current selection
			_selectedIndex = -1;

			ResizeControl();
			Recalculate();
			Invalidate();
		}
		
        protected void OnInsertedPage(int index, object value)
		{
			// If no page is currently selected
			if (_selectedIndex == -1)
			{
				// Then make the inserted page selected
				_selectedIndex = index;
			}

			ResizeControl();
			Recalculate();
			Invalidate();
		}

        protected void OnRemovingPage(int index, object value)
        {
            // Removed page involved in hover calculations?
            if ((_hoverOver == index) || (_hoverItem == index))
                CancelHoverItem();
        
            // Removing the last page?
            if (_tabPages.Count == 1)
            {
                // Get rid of any selection
                _selectedIndex = -1;
            }
            else
            {
                // If removing a page before the selected one...
			    if (index < _selectedIndex)
			    {
                    // ...then the selected index must be decremented to match
					_selectedIndex--;
			    }
			    else
			    {
			        // If the selected page is the last one then...
			        if (_selectedIndex == (_tabPages.Count-1))
			        {
			            // Must reduce selected index
                        _selectedIndex--;
                    }
			    }
	        }
        }

        protected void OnRemovedPage(int index, object value)
        {
			ResizeControl();
			Recalculate();
			Invalidate();
		}

        protected void CancelHoverItem()
        {
            // Currently timing a hover change?
            if (_hoverOver != -1)
            {
                // Prevent timer from expiring
                _hoverTimer.Stop();
                
                // No item being timed
                _hoverOver = -1;
            }

            // Any current hover item?
            if (_hoverItem != -1)
            {
                // No item is being hovered
                _hoverItem = -1;
		        
                // Generate event for end of hover
                OnPagesLeave();
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
			// Create a point representing current mouse position
			Point mousePos = new Point(e.X, e.Y);

			int index = 0;
			int count = _drawTabs.Count;

			// Search each draw cell
			for(; index<count; index++)
			{
				DrawTab dt = _drawTabs[index] as DrawTab;

				// Is mouse over this cell?
				if (dt.DrawRect.Contains(mousePos))
				{
					// If the mouse is not over the hover item
					if (_hoverItem != dt.Index)
					{
					    // And we are not already timing this change in hover
					    if (_hoverOver != dt.Index)
					    {
					        // Start timing the hover change
						    _hoverTimer.Start();
						    
						    // Remember which item we are timing
						    _hoverOver = dt.Index;
				        }
					}

    				break;
				}
			}

			// Failed to find an item?
			if (index == count)
			{
				// If we have a hover item or timing a hover change
				if ((_hoverOver != -1) || (_hoverItem != -1))
				{
				    // Stop any timing
				    CancelHoverItem();
				}
			}

			base.OnMouseMove(e);
		}

        protected override void OnMouseLeave(EventArgs e)
        {
            // Remove any hover state
            CancelHoverItem();
    
			base.OnMouseLeave(e);
		}

		protected void OnTimerExpire(object sender, EventArgs e)
		{
		    // Prevent the timer from firing again
			_hoverTimer.Stop();

            // A change in hover still valid?
            if (_hoverItem != _hoverOver)
            {
                // This item becomes the current hover item
                _hoverItem = _hoverOver;
                
                // No longer in a timing state
                _hoverOver = -1;

			    // Do we need a change in selection?
			    if (_selectedIndex != _hoverItem)
			    {
				    // Change selection and redraw
				    _selectedIndex = _hoverItem;

				    Recalculate();
				    Invalidate();
                }

			    // Generate event to notify where mouse is now hovering
			    OnPageOver(_selectedIndex);
		    }
		}

        protected override void OnMouseDown(MouseEventArgs e)
        {
            // Only select a button or page when using left mouse button
            if (e.Button == MouseButtons.Left)
			{
                // Create a point representing current mouse position
                Point mousePos = new Point(e.X, e.Y);

				int count = _drawTabs.Count;

				// Search each draw cell
				for(int index=0; index<count; index++)
				{
					DrawTab dt = _drawTabs[index] as DrawTab;

					// Is mouse pressed in this draw cell?
					if (dt.DrawRect.Contains(mousePos))
					{
                        // Prevent any hover timer expiring
                        _hoverTimer.Stop();
						    
                        // This becomes the current hover item
                        _hoverItem = _selectedIndex;
						    
                        // Not timing a hover change
                        _hoverOver = _hoverItem;
                        
                        // Will this cause a change in selection?
						if (_selectedIndex != dt.Index)
						{
                            // Change selection and redraw
                            _selectedIndex = dt.Index;
						
							Recalculate();
							Invalidate();
                        }

                        // Generate event to notify a click occured on the selection
                        OnPageClicked(_selectedIndex);

                        break;
					}
				}
			}
       }

        public static int TabStubVector(Font font)
        {
            int fixedVector = _imageVector + _imageGaps;

            int minFontVector = font.Height + _imageGaps;

            // Make sure at least bit enough for the provided font
            if (fixedVector < minFontVector)
                fixedVector = minFontVector;
                
            return fixedVector + _sideGap;
        }

		protected void ResizeControl()
		{
			int textMax = 0;

			// Find largest space needed for drawing page text
			using(Graphics g = this.CreateGraphics())
			{
				foreach(Crownwood.Magic.Controls.TabPage page in _tabPages)
				{
					// Find width of the requested text
					SizeF dimension = g.MeasureString(page.Title, this.Font);

					if ((int)dimension.Width > textMax)
						textMax = (int)dimension.Width;
				}
			}

			// Calculate total width/height needed
			int variableVector = _tabPages.Count * (_imageVector + _imageGaps) + textMax + _imageGap;

            // Calculate the fixed direction value
			int fixedVector = TabStubVector(this.Font);

			// Resize the control as appropriate
			switch(_edge)
			{
				case Edge.Left:
				case Edge.Right:
					this.Size = new Size(fixedVector, variableVector + _beginGap + _endGap);
					break;
				case Edge.Top:
				case Edge.Bottom:
				case Edge.None:
				default:
					this.Size = new Size(variableVector + _beginGap + _endGap, fixedVector);
					break;
			}
		}

		protected void Recalculate()
		{
			// Create a fresh colleciton for drawing objects
			_drawTabs = new ArrayList();

			// Need start and end position markers
			int posEnd;
			int cellVector = _imageVector + _imageGaps;
			int posStart = _beginGap;

			switch(_edge)
			{
				case Edge.Left:
				case Edge.Right:
					posEnd = this.Height - _endGap;
					break;
				case Edge.Top:
				case Edge.Bottom:
				case Edge.None:
				default:
					posEnd = this.Width - _endGap;
					break;
			}

			int count = _tabPages.Count;

			// Process from start of list until we find the selected one
			for(int index=0; (index<count) && (index!=_selectedIndex); index++)
			{
				Rectangle drawRect;

				// Drawing rectangle depends on direction
				switch(_edge)
				{
					case Edge.Left:
						drawRect = new Rectangle(0, posStart, this.Width - _sideGap - 1, cellVector);
						break;
					case Edge.Right:
						drawRect = new Rectangle(_sideGap, posStart, this.Width - _sideGap, cellVector);
						break;
					case Edge.Bottom:
						drawRect = new Rectangle(posStart, _sideGap, cellVector, this.Height - _sideGap);
						break;
					case Edge.Top:
					case Edge.None:
					default:
						drawRect = new Rectangle(posStart, 0, cellVector, this.Height - _sideGap - 1);
						break;
				}

				// Move starting position
				posStart += cellVector;

				// Generate new drawing object for this tab
				_drawTabs.Add(new DrawTab(_tabPages[index], drawRect, index));
			}

			// Process from end of list until we find the selected one
			for(int index=count-1; (index>=0) && (index!=_selectedIndex); index--)
			{
				Rectangle drawRect;

				// Drawing rectangle depends on direction
				switch(_edge)
				{
					case Edge.Left:
						drawRect = new Rectangle(0, posEnd - cellVector, this.Width - _sideGap - 1, cellVector);
						break;
					case Edge.Right:
						drawRect = new Rectangle(_sideGap, posEnd - cellVector, this.Width - _sideGap, cellVector);
						break;
					case Edge.Bottom:
						drawRect = new Rectangle(posEnd - cellVector, _sideGap, cellVector, this.Height - _sideGap);
						break;
					case Edge.Top:
					case Edge.None:
					default:
						drawRect = new Rectangle(posEnd - cellVector, 0, cellVector, this.Height - _sideGap - 1);
						break;
				}

				// Move starting position
				posEnd -= cellVector;

				// Generate new drawing object for this tab
				_drawTabs.Add(new DrawTab(_tabPages[index], drawRect, index));
			}

			if (_selectedIndex != -1)
			{
				Rectangle drawRect;

				// Drawing rectangle depends on direction
				switch(_edge)
				{
					case Edge.Left:
						drawRect = new Rectangle(0, posStart, this.Width - _sideGap - 1, posEnd - posStart);
						break;
					case Edge.Right:
						drawRect = new Rectangle(_sideGap, posStart, this.Width - _sideGap, posEnd - posStart);
						break;
					case Edge.Bottom:
						drawRect = new Rectangle(posStart, _sideGap, posEnd - posStart, this.Height - _sideGap);
						break;
					case Edge.Top:
					case Edge.None:
					default:
						drawRect = new Rectangle(posStart, 0, posEnd - posStart, this.Height - _sideGap - 1);
						break;
				}

				// Generate new drawing object for this tab
				_drawTabs.Add(new DrawTab(_tabPages[_selectedIndex], drawRect, _selectedIndex));
			}
		}

		protected void AdjustRectForEdge(ref Rectangle rect)
		{
			// Adjust rectangle to exclude desired edge
			switch(_edge)
			{
				case Edge.Left:
					rect.X--;
					rect.Width++;
					break;
				case Edge.Right:
					rect.Width++;
					break;
				case Edge.Bottom:
					rect.Height++;
					break;
				case Edge.Top:
				case Edge.None:
				default:
					rect.Y--;
					rect.Height++;
					break;
			}
		}

        protected void DrawOutline(Graphics g, bool pre)
        {
            Rectangle borderRect = new Rectangle(0, 0, this.Width - 1, this.Height - 1);

            // Adjust for drawing area
            switch(_edge)
            {
                case Edge.Left:
                    borderRect.Y += _beginGap;
                    borderRect.Height -= _beginGap + _endGap - 1;
                    borderRect.Width -= _sideGap;
                    break;
                case Edge.Right:
                    borderRect.Y += _beginGap;
                    borderRect.Height -= _beginGap + _endGap - 1;
                    borderRect.X += _sideGap;
                    borderRect.Width -= _sideGap;
                    break;
                case Edge.Bottom:
                    borderRect.Y += _sideGap;
                    borderRect.Height -= _sideGap;
                    borderRect.X += _beginGap;
                    borderRect.Width -= _beginGap + _endGap - 1;
                    break;
                case Edge.Top:
                case Edge.None:
                default:
                    borderRect.Height -= _sideGap;
                    borderRect.X += _beginGap;
                    borderRect.Width -= _beginGap + _endGap - 1;
                    break;
            }

            // Remove unwated drawing edge
            AdjustRectForEdge(ref borderRect);

            if (pre)
            {
                if (_style == VisualStyle.IDE)
                {
			        // Fill tab area in required color
                    using(SolidBrush fillBrush = new SolidBrush(this.BackColor))
                        g.FillRectangle(fillBrush, borderRect);
                }
            }
            else
            {
                if (_style == VisualStyle.Plain)
                {
                    using(Pen penL = new Pen(ControlPaint.LightLight(this.BackColor)),
                          penD = new Pen(ControlPaint.Dark(this.BackColor)))
                    {
                        g.DrawLine(penL, borderRect.Left, borderRect.Top, borderRect.Right, borderRect.Top);
                        g.DrawLine(penL, borderRect.Left, borderRect.Top, borderRect.Left, borderRect.Bottom);
                        g.DrawLine(penD, borderRect.Right, borderRect.Top, borderRect.Right, borderRect.Bottom);
                        g.DrawLine(penD, borderRect.Right, borderRect.Bottom, borderRect.Left, borderRect.Bottom);
                    }
                }
            }
        }

        protected void DrawOutlineForCell(Graphics g, Pen pen, Rectangle rect)
        {
			// Draw border around the tab cell
			if (_style == VisualStyle.IDE)
				g.DrawRectangle(pen, rect);
			else
		    {
		        switch(_edge)
		        {
		            case Edge.Left:
                    case Edge.Right:
                        g.DrawLine(pen, rect.Left + 1, rect.Bottom, rect.Right, rect.Bottom);       
                        break;                    
                    case Edge.Top:
                    case Edge.Bottom:
                        g.DrawLine(pen, rect.Right, rect.Top + 1, rect.Right, rect.Bottom);       
                        break;                    
                }
		    }
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            // Fill background in required color
            if (_style == VisualStyle.IDE)            
                using(SolidBrush fillBrush = new SolidBrush(_backIDE))
                    e.Graphics.FillRectangle(fillBrush, this.ClientRectangle);
            else
                using(SolidBrush fillBrush = new SolidBrush(this.BackColor))
                    e.Graphics.FillRectangle(fillBrush, this.ClientRectangle);

            // Style specific outline drawing
            DrawOutline(e.Graphics, true);

            // Draw border around area
			using(Pen borderPen = new Pen(ControlPaint.LightLight(this.ForeColor)))
			{
				// Draw each of the draw objects
				foreach(DrawTab dt in _drawTabs)
				{
					Rectangle drawRect = dt.DrawRect;

					AdjustRectForEdge(ref drawRect);

                    // Style specific cell outline drawing
                    DrawOutlineForCell(e.Graphics, borderPen, drawRect);

					// Draw the image in the left/top of the cell
					Crownwood.Magic.Controls.TabPage page = dt.TabPage;

					if ((page.ImageIndex != -1) && (page.ImageList != null))
					{
						int xDraw;
						int yDraw;

						switch(_edge)
						{
							case Edge.Left:
							case Edge.Right:
								xDraw = drawRect.Left + (drawRect.Width - _imageVector) / 2;
								yDraw = drawRect.Top + _imageGap;
								break;
							case Edge.Top:
							case Edge.Bottom:
							case Edge.None:
							default:
								xDraw = drawRect.Left + _imageGap;
								yDraw = drawRect.Top + (drawRect.Height - _imageVector) / 2;
								break;
						}

						// Draw the actual image
						e.Graphics.DrawImage(page.ImageList.Images[page.ImageIndex],
											 new Rectangle(xDraw, yDraw, _imageVector, _imageVector));


						// Is anything currently selected
						if (_selectedIndex != -1)
						{
							// Is this page selected?
							if (page == _tabPages[_selectedIndex])
							{
								Rectangle textRect;

								StringFormat drawFormat = new StringFormat();
						        drawFormat.FormatFlags = StringFormatFlags.NoClip | 
						                                 StringFormatFlags.NoWrap;
								drawFormat.Alignment = StringAlignment.Center;
								drawFormat.LineAlignment = StringAlignment.Center;

								// Create text drawing rectangle
								switch(_edge)
								{
									case Edge.Left:
									case Edge.Right:
										textRect = new Rectangle(drawRect.Left, yDraw + _imageVector + _imageGap, 
										                         drawRect.Width, drawRect.Height - _imageVector - _imageGap * 2);
				                        drawFormat.FormatFlags |= StringFormatFlags.DirectionVertical;
										break;
									case Edge.Top:
									case Edge.Bottom:
									case Edge.None:
									default:
										textRect = new Rectangle(xDraw + _imageVector + _imageGap, drawRect.Top, 
										                         drawRect.Width - _imageVector - _imageGap * 2, drawRect.Height);
										break;
								}
								
								Color brushColor = this.ForeColor;
								
								if (_style == VisualStyle.IDE)
								    brushColor = ControlPaint.Light(brushColor);

								using(SolidBrush drawBrush = new SolidBrush(brushColor))
									e.Graphics.DrawString(page.Title, this.Font, drawBrush, textRect, drawFormat);
							}
						}
					}
				}
			}

            // Style specific outline drawing
            DrawOutline(e.Graphics, false);
            
            base.OnPaint(e);
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect macmichal.pl
Poland Poland
Micheal is an independent consultant - www.macmichal.pl.
He's main areas of interest are: DDD\CqRS, TDD, SaaS, Design Patterns, Architecture. He specializes in .Net/C# for the early beginning of it and T-SQL. He's a writer, blogger (blog.macmichal.pl) and speaker.

In his spare time, he's climbing the mountains all over the Europe.

Comments and Discussions