diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceDictionary.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceDictionary.cs index 00c3e73fcf4..445eea5693c 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceDictionary.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceDictionary.cs @@ -109,7 +109,7 @@ private void CopyToWithoutLock(DictionaryEntry[] array, int arrayIndex) entry.Value = value; // refresh the entry value in case it was changed in the previous call } } - + // This is set when the RD is loaded from unsafe xps doc. This will be checked while creating reader for RD source. internal bool IsUnsafe { get; set; } @@ -157,7 +157,7 @@ public Uri Source // that it is being passed down by the Baml parsing code, and it is trying to give us more // information to avoid possible ambiguities in assembly resolving. Use the VersionedUri // to resolve, and the set _source to the OriginalUri so we don't change the return of Source property. - // The versioned Uri is not stored, if the version info is needed while debugging, once this method + // The versioned Uri is not stored, if the version info is needed while debugging, once this method // returns _reader should be set, from there BamlSchemaContext.LocalAssembly contains the version info. if (uriWrapper == null) { @@ -169,10 +169,10 @@ public Uri Source _source = uriWrapper.OriginalUri; sourceUri = uriWrapper.VersionedUri; } - + Clear(); - - + + Uri uri = BindUriHelper.GetResolvedUri(_baseUri, sourceUri); WebRequest request = WpfWebRequestHelper.CreateRequest(uri); @@ -1737,10 +1737,7 @@ private object FetchResource( { // Cache the deferredResourceReference so that it can be validated // in case of a dictionary change prior to its inflation - if (_deferredResourceReferences == null) - { - _deferredResourceReferences = new DeferredResourceReferenceList(); - } + _deferredResourceReferences ??= new DeferredResourceReferenceList(); if (_deferredResourceReferences.Get(resourceKey) is { } existingDeferredResourceReference && existingDeferredResourceReference.Dictionary == this) @@ -1749,14 +1746,7 @@ private object FetchResource( } else { - if (_ownerApps != null) - { - deferredResourceReference = new DeferredAppResourceReference(this, resourceKey); - } - else - { - deferredResourceReference = new DeferredResourceReference(this, resourceKey); - } + deferredResourceReference = _ownerApps is not null ? new DeferredAppResourceReference(this, resourceKey) : new DeferredResourceReference(this, resourceKey); _deferredResourceReferences.AddOrSet(deferredResourceReference); } @@ -1781,10 +1771,29 @@ private object FetchResource( /// private void ValidateDeferredResourceReferences(object resourceKey) { - if (_deferredResourceReferences != null) + if (_deferredResourceReferences is null) + { + return; + } + + if (resourceKey is null) + { + foreach (DeferredResourceReference deferredResourceReference in _deferredResourceReferences) + { + Inflate(deferredResourceReference); + } + } + else { DeferredResourceReference deferredResourceReference = _deferredResourceReferences.Get(resourceKey); + Inflate(deferredResourceReference); + } + + return; + + void Inflate(DeferredResourceReference deferredResourceReference) + { if (deferredResourceReference is not null) { // This will inflate the deferred reference, causing it @@ -2504,9 +2513,9 @@ private enum PrivateFlags : byte /// /// This wrapper class exists so SourceUriTypeConverterMarkupExtension can pass - /// a more complete Uri to help resolve to the correct assembly, while also passing + /// a more complete Uri to help resolve to the correct assembly, while also passing /// the original Uri so that ResourceDictionary.Source still returns the original value. - /// + /// internal class ResourceDictionarySourceUriWrapper : Uri { public ResourceDictionarySourceUriWrapper(Uri originalUri, Uri versionedUri) : base(originalUri.OriginalString, UriKind.RelativeOrAbsolute) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceReferenceExpression.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceReferenceExpression.cs index 7ccb5555d60..24804f3dc2f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceReferenceExpression.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceReferenceExpression.cs @@ -278,7 +278,10 @@ private void InvalidateCacheValue() } } - deferredResourceReference.RemoveFromDictionary(); + // This will inflate the deferred reference, causing it + // to be removed from the list. The list may also be + // purged of dead references. + deferredResourceReference.GetValue(BaseValueSourceInternal.Unknown); } StopListeningForFreezableChanges(resource); @@ -341,8 +344,8 @@ private void InvalidateMentorCache() internal void InvalidateExpressionValue(object sender, EventArgs e) { // VS has a scenario where a TreeWalk invalidates all reference expressions on a DependencyObject. - // If there is a dependency between RRE's, - // invalidating one RRE could cause _targetObject to be null on the other RRE. Hence this check. + // If there is a dependency between RRE's, + // invalidating one RRE could cause _targetObject to be null on the other RRE. Hence this check. if (_targetObject == null) { return; @@ -404,7 +407,7 @@ private void ListenForFreezableChanges(object resource) { _weakContainerRRE = new ResourceReferenceExpressionWeakContainer(this); } - + // Hook up the event to the weak container to prevent memory leaks (Bug436021) _weakContainerRRE.AddChangedHandler(resourceAsFreezable); WriteInternalState(InternalState.IsListeningForFreezableChanges, true); @@ -435,7 +438,7 @@ private void StopListeningForFreezableChanges(object resource) } } - // It is possible that a freezable was unfrozen during the call to ListForFreezableChanges + // It is possible that a freezable was unfrozen during the call to ListForFreezableChanges // but was frozen before the call to StopListeningForFreezableChanges WriteInternalState(InternalState.IsListeningForFreezableChanges, false); } @@ -512,8 +515,8 @@ private enum InternalState : byte #region ResourceReferenceExpressionWeakContainer /// - /// ResourceReferenceExpressionWeakContainer handles the Freezable.Changed event - /// without holding a strong reference to ResourceReferenceExpression. + /// ResourceReferenceExpressionWeakContainer handles the Freezable.Changed event + /// without holding a strong reference to ResourceReferenceExpression. /// private class ResourceReferenceExpressionWeakContainer : WeakReference { @@ -542,7 +545,7 @@ public void AddChangedHandler(Freezable resource) } _resource = resource; - + Debug.Assert(!_resource.IsFrozen); _resource.Changed += new EventHandler(this.InvalidateTargetSubProperty); } @@ -558,7 +561,7 @@ public void RemoveChangedHandler() private Freezable _resource; } - #endregion + #endregion } /// diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs index 4a6622b9d01..280eaa21168 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs @@ -1412,7 +1412,7 @@ private static IntPtr SystemThemeFilterMessage(IntPtr hwnd, int msg, IntPtr wPar } SystemParameters.InvalidateWindowFrameThicknessProperties(); - + if(ThemeManager.IsFluentThemeEnabled) { ThemeManager.ApplySystemTheme(); @@ -1733,8 +1733,7 @@ internal override object GetValue(BaseValueSourceInternal valueSource) // the dictionary else just retun the cached value if (_dictionary != null) { - bool canCache; - object value = _dictionary.GetValue(_keyOrValue, out canCache); + object value = _dictionary.GetValue(_keyOrValue, out bool canCache); if (canCache) { // Note that we are replacing the _keyorValue field @@ -1790,8 +1789,7 @@ internal override Type GetValueType() { // Take a peek at the element type of the ElementStartRecord // within the ResourceDictionary's deferred content. - bool found; - return _dictionary.GetValueType(_keyOrValue, out found); + return _dictionary.GetValueType(_keyOrValue, out bool _); } else { @@ -1800,7 +1798,7 @@ internal override Type GetValueType() } // remove this DeferredResourceReference from its ResourceDictionary - internal virtual void RemoveFromDictionary() + protected virtual void RemoveFromDictionary() { if (_dictionary != null) { @@ -1970,7 +1968,7 @@ internal override Type GetValueType() } // remove this DeferredResourceReference from its ResourceDictionary - internal override void RemoveFromDictionary() + protected override void RemoveFromDictionary() { // DeferredThemeResourceReferences are never added to the dictionary's // list of deferred references, so they don't need to be removed. @@ -2034,11 +2032,11 @@ internal override bool IsUnset #endregion Properties } - internal class DeferredResourceReferenceList + internal class DeferredResourceReferenceList : IEnumerable { private readonly object _syncRoot = new(); private readonly Dictionary> _entries = new(); - private int _potentiallyDeadEntryCount = 0; + private int _potentiallyDeadEntryCount; public void AddOrSet(DeferredResourceReference deferredResourceReference) { @@ -2111,6 +2109,11 @@ private void PurgeIfRequired() } private void Purge() + { + Purge(null); + } + + private void Purge(List aliveItems) { lock (_syncRoot) { @@ -2119,10 +2122,14 @@ private void Purge() foreach (KeyValuePair> entry in _entries) { - if (entry.Value.TryGetTarget(out _) == false) + if (entry.Value.TryGetTarget(out var item) is false) { deadKeys.Add(entry.Key); } + else + { + aliveItems?.Add(item); + } } foreach (object deadKey in deadKeys) @@ -2131,5 +2138,17 @@ private void Purge() } } } + + public IEnumerator GetEnumerator() + { + var aliveItems = new List(_entries.Count); + Purge(aliveItems); + return aliveItems.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } } } diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/DependencyObject.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/DependencyObject.cs index 9c67254d12c..ccda5318a81 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/DependencyObject.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/DependencyObject.cs @@ -863,7 +863,7 @@ internal bool ProvideSelfAsInheritanceContext( DependencyObject doValue, Depende // on side-effects from setting the "Freezable context". Freezable's // implementation does its own checks of the conditions omitted here. // Enhancement suggestion: Freezable should follow the same rules for - // InheritanceContext as everyone else + // InheritanceContext as everyone else if (doValue != null && @@ -910,7 +910,7 @@ internal bool RemoveSelfAsInheritanceContext( DependencyObject doValue, Dependen // on side-effects from setting the "Freezable context". Freezable's // implementation does its own checks of the conditions omitted here. // Enhancement suggestion: Freezable should follow the same rules for - // InheritanceContext as everyone else + // InheritanceContext as everyone else if (doValue != null && @@ -3534,6 +3534,7 @@ internal enum UpdateResult } [FriendAccessAllowed] // Built into Base, also used by Framework. + [Flags] internal enum RequestFlags { FullyResolved = 0x00,