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: 8 // 9 // The above copyright notice and this permission notice shall be 10 // included in all copies or substantial portions of the Software. 11 // 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. 19 // 20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) 21 // 22 // Authors: 23 // Peter Bartok (pbartok@novell.com) 24 // 25 // 26 27 using System; 28 using System.Collections; 29 using System.ComponentModel; 30 using System.Drawing; 31 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)] 37 [ComplexBindingProperties ("DataSource", "DataMember")] 38 public class ErrorProvider : Component, IExtenderProvider, ISupportInitialize 39 { 40 private class ErrorWindow : UserControl 41 { ErrorWindow()42 public ErrorWindow () 43 { 44 SetStyle (ControlStyles.Selectable, false); 45 } 46 } 47 48 #region Private Classes 49 private class ErrorProperty { 50 public ErrorIconAlignment alignment; 51 public int padding; 52 public string text; 53 public Control control; 54 public ErrorProvider ep; 55 private ErrorWindow window; 56 private bool visible; 57 private int blink_count; 58 private EventHandler tick; 59 private System.Windows.Forms.Timer timer; 60 ErrorProperty(ErrorProvider ep, Control control)61 public ErrorProperty(ErrorProvider ep, Control control) { 62 this.ep = ep; 63 this.control = control; 64 65 alignment = ErrorIconAlignment.MiddleRight; 66 padding = 0; 67 text = string.Empty; 68 blink_count = 0; 69 70 tick = new EventHandler(window_Tick); 71 72 window = new ErrorWindow (); 73 window.Visible = false; 74 window.Width = ep.icon.Width; 75 window.Height = ep.icon.Height; 76 77 // UIA Framework: Associate ErrorProvider with Control 78 ErrorProvider.OnUIAErrorProviderHookUp (ep, new ControlEventArgs (control)); 79 80 // UIA Framework: Generate event to associate UserControl with ErrorProvider 81 window.VisibleChanged += delegate (object sender, EventArgs args) { 82 if (window.Visible == true) 83 ErrorProvider.OnUIAControlHookUp (control, new ControlEventArgs (window)); 84 else 85 ErrorProvider.OnUIAControlUnhookUp (control, new ControlEventArgs (window)); 86 }; 87 88 if (control.Parent != null) { 89 // UIA Framework: Generate event to associate UserControl with ErrorProvider 90 ErrorProvider.OnUIAControlHookUp (control, new ControlEventArgs (window)); 91 control.Parent.Controls.Add(window); 92 control.Parent.Controls.SetChildIndex(window, control.Parent.Controls.IndexOf (control) + 1); 93 } 94 95 window.Paint += new PaintEventHandler(window_Paint); 96 window.MouseEnter += new EventHandler(window_MouseEnter); 97 window.MouseLeave += new EventHandler(window_MouseLeave); 98 control.SizeChanged += new EventHandler(control_SizeLocationChanged); 99 control.LocationChanged += new EventHandler(control_SizeLocationChanged); 100 control.ParentChanged += new EventHandler (control_ParentChanged); 101 // Do we want to block mouse clicks? if so we need a few more events handled 102 103 CalculateAlignment(); 104 } 105 106 public string Text { 107 get { 108 return text; 109 } 110 111 set { 112 if (value == null) 113 value = string.Empty; 114 115 bool differentError = text != value; 116 text = value; 117 118 if (text != String.Empty) { 119 window.Visible = true; 120 } else { 121 window.Visible = false; 122 return; 123 } 124 125 // even if blink style is NeverBlink we need it to allow 126 // the timer to elapse at least once to get the icon to 127 // display 128 if (differentError || ep.blinkstyle == ErrorBlinkStyle.AlwaysBlink) { 129 if (timer == null) { 130 timer = new System.Windows.Forms.Timer(); 131 timer.Tick += tick; 132 } 133 timer.Interval = ep.blinkrate; 134 blink_count = 0; 135 timer.Enabled = true; 136 } 137 } 138 } 139 140 public ErrorIconAlignment Alignment { 141 get { 142 return alignment; 143 } 144 145 set { 146 if (alignment != value) { 147 alignment = value; 148 CalculateAlignment(); 149 } 150 } 151 } 152 153 public int Padding { 154 get { 155 return padding; 156 } 157 158 set { 159 if (padding != value) { 160 padding = value; 161 CalculateAlignment(); 162 } 163 } 164 } 165 CalculateAlignment()166 private void CalculateAlignment() { 167 if (visible) { 168 visible = false; 169 ep.tooltip.Visible = false; 170 } 171 172 switch (alignment) { 173 case ErrorIconAlignment.TopLeft: { 174 window.Left = control.Left - ep.icon.Width - padding; 175 window.Top = control.Top; 176 break; 177 } 178 179 case ErrorIconAlignment.TopRight: { 180 window.Left = control.Left + control.Width + padding; 181 window.Top = control.Top; 182 break; 183 } 184 185 case ErrorIconAlignment.MiddleLeft: { 186 window.Left = control.Left - ep.icon.Width - padding; 187 window.Top = control.Top + (control.Height - ep.icon.Height) / 2; 188 break; 189 } 190 191 case ErrorIconAlignment.MiddleRight: { 192 window.Left = control.Left + control.Width + padding; 193 window.Top = control.Top + (control.Height - ep.icon.Height) / 2; 194 break; 195 } 196 197 case ErrorIconAlignment.BottomLeft: { 198 window.Left = control.Left - ep.icon.Width - padding; 199 window.Top = control.Top + control.Height - ep.icon.Height; 200 break; 201 } 202 203 case ErrorIconAlignment.BottomRight: { 204 window.Left = control.Left + control.Width + padding; 205 window.Top = control.Top + control.Height - ep.icon.Height; 206 break; 207 } 208 } 209 } 210 window_Paint(object sender, PaintEventArgs e)211 private void window_Paint(object sender, PaintEventArgs e) { 212 if (text != string.Empty) { 213 e.Graphics.DrawIcon(this.ep.icon, 0, 0); 214 } 215 } 216 window_MouseEnter(object sender, EventArgs e)217 private void window_MouseEnter(object sender, EventArgs e) { 218 if (!visible) { 219 Size size; 220 Point pt; 221 222 visible = true; 223 224 pt = Control.MousePosition; 225 226 size = ThemeEngine.Current.ToolTipSize(ep.tooltip, text); 227 ep.tooltip.Width = size.Width; 228 ep.tooltip.Height = size.Height; 229 ep.tooltip.Text = text; 230 231 if ((pt.X + size.Width) < SystemInformation.WorkingArea.Width) { 232 ep.tooltip.Left = pt.X; 233 } else { 234 ep.tooltip.Left = pt.X - size.Width; 235 } 236 237 if ((pt.Y + size.Height) < (SystemInformation.WorkingArea.Height - 16)) { 238 ep.tooltip.Top = pt.Y + 16; 239 } else { 240 ep.tooltip.Top = pt.Y - size.Height; 241 } 242 243 // UIA Framework: Associate Control with ToolTip, used on Popup events 244 ep.UIAControl = control; 245 246 ep.tooltip.Visible = true; 247 } 248 } 249 window_MouseLeave(object sender, EventArgs e)250 private void window_MouseLeave(object sender, EventArgs e) { 251 if (visible) { 252 visible = false; 253 ep.tooltip.Visible = false; 254 } 255 } 256 control_SizeLocationChanged(object sender, EventArgs e)257 private void control_SizeLocationChanged(object sender, EventArgs e) { 258 if (visible) { 259 visible = false; 260 ep.tooltip.Visible = false; 261 } 262 CalculateAlignment(); 263 } 264 control_ParentChanged(object sender, EventArgs e)265 private void control_ParentChanged (object sender, EventArgs e) 266 { 267 if (control.Parent != null) { 268 269 // UIA Framework: Generate event to disassociate UserControl with ErrorProvider 270 ErrorProvider.OnUIAControlUnhookUp (control, new ControlEventArgs (window)); 271 control.Parent.Controls.Add (window); 272 control.Parent.Controls.SetChildIndex (window, control.Parent.Controls.IndexOf (control) + 1); 273 274 // UIA Framework: Generate event to associate UserControl with ErrorProvider 275 ErrorProvider.OnUIAControlHookUp (control, new ControlEventArgs (window)); 276 } 277 } 278 window_Tick(object sender, EventArgs e)279 private void window_Tick(object sender, EventArgs e) { 280 if (timer.Enabled && control.IsHandleCreated && control.Visible) { 281 Graphics g; 282 283 blink_count++; 284 285 g = window.CreateGraphics(); 286 if ((blink_count % 2) == 0) { 287 g.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(window.Parent.BackColor), window.ClientRectangle); 288 } else { 289 g.DrawIcon(this.ep.icon, 0, 0); 290 } 291 g.Dispose(); 292 293 switch (ep.blinkstyle) { 294 case ErrorBlinkStyle.AlwaysBlink: 295 break; 296 case ErrorBlinkStyle.BlinkIfDifferentError: 297 if (blink_count > 10) 298 timer.Stop(); 299 break; 300 case ErrorBlinkStyle.NeverBlink: 301 timer.Stop (); 302 break; 303 } 304 305 if (blink_count == 11) 306 blink_count = 1; 307 } 308 } 309 } 310 #endregion 311 312 #region Local Variables 313 private int blinkrate; 314 private ErrorBlinkStyle blinkstyle; 315 private string datamember; 316 private object datasource; 317 private ContainerControl container; 318 private Icon icon; 319 private Hashtable controls; 320 private ToolTip.ToolTipWindow tooltip; 321 322 private bool right_to_left; 323 private object tag; 324 #endregion // Local Variables 325 326 #region Public Constructors ErrorProvider()327 public ErrorProvider() 328 { 329 controls = new Hashtable(); 330 331 blinkrate = 250; 332 blinkstyle = ErrorBlinkStyle.BlinkIfDifferentError; 333 334 icon = ResourceImageLoader.GetIcon ("errorProvider.ico"); 335 tooltip = new ToolTip.ToolTipWindow(); 336 337 //UIA Framework: Event used to indicate the ToolTip is shown/hidden. 338 tooltip.VisibleChanged += delegate (object sender, EventArgs args) { 339 if (tooltip.Visible == true) 340 OnUIAPopup (this, new PopupEventArgs (UIAControl, UIAControl, false, Size.Empty)); 341 else if (tooltip.Visible == false) 342 OnUIAUnPopup (this, new PopupEventArgs (UIAControl, UIAControl, false, Size.Empty)); 343 }; 344 } 345 ErrorProvider(ContainerControl parentControl)346 public ErrorProvider(ContainerControl parentControl) : this () 347 { 348 container = parentControl; 349 } 350 ErrorProvider(IContainer container)351 public ErrorProvider (IContainer container) : this () 352 { 353 container.Add (this); 354 } 355 #endregion // Public Constructors 356 357 #region Public Instance Properties 358 [DefaultValue(250)] 359 [RefreshProperties(RefreshProperties.Repaint)] 360 public int BlinkRate { 361 get { 362 return blinkrate; 363 } 364 365 set { 366 blinkrate = value; 367 } 368 } 369 370 [DefaultValue(ErrorBlinkStyle.BlinkIfDifferentError)] 371 public ErrorBlinkStyle BlinkStyle { 372 get { 373 return blinkstyle; 374 } 375 376 set { 377 blinkstyle = value; 378 } 379 } 380 381 [DefaultValue(null)] 382 public ContainerControl ContainerControl { 383 get { 384 return container; 385 } 386 387 set { 388 container = value; 389 } 390 } 391 392 [MonoTODO ("Stub, does nothing")] 393 [DefaultValue (null)] 394 [Editor ("System.Windows.Forms.Design.DataMemberListEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)] 395 public string DataMember { 396 get { 397 return datamember; 398 } 399 400 set { 401 datamember = value; 402 // FIXME - add binding magic and also update BindToDataAndErrors with it 403 } 404 } 405 406 [MonoTODO ("Stub, does nothing")] 407 [DefaultValue (null)] 408 [AttributeProvider (typeof (IListSource))] 409 public object DataSource { 410 get { 411 return datasource; 412 } 413 414 set { 415 datasource = value; 416 // FIXME - add binding magic and also update BindToDataAndErrors with it 417 } 418 } 419 420 [Localizable(true)] 421 public Icon Icon { 422 get { 423 return icon; 424 } 425 426 set { 427 if (value != null && (value.Height != 16 || value.Width != 16)) 428 icon = new Icon (value, 16, 16); 429 else 430 icon = value; 431 } 432 } 433 434 public override ISite Site { 435 set { 436 base.Site = value; 437 } 438 } 439 440 [MonoTODO ("RTL not supported")] 441 [Localizable (true)] 442 [DefaultValue (false)] 443 public virtual bool RightToLeft { 444 get { return right_to_left; } 445 set { right_to_left = value; } 446 } 447 448 [Localizable (false)] 449 [Bindable (true)] 450 [TypeConverter (typeof (StringConverter))] 451 [DefaultValue (null)] 452 [MWFCategory ("Data")] 453 public object Tag { 454 get { return this.tag; } 455 set { this.tag = value; } 456 } 457 #endregion // Public Instance Properties 458 459 #region Public Instance Methods 460 [MonoTODO ("Stub, does nothing")] BindToDataAndErrors(object newDataSource, string newDataMember)461 public void BindToDataAndErrors (object newDataSource, string newDataMember) 462 { 463 datasource = newDataSource; 464 datamember = newDataMember; 465 // FIXME - finish 466 } 467 CanExtend(object extendee)468 public bool CanExtend(object extendee) { 469 if (!(extendee is Control)) { 470 return false; 471 } 472 473 if ((extendee is Form) || (extendee is ToolBar)) { 474 return false; 475 } 476 477 return true; 478 } 479 Clear()480 public void Clear () 481 { 482 foreach (ErrorProperty ep in controls.Values) 483 ep.Text = string.Empty; 484 } 485 486 [Localizable(true)] 487 [DefaultValue("")] GetError(Control control)488 public string GetError(Control control) { 489 return GetErrorProperty(control).Text; 490 } 491 492 [Localizable(true)] 493 [DefaultValue(ErrorIconAlignment.MiddleRight)] GetIconAlignment(Control control)494 public ErrorIconAlignment GetIconAlignment(Control control) { 495 return GetErrorProperty(control).Alignment; 496 } 497 498 [Localizable(true)] 499 [DefaultValue(0)] GetIconPadding(Control control)500 public int GetIconPadding(Control control) { 501 return GetErrorProperty(control).padding; 502 } 503 SetError(Control control, string value)504 public void SetError(Control control, string value) { 505 GetErrorProperty(control).Text = value; 506 } 507 SetIconAlignment(Control control, ErrorIconAlignment value)508 public void SetIconAlignment(Control control, ErrorIconAlignment value) { 509 GetErrorProperty(control).Alignment = value; 510 } 511 SetIconPadding(Control control, int padding)512 public void SetIconPadding(Control control, int padding) { 513 GetErrorProperty(control).Padding = padding; 514 } 515 516 [MonoTODO ("Stub, does nothing")] UpdateBinding()517 public void UpdateBinding () 518 { 519 } 520 #endregion // Public Instance Methods 521 522 #region Protected Instance Methods Dispose(bool disposing)523 protected override void Dispose(bool disposing) { 524 base.Dispose (disposing); 525 } 526 527 [EditorBrowsableAttribute (EditorBrowsableState.Advanced)] OnRightToLeftChanged(EventArgs e)528 protected virtual void OnRightToLeftChanged (EventArgs e) 529 { 530 EventHandler eh = (EventHandler)(Events[RightToLeftChangedEvent]); 531 if (eh != null) 532 eh (this, e); 533 } 534 #endregion // Protected Instance Methods 535 536 #region Private Methods GetErrorProperty(Control control)537 private ErrorProperty GetErrorProperty(Control control) { 538 ErrorProperty ep = (ErrorProperty)controls[control]; 539 if (ep == null) { 540 ep = new ErrorProperty(this, control); 541 controls[control] = ep; 542 } 543 return ep; 544 } 545 #endregion // Private Methods 546 ISupportInitialize.BeginInit()547 void ISupportInitialize.BeginInit () 548 { 549 } 550 ISupportInitialize.EndInit()551 void ISupportInitialize.EndInit () 552 { 553 } 554 555 #region Public Events 556 static object RightToLeftChangedEvent = new object (); 557 558 public event EventHandler RightToLeftChanged { 559 add { Events.AddHandler (RightToLeftChangedEvent, value); } 560 remove { Events.RemoveHandler (RightToLeftChangedEvent, value); } 561 } 562 #endregion 563 564 #region UIA Framework: Events, Properties and Methods 565 // NOTE: 566 // We are using Reflection to add/remove internal events. 567 // Class ToolTipListener uses the events. 568 // 569 // - UIAControlHookUp. Event used to associate UserControl with ErrorProvider 570 // - UIAControlUnhookUp. Event used to disassociate UserControl with ErrorProvider 571 // - UIAErrorProviderHookUp. Event used to associate Control with ErrorProvider 572 // - UIAErrorProviderUnhookUp. Event used to disassociate Control with ErrorProvider 573 // - UIAPopup. Event used show Popup 574 // - UIAUnPopup. Event used to hide popup. 575 576 private Control uia_control; 577 578 internal Control UIAControl { 579 get { return uia_control; } 580 set { uia_control = value; } 581 } 582 583 internal Rectangle UIAToolTipRectangle { 584 get { return tooltip.Bounds; } 585 } 586 587 internal static event ControlEventHandler UIAControlHookUp; 588 internal static event ControlEventHandler UIAControlUnhookUp; 589 internal static event ControlEventHandler UIAErrorProviderHookUp; 590 internal static event ControlEventHandler UIAErrorProviderUnhookUp; 591 internal static event PopupEventHandler UIAPopup; 592 internal static event PopupEventHandler UIAUnPopup; 593 OnUIAPopup(ErrorProvider sender, PopupEventArgs args)594 internal static void OnUIAPopup (ErrorProvider sender, PopupEventArgs args) 595 { 596 if (UIAPopup != null) 597 UIAPopup (sender, args); 598 } 599 OnUIAUnPopup(ErrorProvider sender, PopupEventArgs args)600 internal static void OnUIAUnPopup (ErrorProvider sender, PopupEventArgs args) 601 { 602 if (UIAUnPopup != null) 603 UIAUnPopup (sender, args); 604 } 605 OnUIAControlHookUp(object sender, ControlEventArgs args)606 internal static void OnUIAControlHookUp (object sender, ControlEventArgs args) 607 { 608 if (UIAControlHookUp != null) 609 UIAControlHookUp (sender, args); 610 } 611 OnUIAControlUnhookUp(object sender, ControlEventArgs args)612 internal static void OnUIAControlUnhookUp (object sender, ControlEventArgs args) 613 { 614 if (UIAControlUnhookUp != null) 615 UIAControlUnhookUp (sender, args); 616 } 617 OnUIAErrorProviderHookUp(object sender, ControlEventArgs args)618 internal static void OnUIAErrorProviderHookUp (object sender, ControlEventArgs args) 619 { 620 if (UIAErrorProviderHookUp != null) 621 UIAErrorProviderHookUp (sender, args); 622 } 623 OnUIAErrorProviderUnhookUp(object sender, ControlEventArgs args)624 internal static void OnUIAErrorProviderUnhookUp (object sender, ControlEventArgs args) 625 { 626 if (UIAErrorProviderUnhookUp != null) 627 UIAErrorProviderUnhookUp (sender, args); 628 } 629 #endregion 630 } 631 } 632