www.pudn.com > sudoku.rar > TextStatefulButton.cs


//-------------------------------------------------------------------------- 
//  
//  Copyright (c) Microsoft Corporation.  All rights reserved.  
//  
//  File: TextStatefulButton.cs 
// 
//  Description: Custom button used for all buttons in Sudoku. 
//  
//-------------------------------------------------------------------------- 
 
using System; 
using System.Collections; 
using System.ComponentModel; 
using System.Drawing; 
using System.Drawing.Text; 
using System.Drawing.Drawing2D; 
using System.Drawing.Imaging; 
using System.Windows.Forms; 
using Microsoft.Sudoku.Utilities; 
 
namespace Microsoft.Sudoku.Controls 
{ 
	/// A custom button rendered with multiple images and text. 
	[ToolboxBitmap(typeof(Button))] 
	internal sealed class TextStatefulButton : Control 
	{ 
		/// Initializes the button. 
		public TextStatefulButton() 
		{ 
			SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer | 
				ControlStyles.UserPaint | ControlStyles.ResizeRedraw | 
				ControlStyles.SupportsTransparentBackColor | ControlStyles.CacheText, true); 
			SetStyle(ControlStyles.StandardDoubleClick | ControlStyles.StandardClick, false); 
			SetStyle(ControlStyles.Selectable, false); 
			BackColor = Color.Transparent; 
 
			_centeredFormat = new StringFormat(); //StringFormatFlags.FitBlackBox); 
			_centeredFormat.LineAlignment = StringAlignment.Center; 
			_centeredFormat.Alignment = StringAlignment.Center; 
		} 
 
		/// Image used as the shadow of the control. 
		private Image _shadowImage; 
		/// Image used to represent the pressed/checked state of the control. 
		private Image _checkedImage; 
		/// Image used to represent the unpressed/unchecked state of the control. 
		private Image _uncheckedImage; 
		/// Image used to represent the highlighted state of the control. 
		private Image _highlightedImage; 
		/// Image used to represent the icon/overlay for the control. 
		private Image _overlayImage; 
 
		/// Whether the control can be checked. 
		private bool _allowChecking; 
		/// Whether the control can be unchecked by a user. 
		private bool _allowNonprogrammaticUnchecking; 
		/// Whether the control is checked. 
		private bool _checked; 
		/// Whether the control is currently highlighted. 
		private bool _highlighted; 
		/// Whether the mouse is currently down. 
		private bool _mouseDown; 
 
		/// Used to center the text. 
		private StringFormat _centeredFormat; 
		/// The current font for rendering. 
		private Font _font; 
		/// Whether the font should autosize to fit within the button. 
		private bool _autosizeFont = true; 
		/// Whether the placement of text should be adjusted based on whether the button is checked. 
		private bool _adjustTextPlacement = true; 
 
		/// Sets the image used as the shadow of the control. 
		public Image ShadowImage { set { _shadowImage = value; } } 
		/// Sets the image used to represent the pressed/checked state of the control. 
		public Image CheckedImage { set { _checkedImage = value; } } 
		/// Sets the image used to represent the unpressed/unchecked state of the control. 
		public Image UncheckedImage { set { _uncheckedImage = value; } } 
		/// Sets the image used to represent the highlighted state of the control. 
		public Image HighlightedImage { set { _highlightedImage = value; } } 
		/// Sets the image used to represent the icon/overlay for the control. 
		public Image OverlayImage { set { _overlayImage = value; } } 
 
		/// Gets whether the control can be checked. 
		public bool AllowChecking { get { return _allowChecking; } set { _allowChecking = value; } } 
		/// Gets whether the control can be unchecked by a user. 
		public bool AllowNonprogrammaticUnchecking {  get { return _allowNonprogrammaticUnchecking; } set { _allowNonprogrammaticUnchecking = value; } } 
 
		/// Gets whether the control is currently highlighted. 
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
		[Browsable(false)] 
		private bool Highlighted { get { return _highlighted; } } 
 
		/// Gets wether the mouse is currently down. 
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
		[Browsable(false)] 
		private bool MouseIsDown { get { return _mouseDown; } } 
 
		/// Gets or sets whether the control is checked. 
		public bool Checked  
		{  
			get { return _allowChecking ? _checked : false; }  
			set  
			{ 
				if (_allowChecking && _checked != value) 
				{ 
					CancelEventArgs e = new CancelEventArgs(false); 
					OnBeforeCheckedChanged(e); 
					if (!e.Cancel) 
					{ 
						_checked = value;  
						Invalidate(); 
						OnCheckedChanged(); 
					} 
				} 
			} 
		} 
 
		/// Gets or sets whether the control is focusable. 
		public bool Focusable  
		{  
			get { return GetStyle(ControlStyles.Selectable); }  
			set { SetStyle(ControlStyles.Selectable, value); } 
		} 
 
		/// Gets or sets whether the control supports auto-font resize. 
		public bool AutosizeFont { get { return _autosizeFont; } set { _autosizeFont = value; } } 
 
		/// Gets or sets whether the font size should be adjusted based on whether the control is checked. 
		public bool AdjustTextPlacementWhenChecked { get { return _adjustTextPlacement; } set { _adjustTextPlacement = value; } } 
 
		/// Event raised before the control's checked state changes. 
		public event CancelEventHandler BeforeCheckedChanged; 
		/// Event raised after the control's checked state changes. 
		public event EventHandler CheckedChanged; 
 
		/// Cleans up. 
		/// Whether this is the result of a call to IDisposable.Dispose. 
		protected override void Dispose(bool disposing) 
		{ 
			if (disposing) 
			{ 
				if (_centeredFormat != null) 
				{ 
					_centeredFormat.Dispose(); 
					_centeredFormat = null; 
				} 
				if (_font != null) 
				{ 
					_font.Dispose(); 
					_font = null; 
				} 
			} 
			base.Dispose (disposing); 
		} 
 
		/// Handles the control's resize. 
		protected override void OnResize(EventArgs e) 
		{ 
			ClearFont(); 
			base.OnResize(e); 
		} 
 
		/// Handles the control's layout. 
		protected override void OnLayout(LayoutEventArgs levent) 
		{ 
			ClearFont(); 
			base.OnLayout (levent); 
		} 
 
		/// Handles the control's font change. 
		protected override void OnFontChanged(EventArgs e) 
		{ 
			ClearFont(); 
			base.OnFontChanged (e); 
		} 
 
		/// Handles the control's checked change. 
		private void OnCheckedChanged() 
		{ 
			ClearFont(); 
			EventHandler e = CheckedChanged; 
			if (e != null) e(this, EventArgs.Empty); 
		} 
 
		/// Raises the BeforeCheckedChanged event. 
		private void OnBeforeCheckedChanged(CancelEventArgs e) 
		{ 
			CancelEventHandler handler = BeforeCheckedChanged; 
			if (handler != null) handler(this, e); 
		} 
 
		/// Handles the mouse enter event. 
		protected override void OnMouseEnter(EventArgs e) 
		{ 
			base.OnMouseEnter (e); 
			_highlighted = true; 
			Invalidate(); 
		} 
 
		/// Handles the mouse leave event. 
		protected override void OnMouseLeave(EventArgs e) 
		{ 
			base.OnMouseLeave (e); 
			_highlighted = false; 
			Invalidate(); 
		} 
 
		/// Handles the mouse down event. 
		protected override void OnMouseDown(MouseEventArgs e) 
		{ 
			base.OnMouseDown (e); 
			if ((e.Button & MouseButtons.Left) == MouseButtons.Left) 
			{ 
				_highlighted = false; 
				_mouseDown = true; 
				if (_allowNonprogrammaticUnchecking || !Checked) Checked = !Checked; 
				if (Focusable) Select(); 
				Invalidate(); 
			} 
		} 
 
		/// Handles the mouse up event. 
		protected override void OnMouseUp(MouseEventArgs e) 
		{ 
			if ((e.Button & MouseButtons.Left) == MouseButtons.Left) 
			{ 
				_mouseDown = false; 
				if (!AllowChecking) 
				{ 
					Point mousePosition = Control.MousePosition; 
					if (ClientRectangle.Contains(PointToClient(mousePosition))) 
					{ 
						_highlighted = true; 
					} 
				} 
				Invalidate(); 
				if (ClientRectangle.Contains(PointToClient(MousePosition))) OnClick(e); 
			} 
			base.OnMouseUp (e); 
		} 
 
		/// Clears the currently cached font. 
		private void ClearFont() 
		{ 
			if (_font != null) 
			{ 
				Font f = _font; 
				_font = null; 
				f.Dispose(); 
			} 
		} 
 
		/// Gets the font to use for rendering. 
		/// The font to use for rendering. 
		private Font GetFontForText() 
		{ 
			if (!_autosizeFont) return base.Font; 
			if (_font == null)  
			{ 
				Font baseFont = base.Font; 
				if (Text == null || Text.Length == 0) return baseFont; 
				using(Graphics g = CreateGraphics()) 
				{ 
					int width = Width * 9 / 10; 
					int height = Height * 9 / 10; 
					_font = new Font(baseFont.FontFamily,  
						GraphicsHelpers.GetMaximumEMSize(Text, g, baseFont.FontFamily, baseFont.Style, width, height), baseFont.Style); 
				} 
			} 
			return _font; 
		} 
 
		protected override void OnKeyDown(KeyEventArgs e) 
		{ 
			if (e.KeyCode == Keys.Enter) 
			{ 
				OnClick(e); 
			} 
			else base.OnKeyDown (e); 
		} 
 
		protected override void OnGotFocus(EventArgs e) 
		{ 
			base.OnGotFocus (e); 
			Invalidate(); 
		} 
 
		protected override void OnLostFocus(EventArgs e) 
		{ 
			base.OnLostFocus (e); 
			Invalidate(); 
		} 
 
		/// Paints the button. 
		protected override void OnPaint(PaintEventArgs e) 
		{ 
			base.OnPaint(e); 
 
			// Setup the graphics object 
			e.Graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; 
			e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; 
 
			// Determine the image to be drawn based on the current state 
			Image img = _uncheckedImage; 
			if (Enabled && Visible) 
			{ 
				if (Checked || _mouseDown) img =  _checkedImage; 
				else if (_highlighted || this.Focused) img = _highlightedImage; 
			} 
			 
			if (img != null) 
			{ 
				using(ImageAttributes attrs = new ImageAttributes()) 
				{ 
					// If the control is disabled, use alpha blending 
					ColorMatrix m = new ColorMatrix(); 
					m.Matrix33 = Enabled && Visible ? 1f : .5f; 
					attrs.SetColorMatrix(m); 
 
					int width = ClientRectangle.Width; 
					int height = ClientRectangle.Height; 
 
					// Draw the shadow if appropriate 
					if (Enabled && Visible && !Checked && !MouseIsDown && _shadowImage != null) 
					{ 
						e.Graphics.DrawImage(_shadowImage, new Rectangle(1, 1, width, height), 
							0, 0, _shadowImage.Width, _shadowImage.Height, GraphicsUnit.Pixel, attrs); 
					} 
					width -= 2; 
					height -= 2; 
 
					// Draw the image 
					e.Graphics.DrawImage(img, new Rectangle(0, 0, width, height), 
						0, 0, img.Width, img.Height, GraphicsUnit.Pixel, attrs); 
 
					// Draw the icon if appropriate 
					if (_overlayImage != null) 
					{ 
						Rectangle targetRect = new Rectangle(0, 0, width, height); 
						targetRect.Inflate(-width/8, -height/8); 
						if (Enabled && Visible && (Checked || _mouseDown)) 
						{ 
							targetRect.Offset((int)Math.Ceiling(ClientRectangle.Height/20f), (int)Math.Ceiling(ClientRectangle.Height/20f)); 
							targetRect.Inflate((int)Math.Ceiling(-ClientRectangle.Height/30f), (int)Math.Ceiling(-ClientRectangle.Height/30f)); 
						} 
						e.Graphics.DrawImage(_overlayImage, targetRect, 
							0, 0, _overlayImage.Width, _overlayImage.Height, GraphicsUnit.Pixel, attrs); 
					} 
 
					// If there's text to display 
					if (Text != null && Text.Length > 0) 
					{ 
						RectangleF bounds = new RectangleF(0, 0, width, height); 
				 
						// Update the bounds if necessary 
						if (Enabled && Visible && AdjustTextPlacementWhenChecked && (Checked || MouseIsDown)) 
						{ 
							bounds.Inflate(-ClientRectangle.Height/6, -ClientRectangle.Height/6); 
							bounds.Offset(ClientRectangle.Height/15, ClientRectangle.Height/15); 
						} 
 
						// Get the color to use for the text, alpha blended if the control is disabled 
						Color c = ForeColor; 
						if (!Enabled || !Visible) c = Color.FromArgb(128, c); 
 
						// Draw it 
						using(Brush b = new SolidBrush(c)) // Can cache this for improved performance 
						{ 
							e.Graphics.DrawString(Text, GetFontForText(), b, bounds, _centeredFormat); 
						} 
					} 
				} 
			} 
		} 
	} 
}