| File: System\Windows\Diagnostics\BindingDiagnostics.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // // Description: // Binding diagnostics API // namespace System.Windows.Diagnostics { /// <summary> /// Provides a notification infrastructure for listening to binding failure events. /// </summary> /// <remarks> /// This type supports the .NET Framework infrastructure and is not intended to be used directly /// from application code. /// </remarks> public static class BindingDiagnostics { internal static bool IsEnabled { get; private set; } private static event EventHandler<BindingFailedEventArgs> s_bindingFailed; private static List<BindingFailedEventArgs> s_pendingEvents; private static readonly object s_pendingEventsLock; private const int MaxPendingEvents = 2000; static BindingDiagnostics() { IsEnabled = VisualDiagnostics.IsEnabled && VisualDiagnostics.IsEnvironmentVariableSet(null, XamlSourceInfoHelper.XamlSourceInfoEnvironmentVariable); if (IsEnabled) { // Listeners may miss the initial set of binding failures, so cache events until the first listener attaches. // Normally there will only be one listener added soon after the process starts, // and it will want to know about any binding failures that already happened. s_pendingEvents = new List<BindingFailedEventArgs>(); s_pendingEventsLock = new object(); } } /// <summary> /// Handlers of this event should return control to WPF quickly, and not cache BindingFailedEventArgs for future use. /// </summary> public static event EventHandler<BindingFailedEventArgs> BindingFailed { add { if (IsEnabled) { s_bindingFailed += value; FlushPendingBindingFailedEvents(); } } remove { s_bindingFailed -= value; } } /// <summary> /// Flushes all cached binding failure events and stops any further events from being cached. /// </summary> private static void FlushPendingBindingFailedEvents() { if (s_pendingEvents != null) { List<BindingFailedEventArgs> pendingEvents = null; lock (s_pendingEventsLock) { pendingEvents = s_pendingEvents; // Don't allow any more event caching s_pendingEvents = null; } if (pendingEvents != null) { foreach (BindingFailedEventArgs args in pendingEvents) { s_bindingFailed?.Invoke(null, args); } } } } /// <summary> /// Either triggers the BindingFailed event or caches the event for when the first listener attaches. /// </summary> internal static void NotifyBindingFailed(BindingFailedEventArgs args) { if (!IsEnabled) { return; } if (s_pendingEvents != null) { lock (s_pendingEventsLock) { if (s_pendingEvents != null) { // Limit the pending event count so that memory doesn't grow unbounded if no event handler is ever added if (s_pendingEvents.Count < MaxPendingEvents) { s_pendingEvents.Add(args); } return; } } } s_bindingFailed?.Invoke(null, args); } } }