// Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) // // Authors: // Peter Bartok (pbartok@novell.com) // // using System; using System.Collections; using System.ComponentModel; using System.Drawing; namespace System.Windows.Forms { [ToolboxItemFilter("System.Windows.Forms")] [ProvideProperty("IconAlignment", "System.Windows.Forms.Control, " + Consts.AssemblySystem_Windows_Forms)] [ProvideProperty("IconPadding", "System.Windows.Forms.Control, " + Consts.AssemblySystem_Windows_Forms)] [ProvideProperty("Error", "System.Windows.Forms.Control, " + Consts.AssemblySystem_Windows_Forms)] #if NET_2_0 [ComplexBindingProperties ("DataSource", "DataMember")] #endif public class ErrorProvider : Component, IExtenderProvider #if NET_2_0 , ISupportInitialize #endif { private class ErrorWindow : UserControl { public ErrorWindow () { SetStyle (ControlStyles.Selectable, false); } } #region Private Classes private class ErrorProperty { public ErrorIconAlignment alignment; public int padding; public string text; public Control control; public ErrorProvider ep; private ErrorWindow window; private bool visible; private int blink_count; private EventHandler tick; private System.Windows.Forms.Timer timer; public ErrorProperty(ErrorProvider ep, Control control) { this.ep = ep; this.control = control; alignment = ErrorIconAlignment.MiddleRight; padding = 0; text = string.Empty; blink_count = 0; tick = new EventHandler(window_Tick); window = new ErrorWindow (); window.Visible = false; window.Width = ep.icon.Width; window.Height = ep.icon.Height; if (ep.container != null) { ep.container.Controls.Add(window); ep.container.Controls.SetChildIndex(window, 0); } else if (control.Parent != null) { control.Parent.Controls.Add(window); control.Parent.Controls.SetChildIndex(window, 0); } window.Paint += new PaintEventHandler(window_Paint); window.MouseEnter += new EventHandler(window_MouseEnter); window.MouseLeave += new EventHandler(window_MouseLeave); control.SizeChanged += new EventHandler(control_SizeLocationChanged); control.LocationChanged += new EventHandler(control_SizeLocationChanged); control.ParentChanged += new EventHandler (control_ParentChanged); // Do we want to block mouse clicks? if so we need a few more events handled CalculateAlignment(); } public string Text { get { return text; } set { if (value == null) value = string.Empty; bool differentError = text != value; text = value; if (text != String.Empty) { window.Visible = true; } else { window.Visible = false; return; } // even if blink style is NeverBlink we need it to allow // the timer to elapse at least once to get the icon to // display if (differentError || ep.blinkstyle == ErrorBlinkStyle.AlwaysBlink) { if (timer == null) { timer = new System.Windows.Forms.Timer(); timer.Tick += tick; } timer.Interval = ep.blinkrate; blink_count = 0; timer.Enabled = true; } } } public ErrorIconAlignment Alignment { get { return alignment; } set { if (alignment != value) { alignment = value; CalculateAlignment(); } } } public int Padding { get { return padding; } set { if (padding != value) { padding = value; CalculateAlignment(); } } } private void CalculateAlignment() { if (visible) { visible = false; ep.tooltip.Visible = false; } switch (alignment) { case ErrorIconAlignment.TopLeft: { window.Left = control.Left - ep.icon.Width - padding; window.Top = control.Top; break; } case ErrorIconAlignment.TopRight: { window.Left = control.Left + control.Width + padding; window.Top = control.Top; break; } case ErrorIconAlignment.MiddleLeft: { window.Left = control.Left - ep.icon.Width - padding; window.Top = control.Top + (control.Height - ep.icon.Height) / 2; break; } case ErrorIconAlignment.MiddleRight: { window.Left = control.Left + control.Width + padding; window.Top = control.Top + (control.Height - ep.icon.Height) / 2; break; } case ErrorIconAlignment.BottomLeft: { window.Left = control.Left - ep.icon.Width - padding; window.Top = control.Top + control.Height - ep.icon.Height; break; } case ErrorIconAlignment.BottomRight: { window.Left = control.Left + control.Width + padding; window.Top = control.Top + control.Height - ep.icon.Height; break; } } } private void window_Paint(object sender, PaintEventArgs e) { if (text != string.Empty) { e.Graphics.DrawIcon(this.ep.icon, 0, 0); } } private void window_MouseEnter(object sender, EventArgs e) { if (!visible) { Size size; Point pt; visible = true; pt = Control.MousePosition; size = ThemeEngine.Current.ToolTipSize(ep.tooltip, text); ep.tooltip.Width = size.Width; ep.tooltip.Height = size.Height; ep.tooltip.Text = text; if ((pt.X + size.Width) < SystemInformation.WorkingArea.Width) { ep.tooltip.Left = pt.X; } else { ep.tooltip.Left = pt.X - size.Width; } if ((pt.Y + size.Height) < (SystemInformation.WorkingArea.Height - 16)) { ep.tooltip.Top = pt.Y + 16; } else { ep.tooltip.Top = pt.Y - size.Height; } ep.tooltip.Visible = true; } } private void window_MouseLeave(object sender, EventArgs e) { if (visible) { visible = false; ep.tooltip.Visible = false; } } private void control_SizeLocationChanged(object sender, EventArgs e) { if (visible) { visible = false; ep.tooltip.Visible = false; } CalculateAlignment(); } private void control_ParentChanged (object sender, EventArgs e) { if (ep.container != null) return; control.Parent.Controls.Add (window); control.Parent.Controls.SetChildIndex (window, 0); } private void window_Tick(object sender, EventArgs e) { if (timer.Enabled && control.IsHandleCreated && control.Visible) { Graphics g; blink_count++; g = window.CreateGraphics(); if ((blink_count % 2) == 0) { g.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(window.Parent.BackColor), window.ClientRectangle); } else { g.DrawIcon(this.ep.icon, 0, 0); } g.Dispose(); switch (ep.blinkstyle) { case ErrorBlinkStyle.AlwaysBlink: break; case ErrorBlinkStyle.BlinkIfDifferentError: if (blink_count > 10) timer.Stop(); break; case ErrorBlinkStyle.NeverBlink: timer.Stop (); break; } if (blink_count == 11) blink_count = 1; } } } #endregion #region Local Variables private int blinkrate; private ErrorBlinkStyle blinkstyle; private string datamember; private object datasource; private ContainerControl container; private Icon icon; private Hashtable controls; private ToolTip.ToolTipWindow tooltip; #if NET_2_0 private object tag; #endif #endregion // Local Variables #region Public Constructors public ErrorProvider() { controls = new Hashtable(); blinkrate = 250; blinkstyle = ErrorBlinkStyle.BlinkIfDifferentError; icon = (Icon)Locale.GetResource("errorProvider.ico"); tooltip = new ToolTip.ToolTipWindow(); } public ErrorProvider(ContainerControl parentControl) : this () { container = parentControl; } #if NET_2_0 public ErrorProvider (IContainer container) : this () { container.Add (this); } #endif #endregion // Public Constructors #region Public Instance Properties [DefaultValue(250)] [RefreshProperties(RefreshProperties.Repaint)] public int BlinkRate { get { return blinkrate; } set { blinkrate = value; } } [DefaultValue(ErrorBlinkStyle.BlinkIfDifferentError)] public ErrorBlinkStyle BlinkStyle { get { return blinkstyle; } set { blinkstyle = value; } } [DefaultValue(null)] public ContainerControl ContainerControl { get { return container; } set { container = value; } } [MonoTODO] [DefaultValue(null)] [Editor ("System.Windows.Forms.Design.DataMemberListEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)] public string DataMember { get { return datamember; } set { datamember = value; // FIXME - add binding magic and also update BindToDataAndErrors with it } } [MonoTODO] [DefaultValue(null)] #if NET_2_0 [AttributeProvider (typeof (IListSource))] #else [TypeConverter("System.Windows.Forms.Design.DataSourceConverter, " + Consts.AssemblySystem_Design)] #endif public object DataSource { get { return datasource; } set { datasource = value; // FIXME - add binding magic and also update BindToDataAndErrors with it } } [Localizable(true)] public Icon Icon { get { return icon; } set { icon = value; } } public override ISite Site { set { base.Site = value; } } #if NET_2_0 [Localizable (false)] [Bindable (true)] [TypeConverter (typeof (StringConverter))] [DefaultValue (null)] [MWFCategory ("Data")] public object Tag { get { return this.tag; } set { this.tag = value; } } #endif #endregion // Public Instance Properties #region Public Instance Methods [MonoTODO] public void BindToDataAndErrors(object newDataSource, string newDataMember) { datasource = newDataSource; datamember = newDataMember; // FIXME - finish } public bool CanExtend(object extendee) { if (!(extendee is Control)) { return false; } if ((extendee is Form) || (extendee is ToolBar)) { return false; } return true; } [Localizable(true)] [DefaultValue("")] public string GetError(Control control) { return GetErrorProperty(control).Text; } [Localizable(true)] [DefaultValue(ErrorIconAlignment.MiddleRight)] public ErrorIconAlignment GetIconAlignment(Control control) { return GetErrorProperty(control).Alignment; } [Localizable(true)] [DefaultValue(0)] public int GetIconPadding(Control control) { return GetErrorProperty(control).padding; } public void SetError(Control control, string value) { GetErrorProperty(control).Text = value; } public void SetIconAlignment(Control control, ErrorIconAlignment value) { GetErrorProperty(control).Alignment = value; } public void SetIconPadding(Control control, int padding) { GetErrorProperty(control).Padding = padding; } [MonoTODO] public void UpdateBinding() { } #endregion // Public Instance Methods #region Protected Instance Methods protected override void Dispose(bool disposing) { base.Dispose (disposing); } #endregion // Protected Instance Methods #region Private Methods private ErrorProperty GetErrorProperty(Control control) { ErrorProperty ep = (ErrorProperty)controls[control]; if (ep == null) { ep = new ErrorProperty(this, control); controls[control] = ep; } return ep; } #endregion // Private Methods #if NET_2_0 void ISupportInitialize.BeginInit () { } void ISupportInitialize.EndInit () { } #endif } }