1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
23 // Peter Bartok (pbartok@novell.com)
28 using System.Collections;
29 using System.ComponentModel;
32 namespace System.Windows.Forms {
33 [ToolboxItemFilter("System.Windows.Forms")]
34 [ProvideProperty("IconAlignment", "System.Windows.Forms.Control, " + Consts.AssemblySystem_Windows_Forms)]
35 [ProvideProperty("IconPadding", "System.Windows.Forms.Control, " + Consts.AssemblySystem_Windows_Forms)]
36 [ProvideProperty("Error", "System.Windows.Forms.Control, " + Consts.AssemblySystem_Windows_Forms)]
38 [ComplexBindingProperties ("DataSource", "DataMember")]
40 public class ErrorProvider : Component, IExtenderProvider
45 private class ErrorWindow : UserControl
49 SetStyle (ControlStyles.Selectable, false);
53 #region Private Classes
54 private class ErrorProperty {
55 public ErrorIconAlignment alignment;
58 public Control control;
59 public ErrorProvider ep;
60 private ErrorWindow window;
62 private int blink_count;
63 private EventHandler tick;
64 private System.Windows.Forms.Timer timer;
66 public ErrorProperty(ErrorProvider ep, Control control) {
68 this.control = control;
70 alignment = ErrorIconAlignment.MiddleRight;
75 tick = new EventHandler(window_Tick);
77 window = new ErrorWindow ();
78 window.Visible = false;
79 window.Width = ep.icon.Width;
80 window.Height = ep.icon.Height;
82 if (ep.container != null) {
83 ep.container.Controls.Add(window);
84 ep.container.Controls.SetChildIndex(window, 0);
85 } else if (control.Parent != null) {
86 control.Parent.Controls.Add(window);
87 control.Parent.Controls.SetChildIndex(window, 0);
90 window.Paint += new PaintEventHandler(window_Paint);
91 window.MouseEnter += new EventHandler(window_MouseEnter);
92 window.MouseLeave += new EventHandler(window_MouseLeave);
93 control.SizeChanged += new EventHandler(control_SizeLocationChanged);
94 control.LocationChanged += new EventHandler(control_SizeLocationChanged);
95 control.ParentChanged += new EventHandler (control_ParentChanged);
96 // Do we want to block mouse clicks? if so we need a few more events handled
108 value = string.Empty;
110 bool differentError = text != value;
113 if (text != String.Empty) {
114 window.Visible = true;
116 window.Visible = false;
120 // even if blink style is NeverBlink we need it to allow
121 // the timer to elapse at least once to get the icon to
123 if (differentError || ep.blinkstyle == ErrorBlinkStyle.AlwaysBlink) {
125 timer = new System.Windows.Forms.Timer();
128 timer.Interval = ep.blinkrate;
130 timer.Enabled = true;
135 public ErrorIconAlignment Alignment {
141 if (alignment != value) {
143 CalculateAlignment();
154 if (padding != value) {
156 CalculateAlignment();
161 private void CalculateAlignment() {
164 ep.tooltip.Visible = false;
168 case ErrorIconAlignment.TopLeft: {
169 window.Left = control.Left - ep.icon.Width - padding;
170 window.Top = control.Top;
174 case ErrorIconAlignment.TopRight: {
175 window.Left = control.Left + control.Width + padding;
176 window.Top = control.Top;
180 case ErrorIconAlignment.MiddleLeft: {
181 window.Left = control.Left - ep.icon.Width - padding;
182 window.Top = control.Top + (control.Height - ep.icon.Height) / 2;
186 case ErrorIconAlignment.MiddleRight: {
187 window.Left = control.Left + control.Width + padding;
188 window.Top = control.Top + (control.Height - ep.icon.Height) / 2;
192 case ErrorIconAlignment.BottomLeft: {
193 window.Left = control.Left - ep.icon.Width - padding;
194 window.Top = control.Top + control.Height - ep.icon.Height;
198 case ErrorIconAlignment.BottomRight: {
199 window.Left = control.Left + control.Width + padding;
200 window.Top = control.Top + control.Height - ep.icon.Height;
206 private void window_Paint(object sender, PaintEventArgs e) {
207 if (text != string.Empty) {
208 e.Graphics.DrawIcon(this.ep.icon, 0, 0);
212 private void window_MouseEnter(object sender, EventArgs e) {
219 pt = Control.MousePosition;
221 size = ThemeEngine.Current.ToolTipSize(ep.tooltip, text);
222 ep.tooltip.Width = size.Width;
223 ep.tooltip.Height = size.Height;
224 ep.tooltip.Text = text;
226 if ((pt.X + size.Width) < SystemInformation.WorkingArea.Width) {
227 ep.tooltip.Left = pt.X;
229 ep.tooltip.Left = pt.X - size.Width;
232 if ((pt.Y + size.Height) < (SystemInformation.WorkingArea.Height - 16)) {
233 ep.tooltip.Top = pt.Y + 16;
235 ep.tooltip.Top = pt.Y - size.Height;
237 ep.tooltip.Visible = true;
241 private void window_MouseLeave(object sender, EventArgs e) {
244 ep.tooltip.Visible = false;
248 private void control_SizeLocationChanged(object sender, EventArgs e) {
251 ep.tooltip.Visible = false;
253 CalculateAlignment();
256 private void control_ParentChanged (object sender, EventArgs e)
258 if (ep.container != null)
260 control.Parent.Controls.Add (window);
261 control.Parent.Controls.SetChildIndex (window, 0);
264 private void window_Tick(object sender, EventArgs e) {
265 if (timer.Enabled && control.IsHandleCreated && control.Visible) {
270 g = window.CreateGraphics();
271 if ((blink_count % 2) == 0) {
272 g.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(window.Parent.BackColor), window.ClientRectangle);
274 g.DrawIcon(this.ep.icon, 0, 0);
278 switch (ep.blinkstyle) {
279 case ErrorBlinkStyle.AlwaysBlink:
281 case ErrorBlinkStyle.BlinkIfDifferentError:
282 if (blink_count > 10)
285 case ErrorBlinkStyle.NeverBlink:
290 if (blink_count == 11)
297 #region Local Variables
298 private int blinkrate;
299 private ErrorBlinkStyle blinkstyle;
300 private string datamember;
301 private object datasource;
302 private ContainerControl container;
304 private Hashtable controls;
305 private ToolTip.ToolTipWindow tooltip;
310 #endregion // Local Variables
312 #region Public Constructors
313 public ErrorProvider()
315 controls = new Hashtable();
318 blinkstyle = ErrorBlinkStyle.BlinkIfDifferentError;
320 icon = (Icon)Locale.GetResource("errorProvider.ico");
321 tooltip = new ToolTip.ToolTipWindow();
324 public ErrorProvider(ContainerControl parentControl) : this ()
326 container = parentControl;
330 public ErrorProvider (IContainer container) : this ()
332 container.Add (this);
335 #endregion // Public Constructors
337 #region Public Instance Properties
339 [RefreshProperties(RefreshProperties.Repaint)]
340 public int BlinkRate {
350 [DefaultValue(ErrorBlinkStyle.BlinkIfDifferentError)]
351 public ErrorBlinkStyle BlinkStyle {
362 public ContainerControl ContainerControl {
374 [Editor ("System.Windows.Forms.Design.DataMemberListEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
375 public string DataMember {
382 // FIXME - add binding magic and also update BindToDataAndErrors with it
389 [AttributeProvider (typeof (IListSource))]
391 [TypeConverter("System.Windows.Forms.Design.DataSourceConverter, " + Consts.AssemblySystem_Design)]
393 public object DataSource {
400 // FIXME - add binding magic and also update BindToDataAndErrors with it
415 public override ISite Site {
422 [Localizable (false)]
424 [TypeConverter (typeof (StringConverter))]
425 [DefaultValue (null)]
426 [MWFCategory ("Data")]
428 get { return this.tag; }
429 set { this.tag = value; }
432 #endregion // Public Instance Properties
434 #region Public Instance Methods
436 public void BindToDataAndErrors(object newDataSource, string newDataMember) {
437 datasource = newDataSource;
438 datamember = newDataMember;
442 public bool CanExtend(object extendee) {
443 if (!(extendee is Control)) {
447 if ((extendee is Form) || (extendee is ToolBar)) {
456 public string GetError(Control control) {
457 return GetErrorProperty(control).Text;
461 [DefaultValue(ErrorIconAlignment.MiddleRight)]
462 public ErrorIconAlignment GetIconAlignment(Control control) {
463 return GetErrorProperty(control).Alignment;
468 public int GetIconPadding(Control control) {
469 return GetErrorProperty(control).padding;
472 public void SetError(Control control, string value) {
473 GetErrorProperty(control).Text = value;
476 public void SetIconAlignment(Control control, ErrorIconAlignment value) {
477 GetErrorProperty(control).Alignment = value;
480 public void SetIconPadding(Control control, int padding) {
481 GetErrorProperty(control).Padding = padding;
485 public void UpdateBinding() {
487 #endregion // Public Instance Methods
489 #region Protected Instance Methods
490 protected override void Dispose(bool disposing) {
491 base.Dispose (disposing);
493 #endregion // Protected Instance Methods
495 #region Private Methods
496 private ErrorProperty GetErrorProperty(Control control) {
497 ErrorProperty ep = (ErrorProperty)controls[control];
499 ep = new ErrorProperty(this, control);
500 controls[control] = ep;
504 #endregion // Private Methods
507 void ISupportInitialize.BeginInit ()
511 void ISupportInitialize.EndInit ()