Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / WinForms / Managed / System / WinForms / ErrorProvider.cs / 2 / ErrorProvider.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Windows.Forms { using System.Threading; using System.Runtime.Remoting; using System.Runtime.InteropServices; using System.ComponentModel; using System.ComponentModel.Design; using System.Diagnostics; using System; using System.Collections; using System.Globalization; using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.Design; using System.Windows.Forms.Internal; using System.Security.Permissions; ////// /// ErrorProvider presents a simple user interface for indicating to the /// user that a control on a form has an error associated with it. If a /// error description string is specified for the control, then an icon /// will appear next to the control, and when the mouse hovers over the /// icon, a tooltip will appear showing the error description string. /// [ ProvideProperty("IconPadding", typeof(Control)), ProvideProperty("IconAlignment", typeof(Control)), ProvideProperty("Error", typeof(Control)), ToolboxItemFilter("System.Windows.Forms"), ComplexBindingProperties("DataSource", "DataMember"), SRDescription(SR.DescriptionErrorProvider) ] public class ErrorProvider : Component, IExtenderProvider, ISupportInitialize { // // FIELDS // Hashtable items = new Hashtable(); Hashtable windows = new Hashtable(); Icon icon = DefaultIcon; IconRegion region; int itemIdCounter; int blinkRate; ErrorBlinkStyle blinkStyle; bool showIcon = true; // used for blinking private bool inSetErrorManager = false; private bool setErrorManagerOnEndInit = false; private bool initializing = false; [ThreadStatic] static Icon defaultIcon = null; const int defaultBlinkRate = 250; const ErrorBlinkStyle defaultBlinkStyle = ErrorBlinkStyle.BlinkIfDifferentError; const ErrorIconAlignment defaultIconAlignment = ErrorIconAlignment.MiddleRight; // data binding private ContainerControl parentControl; private object dataSource = null; private string dataMember = null; private BindingManagerBase errorManager; private EventHandler currentChanged; // listen to the OnPropertyChanged event in the ContainerControl private EventHandler propChangedEvent; private EventHandler onRightToLeftChanged; private bool rightToLeft = false; private object userData; // // CONSTRUCTOR // ////// /// Default constructor. /// public ErrorProvider() { icon = DefaultIcon; blinkRate = defaultBlinkRate; blinkStyle = defaultBlinkStyle; currentChanged = new EventHandler(ErrorManager_CurrentChanged); } ////// /// public ErrorProvider(ContainerControl parentControl) : this() { this.parentControl = parentControl; propChangedEvent = new EventHandler(ParentControl_BindingContextChanged); parentControl.BindingContextChanged += propChangedEvent; } ///[To be supplied.] ////// /// public ErrorProvider(IContainer container) : this() { container.Add(this); } // // PROPERTIES // ///[To be supplied.] ///public override ISite Site { set { base.Site = value; if (value == null) return; IDesignerHost host = value.GetService(typeof(IDesignerHost)) as IDesignerHost; if (host != null) { IComponent baseComp = host.RootComponent; if (baseComp is ContainerControl) { this.ContainerControl = (ContainerControl) baseComp; } } } } /// /// /// Returns or sets when the error icon flashes. /// [ SRCategory(SR.CatBehavior), DefaultValue(defaultBlinkStyle), SRDescription(SR.ErrorProviderBlinkStyleDescr) ] public ErrorBlinkStyle BlinkStyle { get { if (blinkRate == 0) { return ErrorBlinkStyle.NeverBlink; } return blinkStyle; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)ErrorBlinkStyle.BlinkIfDifferentError, (int)ErrorBlinkStyle.NeverBlink)){ throw new InvalidEnumArgumentException("value", (int)value, typeof(ErrorBlinkStyle)); } // If the blinkRate == 0, then set blinkStyle = neverBlink // if (this.blinkRate == 0) { value = ErrorBlinkStyle.NeverBlink; } if (this.blinkStyle == value) { return; } if (value == ErrorBlinkStyle.AlwaysBlink) { // we need to startBlinking on all the controlItems // in our items hashTable. this.showIcon = true; this.blinkStyle = ErrorBlinkStyle.AlwaysBlink; foreach (ErrorWindow w in windows.Values) { w.StartBlinking(); } } else if (blinkStyle == ErrorBlinkStyle.AlwaysBlink) { // we need to stop blinking... this.blinkStyle = value; foreach (ErrorWindow w in windows.Values) { w.StopBlinking(); } } else { this.blinkStyle = value; } } } ////// /// Indicates what container control (usually the form) should be inspected for bindings. /// A binding will reveal what control to place errors on for a /// error in the current row in the DataSource/DataMember pair. /// [ DefaultValue(null), SRCategory(SR.CatData), SRDescription(SR.ErrorProviderContainerControlDescr) ] public ContainerControl ContainerControl { [UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)] [UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)] get { return parentControl; } set { if (parentControl != value) { if (parentControl != null) parentControl.BindingContextChanged -= propChangedEvent; parentControl = value; if (parentControl != null) parentControl.BindingContextChanged += propChangedEvent; Set_ErrorManager(this.DataSource, this.DataMember, true); } } } ////// /// This is used for international applications where the language /// is written from RightToLeft. When this property is true, // text will be from right to left. /// [ SRCategory(SR.CatAppearance), Localizable(true), DefaultValue(false), SRDescription(SR.ControlRightToLeftDescr) ] public virtual bool RightToLeft { get { return rightToLeft; } set { if (value != rightToLeft) { rightToLeft = value; OnRightToLeftChanged(EventArgs.Empty); } } } ////// /// [SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftChangedDescr)] public event EventHandler RightToLeftChanged { add { onRightToLeftChanged += value; } remove { onRightToLeftChanged -= value; } } ///[To be supplied.] ////// /// User defined data associated with the control. /// [ SRCategory(SR.CatData), Localizable(false), Bindable(true), SRDescription(SR.ControlTagDescr), DefaultValue(null), TypeConverter(typeof(StringConverter)), ] public object Tag { get { return userData; } set { userData = value; } } private void Set_ErrorManager(object newDataSource, string newDataMember, bool force) { if (inSetErrorManager) return; inSetErrorManager = true; try { bool dataSourceChanged = this.DataSource != newDataSource; bool dataMemberChanged = this.DataMember != newDataMember; //if nothing changed, then do not do any work // if (!dataSourceChanged && !dataMemberChanged && !force) { return; } // set the dataSource and the dataMember // this.dataSource = newDataSource; this.dataMember = newDataMember; if (initializing) { setErrorManagerOnEndInit = true; } else { // unwire the errorManager: // UnwireEvents(errorManager); // get the new errorManager // if (parentControl != null && this.dataSource != null && parentControl.BindingContext != null) { errorManager = parentControl.BindingContext[this.dataSource, this.dataMember]; } else { errorManager = null; } // wire the events // WireEvents(errorManager); // see if there are errors at the current // item in the list, w/o waiting for the position to change if (errorManager != null) UpdateBinding(); } } finally { inSetErrorManager = false; } } ////// /// Indicates the source of data to bind errors against. /// [ DefaultValue(null), SRCategory(SR.CatData), AttributeProvider(typeof(IListSource)), SRDescription(SR.ErrorProviderDataSourceDescr) ] public object DataSource { get { return dataSource; } set { if (parentControl != null && value != null && !String.IsNullOrEmpty(this.dataMember)) { // Let's check if the datamember exists in the new data source try { errorManager = parentControl.BindingContext[value, this.dataMember]; } catch (ArgumentException) { // The data member doesn't exist in the data source, so set it to null this.dataMember = ""; } } Set_ErrorManager(value, this.DataMember, false); } } ////// /// private bool ShouldSerializeDataSource() { return (dataSource != null); } ///[To be supplied.] ////// /// Indicates the sub-list of data from the DataSource to bind errors against. /// [ DefaultValue(null), SRCategory(SR.CatData), Editor("System.Windows.Forms.Design.DataMemberListEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)), SRDescription(SR.ErrorProviderDataMemberDescr) ] public string DataMember { get { return dataMember; } set { if (value == null) value = ""; Set_ErrorManager(this.DataSource, value, false); } } ////// /// private bool ShouldSerializeDataMember() { return (dataMember != null && dataMember.Length != 0); } ///[To be supplied.] ////// /// public void BindToDataAndErrors(object newDataSource, string newDataMember) { Set_ErrorManager(newDataSource, newDataMember, false); } private void WireEvents(BindingManagerBase listManager) { if (listManager != null) { listManager.CurrentChanged += currentChanged; listManager.BindingComplete += new BindingCompleteEventHandler(this.ErrorManager_BindingComplete); CurrencyManager currManager = listManager as CurrencyManager; if (currManager != null) { currManager.ItemChanged += new ItemChangedEventHandler(this.ErrorManager_ItemChanged); currManager.Bindings.CollectionChanged += new CollectionChangeEventHandler(this.ErrorManager_BindingsChanged); } } } private void UnwireEvents(BindingManagerBase listManager) { if (listManager != null) { listManager.CurrentChanged -= currentChanged; listManager.BindingComplete -= new BindingCompleteEventHandler(this.ErrorManager_BindingComplete); CurrencyManager currManager = listManager as CurrencyManager; if (currManager != null) { currManager.ItemChanged -= new ItemChangedEventHandler(this.ErrorManager_ItemChanged); currManager.Bindings.CollectionChanged -= new CollectionChangeEventHandler(this.ErrorManager_BindingsChanged); } } } private void ErrorManager_BindingComplete(object sender, BindingCompleteEventArgs e) { Binding binding = e.Binding; if (binding != null && binding.Control != null) { SetError(binding.Control, (e.ErrorText == null ? String.Empty : e.ErrorText)); } } private void ErrorManager_BindingsChanged(object sender, CollectionChangeEventArgs e) { ErrorManager_CurrentChanged(errorManager, e); } private void ParentControl_BindingContextChanged(object sender, EventArgs e) { Set_ErrorManager(this.DataSource, this.DataMember, true); } // Work around... we should figure out if errors changed automatically. ///[To be supplied.] ////// /// public void UpdateBinding() { ErrorManager_CurrentChanged(errorManager, EventArgs.Empty); } private void ErrorManager_ItemChanged(object sender, ItemChangedEventArgs e) { BindingsCollection errBindings = errorManager.Bindings; int bindingsCount = errBindings.Count; // If the list became empty then reset the errors if (e.Index == -1 && errorManager.Count == 0) { for (int j = 0; j < bindingsCount; j++) { if (errBindings[j].Control != null) { // ...ignore everything but bindings to Controls SetError(errBindings[j].Control, ""); } } } else { ErrorManager_CurrentChanged(sender, e); } } private void ErrorManager_CurrentChanged(object sender, EventArgs e) { Debug.Assert(sender == errorManager, "who else can send us messages?"); // flush the old list // // items.Clear(); if (errorManager.Count == 0) { return; } object value = errorManager.Current; if ( !(value is IDataErrorInfo)) { return; } BindingsCollection errBindings = errorManager.Bindings; int bindingsCount = errBindings.Count; // we need to delete the blinkPhases from each controlItem (suppose // that the error that we get is the same error. then we want to // show the error and not blink ) // foreach (ControlItem ctl in items.Values) { ctl.BlinkPhase = 0; } // We can only show one error per control, so we will build up a string... // Hashtable controlError = new Hashtable(bindingsCount); for (int j = 0; j < bindingsCount; j++) { // Ignore everything but bindings to Controls if (errBindings[j].Control == null) { continue; } BindToObject dataBinding = errBindings[j].BindToObject; string error = ((IDataErrorInfo) value)[dataBinding.BindingMemberInfo.BindingField]; if (error == null) { error = ""; } string outputError = ""; if (controlError.Contains(errBindings[j].Control)) outputError = (string) controlError[errBindings[j].Control]; // VSWhidbey 106890: Utilize the error string without including the field name. if (String.IsNullOrEmpty(outputError)) { outputError = error; } else { outputError = string.Concat(outputError, "\r\n", error); } controlError[errBindings[j].Control] = outputError; } IEnumerator enumerator = controlError.GetEnumerator(); while (enumerator.MoveNext()) { DictionaryEntry entry = (DictionaryEntry) enumerator.Current; SetError((Control) entry.Key, (string) entry.Value); } } ///[To be supplied.] ////// /// Returns or set the rate in milliseconds at which the error icon flashes. /// [ SRCategory(SR.CatBehavior), DefaultValue(defaultBlinkRate), SRDescription(SR.ErrorProviderBlinkRateDescr), RefreshProperties(RefreshProperties.Repaint) ] public int BlinkRate { get { return blinkRate; } set { if (value < 0) { throw new ArgumentOutOfRangeException("BlinkRate", value, SR.GetString(SR.BlinkRateMustBeZeroOrMore)); } blinkRate = value; // If we set the blinkRate = 0 then set BlinkStyle = NeverBlink if (blinkRate == 0) BlinkStyle = ErrorBlinkStyle.NeverBlink; } } ////// /// Demand load and cache the default icon. /// ///static Icon DefaultIcon { get { if (defaultIcon == null) { lock (typeof(ErrorProvider)) { if (defaultIcon == null) { defaultIcon = new Icon(typeof(ErrorProvider), "Error.ico"); } } } return defaultIcon; } } /// /// /// Returns or sets the Icon that displayed next to a control when an error /// description string has been set for the control. For best results, an /// icon containing a 16 by 16 icon should be used. /// [ Localizable(true), SRCategory(SR.CatAppearance), SRDescription(SR.ErrorProviderIconDescr) ] public Icon Icon { get { return icon; } set { if (value == null) throw new ArgumentNullException("value"); icon = value; DisposeRegion(); ErrorWindow[] array = new ErrorWindow[windows.Values.Count]; windows.Values.CopyTo(array, 0); for (int i = 0; i < array.Length; i++) array[i].Update(false /*timerCaused*/); } } ////// /// Create the icon region on demand. /// ///internal IconRegion Region { get { if (region == null) region = new IconRegion(Icon); return region; } } // // METHODS // // Begin bulk member initialization - deferring binding to data source until EndInit is reached void ISupportInitialize.BeginInit() { initializing = true; } // End bulk member initialization by binding to data source private void EndInitCore() { initializing = false; if (setErrorManagerOnEndInit) { setErrorManagerOnEndInit = false; Set_ErrorManager(this.DataSource, this.DataMember, true); } } // Check to see if DataSource has completed its initialization, before ending our initialization. // If DataSource is still initializing, hook its Initialized event and wait for it to signal completion. // If DataSource is already initialized, just go ahead and complete our initialization now. // void ISupportInitialize.EndInit() { ISupportInitializeNotification dsInit = (this.DataSource as ISupportInitializeNotification); if (dsInit != null && !dsInit.IsInitialized) { dsInit.Initialized += new EventHandler(DataSource_Initialized); } else { EndInitCore(); } } // Respond to late completion of the DataSource's initialization, by completing our own initialization. // This situation can arise if the call to the DataSource's EndInit() method comes after the call to the // BindingSource's EndInit() method (since code-generated ordering of these calls is non-deterministic). // private void DataSource_Initialized(object sender, EventArgs e) { ISupportInitializeNotification dsInit = (this.DataSource as ISupportInitializeNotification); Debug.Assert(dsInit != null, "ErrorProvider: ISupportInitializeNotification.Initialized event received, but current DataSource does not support ISupportInitializeNotification!"); Debug.Assert(dsInit.IsInitialized, "ErrorProvider: DataSource sent ISupportInitializeNotification.Initialized event but before it had finished initializing."); if (dsInit != null) { dsInit.Initialized -= new EventHandler(DataSource_Initialized); } EndInitCore(); } /// /// /// Clears all errors being tracked by this error provider, ie. undoes all previous calls to SetError. /// public void Clear() { ErrorWindow[] w = new ErrorWindow[windows.Values.Count]; windows.Values.CopyTo(w, 0); for (int i = 0; i < w.Length; i++) { w[i].Dispose(); } windows.Clear(); foreach (ControlItem item in items.Values) { if (item != null) { item.Dispose(); } } items.Clear(); } ////// /// Returns whether a control can be extended. /// public bool CanExtend(object extendee) { return extendee is Control && !(extendee is Form) && !(extendee is ToolBar); } ////// /// Release any resources that this component is using. After calling Dispose, /// the component should no longer be used. /// protected override void Dispose(bool disposing) { if (disposing) { Clear(); DisposeRegion(); UnwireEvents(errorManager); } base.Dispose(disposing); } ////// /// Helper to dispose the cached icon region. /// ///void DisposeRegion() { if (region != null) { region.Dispose(); region = null; } } /// /// /// Helper to make sure we have allocated a control item for this control. /// ///private ControlItem EnsureControlItem(Control control) { if (control == null) throw new ArgumentNullException("control"); ControlItem item = (ControlItem)items[control]; if (item == null) { item = new ControlItem(this, control, (IntPtr)(++itemIdCounter)); items[control] = item; } return item; } /// /// /// Helper to make sure we have allocated an error window for this control. /// ///internal ErrorWindow EnsureErrorWindow(Control parent) { ErrorWindow window = (ErrorWindow)windows[parent]; if (window == null) { window = new ErrorWindow(this, parent); windows[parent] = window; } return window; } /// /// /// Returns the current error description string for the specified control. /// [ DefaultValue(""), Localizable(true), SRCategory(SR.CatAppearance), SRDescription(SR.ErrorProviderErrorDescr) ] public string GetError(Control control) { return EnsureControlItem(control).Error; } ////// /// Returns where the error icon should be placed relative to the control. /// [ DefaultValue(defaultIconAlignment), Localizable(true), SRCategory(SR.CatAppearance), SRDescription(SR.ErrorProviderIconAlignmentDescr) ] public ErrorIconAlignment GetIconAlignment(Control control) { return EnsureControlItem(control).IconAlignment; } ////// /// Returns the amount of extra space to leave next to the error icon. /// [ DefaultValue(0), Localizable(true), SRCategory(SR.CatAppearance), SRDescription(SR.ErrorProviderIconPaddingDescr) ] public int GetIconPadding(Control control) { return EnsureControlItem(control).IconPadding; } private void ResetIcon() { Icon = DefaultIcon; } ////// /// [EditorBrowsable(EditorBrowsableState.Advanced)] protected virtual void OnRightToLeftChanged(EventArgs e) { foreach (ErrorWindow w in windows.Values) { w.Update(false); } if (onRightToLeftChanged != null) { onRightToLeftChanged(this, e); } } ///[To be supplied.] ////// /// Sets the error description string for the specified control. /// public void SetError(Control control, string value) { EnsureControlItem(control).Error = value; } ////// /// Sets where the error icon should be placed relative to the control. /// public void SetIconAlignment(Control control, ErrorIconAlignment value) { EnsureControlItem(control).IconAlignment = value; } ////// /// Sets the amount of extra space to leave next to the error icon. /// public void SetIconPadding(Control control, int padding) { EnsureControlItem(control).IconPadding = padding; } private bool ShouldSerializeIcon() { return Icon != DefaultIcon; } ////// /// There is one ErrorWindow for each control parent. It is parented to the /// control parent. The window's region is made up of the regions from icons /// of all child icons. The window's size is the enclosing rectangle for all /// the regions. A tooltip window is created as a child of this window. The /// rectangle associated with each error icon being displayed is added as a /// tool to the tooltip window. /// ///internal class ErrorWindow : NativeWindow { // // FIELDS // ArrayList items = new ArrayList(); Control parent; ErrorProvider provider; Rectangle windowBounds = Rectangle.Empty; System.Windows.Forms.Timer timer; NativeWindow tipWindow; // VSWhidbey #455702 DeviceContext mirrordc= null; Size mirrordcExtent = Size.Empty; Point mirrordcOrigin = Point.Empty; DeviceContextMapMode mirrordcMode = DeviceContextMapMode.Text; // // CONSTRUCTORS // /// /// /// Construct an error window for this provider and control parent. /// public ErrorWindow(ErrorProvider provider, Control parent) { this.provider = provider; this.parent = parent; } // // METHODS // ////// /// This is called when a control would like to show an error icon. /// public void Add(ControlItem item) { items.Add(item); if (!EnsureCreated()) { return; } NativeMethods.TOOLINFO_T toolInfo = new NativeMethods.TOOLINFO_T(); toolInfo.cbSize = Marshal.SizeOf(toolInfo); toolInfo.hwnd = Handle; toolInfo.uId = item.Id; toolInfo.lpszText = item.Error; toolInfo.uFlags = NativeMethods.TTF_SUBCLASS; UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_ADDTOOL, 0, toolInfo); Update(false /*timerCaused*/); } ////// /// Called to get rid of any resources the Object may have. /// public void Dispose() { EnsureDestroyed(); } ////// /// Make sure the error window is created, and the tooltip window is created. /// bool EnsureCreated() { if (Handle == IntPtr.Zero) { if (!parent.IsHandleCreated) { return false; } CreateParams cparams = new CreateParams(); cparams.Caption = String.Empty; cparams.Style = NativeMethods.WS_VISIBLE | NativeMethods.WS_CHILD; cparams.ClassStyle = NativeMethods.CS_DBLCLKS; cparams.X = 0; cparams.Y = 0; cparams.Width = 0; cparams.Height = 0; cparams.Parent = parent.Handle; CreateHandle(cparams); NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); icc.dwICC = NativeMethods.ICC_TAB_CLASSES; icc.dwSize = Marshal.SizeOf(icc); SafeNativeMethods.InitCommonControlsEx(icc); cparams = new CreateParams(); cparams.Parent = Handle; cparams.ClassName = NativeMethods.TOOLTIPS_CLASS; cparams.Style = NativeMethods.TTS_ALWAYSTIP; tipWindow = new NativeWindow(); tipWindow.CreateHandle(cparams); UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width); SafeNativeMethods.SetWindowPos(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.HWND_TOP, 0, 0, 0, 0, NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOACTIVATE); UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_SETDELAYTIME, NativeMethods.TTDT_INITIAL, 0); } return true; } ////// /// Destroy the timer, toolwindow, and the error window itself. /// void EnsureDestroyed() { if (timer != null) { timer.Dispose(); timer = null; } if (tipWindow != null) { tipWindow.DestroyHandle(); tipWindow = null; } // Hide the window and invalidate the parent to ensure // that we leave no visual artifacts... given that we // have a bizare region window, this is needed. // SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), NativeMethods.HWND_TOP, windowBounds.X, windowBounds.Y, windowBounds.Width, windowBounds.Height, NativeMethods.SWP_HIDEWINDOW | NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE); if (parent != null) { parent.Invalidate(true); } DestroyHandle(); Debug.Assert(mirrordc == null, "Why is mirrordc non-null?"); if (mirrordc != null) { mirrordc.Dispose(); } } ////// /// VSWhidbey #455702. /// /// Since we added mirroring to certain controls, we need to make sure the /// error icons show up in the correct place. We cannot mirror the errorwindow /// in EnsureCreated (although that would have been really easy), since we use /// GDI+ for some of this code, and as we all know, GDI+ does not handle mirroring /// at all. /// /// To work around that we create our own mirrored dc when we need to. /// /// void CreateMirrorDC(IntPtr hdc, int originOffset) { Debug.Assert(mirrordc == null, "Why is mirrordc non-null? Did you not call RestoreMirrorDC?"); mirrordc = DeviceContext.FromHdc(hdc); if (parent.IsMirrored && mirrordc != null) { mirrordc.SaveHdc(); mirrordcExtent = mirrordc.ViewportExtent; mirrordcOrigin = mirrordc.ViewportOrigin; mirrordcMode = mirrordc.SetMapMode(DeviceContextMapMode.Anisotropic); mirrordc.ViewportExtent = new Size(-(mirrordcExtent.Width), mirrordcExtent.Height); mirrordc.ViewportOrigin = new Point(mirrordcOrigin.X + originOffset, mirrordcOrigin.Y); } } void RestoreMirrorDC() { if (parent.IsMirrored && mirrordc != null) { mirrordc.ViewportExtent = mirrordcExtent; mirrordc.ViewportOrigin = mirrordcOrigin; mirrordc.SetMapMode(mirrordcMode); mirrordc.RestoreHdc(); mirrordc.Dispose(); } mirrordc= null; mirrordcExtent = Size.Empty; mirrordcOrigin = Point.Empty; mirrordcMode = DeviceContextMapMode.Text; } ////// /// This is called when the error window needs to paint. We paint each icon at its /// correct location. /// void OnPaint(ref Message m) { NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT(); IntPtr hdc = UnsafeNativeMethods.BeginPaint(new HandleRef(this, Handle), ref ps); CreateMirrorDC(hdc, windowBounds.Width - 1); try { for (int i = 0; i < items.Count; i++) { ControlItem item = (ControlItem)items[i]; Rectangle bounds = item.GetIconBounds(provider.Region.Size); SafeNativeMethods.DrawIconEx(new HandleRef(this, mirrordc.Hdc), bounds.X - windowBounds.X, bounds.Y - windowBounds.Y, new HandleRef(provider.Region, provider.Region.IconHandle), bounds.Width, bounds.Height, 0, NativeMethods.NullHandleRef, NativeMethods.DI_NORMAL); } } finally { RestoreMirrorDC(); } UnsafeNativeMethods.EndPaint(new HandleRef(this, Handle), ref ps); } protected override void OnThreadException(Exception e) { Application.OnThreadException(e); } ////// /// This is called when an error icon is flashing, and the view needs to be updatd. /// void OnTimer(Object sender, EventArgs e) { int blinkPhase = 0; for (int i = 0; i < items.Count; i++) { blinkPhase += ((ControlItem)items[i]).BlinkPhase; } if (blinkPhase == 0 && provider.BlinkStyle != ErrorBlinkStyle.AlwaysBlink) { Debug.Assert(timer != null); timer.Stop(); } Update(true /*timerCaused*/); } private void OnToolTipVisibilityChanging(System.IntPtr id, bool toolTipShown) { for (int i = 0; i < items.Count; i++) { if (((ControlItem)items[i]).Id == id) { ((ControlItem)items[i]).ToolTipShown = toolTipShown; } } #if DEBUG int shownTooltips = 0; for (int j = 0; j < items.Count; j++) { if (((ControlItem)items[j]).ToolTipShown) { shownTooltips++; } } Debug.Assert(shownTooltips <= 1); #endif } ////// /// This is called when a control no longer needs to display an error icon. /// public void Remove(ControlItem item) { items.Remove(item); if (tipWindow != null) { NativeMethods.TOOLINFO_T toolInfo = new NativeMethods.TOOLINFO_T(); toolInfo.cbSize = Marshal.SizeOf(toolInfo); toolInfo.hwnd = Handle; toolInfo.uId = item.Id; UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_DELTOOL, 0, toolInfo); } if (items.Count == 0) { EnsureDestroyed(); } else { Update(false /*timerCaused*/); } } ////// /// Start the blinking process. The timer will fire until there are no more /// icons that need to blink. /// internal void StartBlinking() { if (timer == null) { timer = new System.Windows.Forms.Timer(); timer.Tick += new EventHandler(OnTimer); } timer.Interval = provider.BlinkRate; timer.Start(); Update(false /*timerCaused*/); } internal void StopBlinking() { if (timer != null) { timer.Stop(); } Update(false /*timerCaused*/); } ////// /// Move and size the error window, compute and set the window region, /// set the tooltip rectangles and descriptions. This basically brings /// the error window up to date with the internal data structures. /// public void Update(bool timerCaused) { IconRegion iconRegion = provider.Region; Size size = iconRegion.Size; windowBounds = Rectangle.Empty; for (int i = 0; i < items.Count; i++) { ControlItem item = (ControlItem)items[i]; Rectangle iconBounds = item.GetIconBounds(size); if (windowBounds.IsEmpty) windowBounds = iconBounds; else windowBounds = Rectangle.Union(windowBounds, iconBounds); } Region windowRegion = new Region(new Rectangle(0, 0, 0, 0)); IntPtr windowRegionHandle = IntPtr.Zero; try { for (int i = 0; i < items.Count; i++) { ControlItem item = (ControlItem)items[i]; Rectangle iconBounds = item.GetIconBounds(size); iconBounds.X -= windowBounds.X; iconBounds.Y -= windowBounds.Y; bool showIcon = true; if (!item.ToolTipShown) { switch (provider.BlinkStyle) { case ErrorBlinkStyle.NeverBlink: // always show icon break; case ErrorBlinkStyle.BlinkIfDifferentError: showIcon = (item.BlinkPhase == 0) || (item.BlinkPhase > 0 && (item.BlinkPhase & 1) == (i & 1)); break; case ErrorBlinkStyle.AlwaysBlink: showIcon = ((i & 1) == 0) == provider.showIcon; break; } } if (showIcon) { iconRegion.Region.Translate(iconBounds.X, iconBounds.Y); windowRegion.Union(iconRegion.Region); iconRegion.Region.Translate(-iconBounds.X, -iconBounds.Y); } if (tipWindow != null) { NativeMethods.TOOLINFO_T toolInfo = new NativeMethods.TOOLINFO_T(); toolInfo.cbSize = Marshal.SizeOf(toolInfo); toolInfo.hwnd = Handle; toolInfo.uId = item.Id; toolInfo.lpszText = item.Error; toolInfo.rect = NativeMethods.RECT.FromXYWH(iconBounds.X, iconBounds.Y, iconBounds.Width, iconBounds.Height); toolInfo.uFlags = NativeMethods.TTF_SUBCLASS; if (provider.RightToLeft) { toolInfo.uFlags |= NativeMethods.TTF_RTLREADING; } UnsafeNativeMethods.SendMessage(new HandleRef(tipWindow, tipWindow.Handle), NativeMethods.TTM_SETTOOLINFO, 0, toolInfo); } if (timerCaused && item.BlinkPhase > 0) { item.BlinkPhase--; } } if (timerCaused) { provider.showIcon = !provider.showIcon; } DeviceContext dc = null; dc = DeviceContext.FromHwnd(this.Handle); try { CreateMirrorDC(dc.Hdc, windowBounds.Width); Graphics graphics = Graphics.FromHdcInternal(mirrordc.Hdc); try { windowRegionHandle = windowRegion.GetHrgn(graphics); System.Internal.HandleCollector.Add(windowRegionHandle, NativeMethods.CommonHandles.GDI); } finally { graphics.Dispose(); RestoreMirrorDC(); } if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, Handle), new HandleRef(windowRegion, windowRegionHandle), true) != 0) { //The HWnd owns the region. windowRegionHandle = IntPtr.Zero; } } finally { if (dc != null) { dc.Dispose(); } } } finally { windowRegion.Dispose(); if (windowRegionHandle != IntPtr.Zero) { SafeNativeMethods.DeleteObject(new HandleRef(null, windowRegionHandle)); } } SafeNativeMethods.SetWindowPos(new HandleRef(this, Handle), NativeMethods.HWND_TOP, windowBounds.X, windowBounds.Y, windowBounds.Width, windowBounds.Height, NativeMethods.SWP_NOACTIVATE); SafeNativeMethods.InvalidateRect(new HandleRef(this, Handle), null, false); } ////// /// Called when the error window gets a windows message. /// protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_NOTIFY: NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); if (nmhdr.code == NativeMethods.TTN_SHOW || nmhdr.code == NativeMethods.TTN_POP) { OnToolTipVisibilityChanging(nmhdr.idFrom, nmhdr.code == NativeMethods.TTN_SHOW); } break; case NativeMethods.WM_ERASEBKGND: break; case NativeMethods.WM_PAINT: OnPaint(ref m); break; default: base.WndProc(ref m); break; } } } ////// /// There is one ControlItem for each control that the ErrorProvider is /// is tracking state for. It contains the values of all the extender /// properties. /// internal class ControlItem { // // FIELDS // string error; Control control; ErrorWindow window; ErrorProvider provider; int blinkPhase; IntPtr id; int iconPadding; bool toolTipShown; ErrorIconAlignment iconAlignment; const int startingBlinkPhase = 10; // cause we want to blink 5 times // // CONSTRUCTORS // ////// /// Construct the item with its associated control, provider, and /// a unique ID. The ID is used for the tooltip ID. /// public ControlItem(ErrorProvider provider, Control control, IntPtr id) { this.toolTipShown = false; this.iconAlignment = defaultIconAlignment; this.error = String.Empty; this.id = id; this.control = control; this.provider = provider; this.control.HandleCreated += new EventHandler(OnCreateHandle); this.control.HandleDestroyed += new EventHandler(OnDestroyHandle); this.control.LocationChanged += new EventHandler(OnBoundsChanged); this.control.SizeChanged += new EventHandler(OnBoundsChanged); this.control.VisibleChanged += new EventHandler(OnParentVisibleChanged); this.control.ParentChanged += new EventHandler(OnParentVisibleChanged); } public void Dispose() { if (control != null) { control.HandleCreated -= new EventHandler(OnCreateHandle); control.HandleDestroyed -= new EventHandler(OnDestroyHandle); control.LocationChanged -= new EventHandler(OnBoundsChanged); control.SizeChanged -= new EventHandler(OnBoundsChanged); control.VisibleChanged -= new EventHandler(OnParentVisibleChanged); control.ParentChanged -= new EventHandler(OnParentVisibleChanged); } error = string.Empty; } // // PROPERTIES // ////// /// Returns the unique ID for this control. The ID used as the tooltip ID. /// public IntPtr Id { get { return id; } } ////// /// Returns or set the phase of blinking that this control is currently /// in. If zero, the control is not blinking. If odd, then the control /// is blinking, but invisible. If even, the control is blinking and /// currently visible. Each time the blink timer fires, this value is /// reduced by one (until zero), thus causing the error icon to appear /// or disappear. /// public int BlinkPhase { get { return blinkPhase; } set { blinkPhase = value; } } ////// /// Returns or sets the icon padding for the control. /// public int IconPadding { get { return iconPadding; } set { if (iconPadding != value) { iconPadding = value; UpdateWindow(); } } } ////// /// Returns or sets the error description string for the control. /// public string Error { get { return error; } set { if (value == null) { value = ""; } // if the error is the same and the blinkStyle is not AlwaysBlink, then // we should not add the error and not start blinking. if (error.Equals(value) && provider.BlinkStyle != ErrorBlinkStyle.AlwaysBlink) { return; } bool adding = error.Length == 0; error = value; if (value.Length == 0) { RemoveFromWindow(); } else { if (adding) { AddToWindow(); } else { if (provider.BlinkStyle != ErrorBlinkStyle.NeverBlink) { StartBlinking(); } else { UpdateWindow(); } } } } } ////// /// Returns or sets the location of the error icon for the control. /// public ErrorIconAlignment IconAlignment { get { return iconAlignment; } set { if (iconAlignment != value) { //valid values are 0x0 to 0x5 if (!ClientUtils.IsEnumValid(value, (int)value, (int)ErrorIconAlignment.TopLeft, (int)ErrorIconAlignment.BottomRight)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(ErrorIconAlignment)); } iconAlignment = value; UpdateWindow(); } } } ////// /// Returns true if the tooltip for this control item is currently shown. /// public bool ToolTipShown { get { return this.toolTipShown; } set { this.toolTipShown = value; } } internal ErrorIconAlignment RTLTranslateIconAlignment(ErrorIconAlignment align) { if (provider.RightToLeft) { switch (align) { case ErrorIconAlignment.TopLeft: return ErrorIconAlignment.TopRight; case ErrorIconAlignment.MiddleLeft: return ErrorIconAlignment.MiddleRight; case ErrorIconAlignment.BottomLeft: return ErrorIconAlignment.BottomRight; case ErrorIconAlignment.TopRight: return ErrorIconAlignment.TopLeft; case ErrorIconAlignment.MiddleRight: return ErrorIconAlignment.MiddleLeft; case ErrorIconAlignment.BottomRight: return ErrorIconAlignment.BottomLeft; default: Debug.Fail("Unknown ErrorIconAlignment value"); return align; } } else { return align; } } ////// /// Returns the location of the icon in the same coordinate system as /// the control being extended. The size passed in is the size of /// the icon. /// internal Rectangle GetIconBounds(Size size) { int x = 0; int y = 0; switch (RTLTranslateIconAlignment(IconAlignment)) { case ErrorIconAlignment.TopLeft: case ErrorIconAlignment.MiddleLeft: case ErrorIconAlignment.BottomLeft: x = control.Left - size.Width - iconPadding; break; case ErrorIconAlignment.TopRight: case ErrorIconAlignment.MiddleRight: case ErrorIconAlignment.BottomRight: x = control.Right + iconPadding; break; } switch (IconAlignment) { case ErrorIconAlignment.TopLeft: case ErrorIconAlignment.TopRight: y = control.Top; break; case ErrorIconAlignment.MiddleLeft: case ErrorIconAlignment.MiddleRight: y = control.Top + (control.Height - size.Height) / 2; break; case ErrorIconAlignment.BottomLeft: case ErrorIconAlignment.BottomRight: y = control.Bottom - size.Height; break; } return new Rectangle(x, y, size.Width, size.Height); } ////// /// If this control's error icon has been added to the error /// window, then update the window state because some property /// has changed. /// void UpdateWindow() { if (window != null) { window.Update(false /*timerCaused*/); } } ////// /// If this control's error icon has been added to the error /// window, then start blinking the error window. The blink /// count /// void StartBlinking() { if (window != null) { BlinkPhase = startingBlinkPhase; window.StartBlinking(); } } ////// /// Add this control's error icon to the error window. /// void AddToWindow() { // if we are recreating the control, then add the control. if (window == null && (control.Created || control.RecreatingHandle) && control.Visible && control.ParentInternal != null && error.Length > 0) { window = provider.EnsureErrorWindow(control.ParentInternal); window.Add(this); // Make sure that we blink if the style is set to AlwaysBlink or BlinkIfDifferrentError if (provider.BlinkStyle != ErrorBlinkStyle.NeverBlink) { StartBlinking(); } } } ////// /// Remove this control's error icon from the error window. /// void RemoveFromWindow() { if (window != null) { window.Remove(this); window = null; } } ////// /// This is called when a property on the control is changed. /// void OnBoundsChanged(Object sender, EventArgs e) { UpdateWindow(); } void OnParentVisibleChanged(Object sender, EventArgs e) { this.BlinkPhase = 0; RemoveFromWindow(); AddToWindow(); } ////// /// This is called when the control's handle is created. /// void OnCreateHandle(Object sender, EventArgs e) { AddToWindow(); } ////// /// This is called when the control's handle is destroyed. /// void OnDestroyHandle(Object sender, EventArgs e) { RemoveFromWindow(); } } ////// /// This represents the HRGN of icon. The region is calculate from the icon's mask. /// internal class IconRegion { // // FIELDS // Region region; Icon icon; // // CONSTRUCTORS // ////// /// Constructor that takes an Icon and extracts its 16x16 version. /// public IconRegion(Icon icon) { this.icon = new Icon(icon, 16, 16); } // // PROPERTIES // ////// /// Returns the handle of the icon. /// public IntPtr IconHandle { get { return icon.Handle; } } ////// /// Returns the handle of the region. /// public Region Region { get { if (region == null) { region = new Region(new Rectangle(0,0,0,0)); IntPtr mask = IntPtr.Zero; try { Size size = icon.Size; Bitmap bitmap = icon.ToBitmap(); bitmap.MakeTransparent(); mask = ControlPaint.CreateHBitmapTransparencyMask(bitmap); bitmap.Dispose(); int widthInBytes = size.Width / 8; byte[] bits = new byte[widthInBytes * size.Height]; SafeNativeMethods.GetBitmapBits(new HandleRef(null, mask), bits.Length, bits); for (int y = 0; y < size.Height; y++) { for (int x = 0; x < size.Width; x++) { // see if bit is set in mask. bits in byte are reversed. 0 is black (set). if ((bits[y * widthInBytes + x / 8] & (1 << (7 - (x % 8)))) == 0) { region.Union(new Rectangle(x, y, 1, 1)); } } } region.Intersect(new Rectangle(0, 0, size.Width, size.Height)); } finally { if (mask != IntPtr.Zero) SafeNativeMethods.DeleteObject(new HandleRef(null, mask)); } } return region; } } ////// /// Return the size of the icon. /// public Size Size { get { return icon.Size; } } // // METHODS // ////// /// Release any resources held by this Object. /// public void Dispose() { if (region != null) { region.Dispose(); region = null; } icon.Dispose(); } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- TypeSystem.cs
- WindowsStartMenu.cs
- DrawToolTipEventArgs.cs
- IntSecurity.cs
- PointLightBase.cs
- GlyphTypeface.cs
- DataGridViewRowHeaderCell.cs
- StandardCommands.cs
- ExclusiveNamedPipeTransportManager.cs
- CodeArgumentReferenceExpression.cs
- QilStrConcat.cs
- WebPartConnectionsConfigureVerb.cs
- DateTimePicker.cs
- Array.cs
- Compiler.cs
- Ops.cs
- XPathDocumentNavigator.cs
- BufferModeSettings.cs
- ExceptionUtil.cs
- HttpResponseHeader.cs
- ISessionStateStore.cs
- MeasureItemEvent.cs
- CommandManager.cs
- MaterializeFromAtom.cs
- SymbolType.cs
- XmlDictionaryString.cs
- CodeLabeledStatement.cs
- Thumb.cs
- LinkButton.cs
- DbSetClause.cs
- _SSPIWrapper.cs
- dbdatarecord.cs
- VisualTreeFlattener.cs
- RowsCopiedEventArgs.cs
- externdll.cs
- CodeTypeMemberCollection.cs
- WebPartUtil.cs
- ObjectStateEntry.cs
- QualificationDataItem.cs
- ApplicationFileParser.cs
- SpellerHighlightLayer.cs
- DesignTimeVisibleAttribute.cs
- PnrpPeerResolverElement.cs
- TemplateControl.cs
- XmlSchemaParticle.cs
- NativeMethods.cs
- StrokeIntersection.cs
- BindingValueChangedEventArgs.cs
- Deflater.cs
- Simplifier.cs
- OutOfMemoryException.cs
- MetabaseServerConfig.cs
- HybridWebProxyFinder.cs
- DataServiceQuery.cs
- TextTreeRootTextBlock.cs
- Selection.cs
- PointCollectionValueSerializer.cs
- SelectorAutomationPeer.cs
- xdrvalidator.cs
- TabletDevice.cs
- ColumnTypeConverter.cs
- ListViewGroupConverter.cs
- ResourceFallbackManager.cs
- CommentAction.cs
- ToolBarButtonClickEvent.cs
- Wizard.cs
- SqlCachedBuffer.cs
- DecoderNLS.cs
- SystemEvents.cs
- ValueUnavailableException.cs
- _NestedSingleAsyncResult.cs
- AppDomainShutdownMonitor.cs
- AdornerPresentationContext.cs
- HttpWebRequest.cs
- ValueOfAction.cs
- TimeSpanMinutesConverter.cs
- DesignTimeData.cs
- EditorOptionAttribute.cs
- GeometryGroup.cs
- PrivilegedConfigurationManager.cs
- PtsPage.cs
- xmlsaver.cs
- CollectionView.cs
- InheritanceContextChangedEventManager.cs
- CodeGen.cs
- CaseInsensitiveOrdinalStringComparer.cs
- NativeWindow.cs
- ModelItemDictionary.cs
- FilterQueryOptionExpression.cs
- TextBox.cs
- FormsAuthenticationEventArgs.cs
- DataGridViewSelectedRowCollection.cs
- VerticalAlignConverter.cs
- Closure.cs
- BaseServiceProvider.cs
- RtfControls.cs
- SecurityChannelFaultConverter.cs
- RelationshipManager.cs
- HttpResponse.cs
- XhtmlStyleClass.cs