Skip to content

Commit 0be453c

Browse files
Merge pull request #9393 from batzen/issues/dynamicresource_fix
Fixing issues with DynamicResource usage
2 parents 903093e + 701607e commit 0be453c

File tree

4 files changed

+73
-41
lines changed

4 files changed

+73
-41
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceDictionary.cs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ private void CopyToWithoutLock(DictionaryEntry[] array, int arrayIndex)
109109
entry.Value = value; // refresh the entry value in case it was changed in the previous call
110110
}
111111
}
112-
112+
113113
// This is set when the RD is loaded from unsafe xps doc. This will be checked while creating reader for RD source.
114114
internal bool IsUnsafe { get; set; }
115115

@@ -157,7 +157,7 @@ public Uri Source
157157
// that it is being passed down by the Baml parsing code, and it is trying to give us more
158158
// information to avoid possible ambiguities in assembly resolving. Use the VersionedUri
159159
// to resolve, and the set _source to the OriginalUri so we don't change the return of Source property.
160-
// The versioned Uri is not stored, if the version info is needed while debugging, once this method
160+
// The versioned Uri is not stored, if the version info is needed while debugging, once this method
161161
// returns _reader should be set, from there BamlSchemaContext.LocalAssembly contains the version info.
162162
if (uriWrapper == null)
163163
{
@@ -169,10 +169,10 @@ public Uri Source
169169
_source = uriWrapper.OriginalUri;
170170
sourceUri = uriWrapper.VersionedUri;
171171
}
172-
172+
173173
Clear();
174-
175-
174+
175+
176176
Uri uri = BindUriHelper.GetResolvedUri(_baseUri, sourceUri);
177177

178178
WebRequest request = WpfWebRequestHelper.CreateRequest(uri);
@@ -1737,10 +1737,7 @@ private object FetchResource(
17371737
{
17381738
// Cache the deferredResourceReference so that it can be validated
17391739
// in case of a dictionary change prior to its inflation
1740-
if (_deferredResourceReferences == null)
1741-
{
1742-
_deferredResourceReferences = new DeferredResourceReferenceList();
1743-
}
1740+
_deferredResourceReferences ??= new DeferredResourceReferenceList();
17441741

17451742
if (_deferredResourceReferences.Get(resourceKey) is { } existingDeferredResourceReference
17461743
&& existingDeferredResourceReference.Dictionary == this)
@@ -1749,14 +1746,7 @@ private object FetchResource(
17491746
}
17501747
else
17511748
{
1752-
if (_ownerApps != null)
1753-
{
1754-
deferredResourceReference = new DeferredAppResourceReference(this, resourceKey);
1755-
}
1756-
else
1757-
{
1758-
deferredResourceReference = new DeferredResourceReference(this, resourceKey);
1759-
}
1749+
deferredResourceReference = _ownerApps is not null ? new DeferredAppResourceReference(this, resourceKey) : new DeferredResourceReference(this, resourceKey);
17601750

17611751
_deferredResourceReferences.AddOrSet(deferredResourceReference);
17621752
}
@@ -1781,10 +1771,29 @@ private object FetchResource(
17811771
/// </summary>
17821772
private void ValidateDeferredResourceReferences(object resourceKey)
17831773
{
1784-
if (_deferredResourceReferences != null)
1774+
if (_deferredResourceReferences is null)
1775+
{
1776+
return;
1777+
}
1778+
1779+
if (resourceKey is null)
1780+
{
1781+
foreach (DeferredResourceReference deferredResourceReference in _deferredResourceReferences)
1782+
{
1783+
Inflate(deferredResourceReference);
1784+
}
1785+
}
1786+
else
17851787
{
17861788
DeferredResourceReference deferredResourceReference = _deferredResourceReferences.Get(resourceKey);
17871789

1790+
Inflate(deferredResourceReference);
1791+
}
1792+
1793+
return;
1794+
1795+
void Inflate(DeferredResourceReference deferredResourceReference)
1796+
{
17881797
if (deferredResourceReference is not null)
17891798
{
17901799
// This will inflate the deferred reference, causing it
@@ -2504,9 +2513,9 @@ private enum PrivateFlags : byte
25042513

25052514
/// <summary>
25062515
/// This wrapper class exists so SourceUriTypeConverterMarkupExtension can pass
2507-
/// a more complete Uri to help resolve to the correct assembly, while also passing
2516+
/// a more complete Uri to help resolve to the correct assembly, while also passing
25082517
/// the original Uri so that ResourceDictionary.Source still returns the original value.
2509-
/// </summary>
2518+
/// </summary>
25102519
internal class ResourceDictionarySourceUriWrapper : Uri
25112520
{
25122521
public ResourceDictionarySourceUriWrapper(Uri originalUri, Uri versionedUri) : base(originalUri.OriginalString, UriKind.RelativeOrAbsolute)

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceReferenceExpression.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,10 @@ private void InvalidateCacheValue()
278278
}
279279
}
280280

281-
deferredResourceReference.RemoveFromDictionary();
281+
// This will inflate the deferred reference, causing it
282+
// to be removed from the list. The list may also be
283+
// purged of dead references.
284+
deferredResourceReference.GetValue(BaseValueSourceInternal.Unknown);
282285
}
283286

284287
StopListeningForFreezableChanges(resource);
@@ -341,8 +344,8 @@ private void InvalidateMentorCache()
341344
internal void InvalidateExpressionValue(object sender, EventArgs e)
342345
{
343346
// VS has a scenario where a TreeWalk invalidates all reference expressions on a DependencyObject.
344-
// If there is a dependency between RRE's,
345-
// invalidating one RRE could cause _targetObject to be null on the other RRE. Hence this check.
347+
// If there is a dependency between RRE's,
348+
// invalidating one RRE could cause _targetObject to be null on the other RRE. Hence this check.
346349
if (_targetObject == null)
347350
{
348351
return;
@@ -404,7 +407,7 @@ private void ListenForFreezableChanges(object resource)
404407
{
405408
_weakContainerRRE = new ResourceReferenceExpressionWeakContainer(this);
406409
}
407-
410+
408411
// Hook up the event to the weak container to prevent memory leaks (Bug436021)
409412
_weakContainerRRE.AddChangedHandler(resourceAsFreezable);
410413
WriteInternalState(InternalState.IsListeningForFreezableChanges, true);
@@ -435,7 +438,7 @@ private void StopListeningForFreezableChanges(object resource)
435438
}
436439
}
437440

438-
// It is possible that a freezable was unfrozen during the call to ListForFreezableChanges
441+
// It is possible that a freezable was unfrozen during the call to ListForFreezableChanges
439442
// but was frozen before the call to StopListeningForFreezableChanges
440443
WriteInternalState(InternalState.IsListeningForFreezableChanges, false);
441444
}
@@ -512,8 +515,8 @@ private enum InternalState : byte
512515
#region ResourceReferenceExpressionWeakContainer
513516

514517
/// <summary>
515-
/// ResourceReferenceExpressionWeakContainer handles the Freezable.Changed event
516-
/// without holding a strong reference to ResourceReferenceExpression.
518+
/// ResourceReferenceExpressionWeakContainer handles the Freezable.Changed event
519+
/// without holding a strong reference to ResourceReferenceExpression.
517520
/// </summary>
518521
private class ResourceReferenceExpressionWeakContainer : WeakReference
519522
{
@@ -542,7 +545,7 @@ public void AddChangedHandler(Freezable resource)
542545
}
543546

544547
_resource = resource;
545-
548+
546549
Debug.Assert(!_resource.IsFrozen);
547550
_resource.Changed += new EventHandler(this.InvalidateTargetSubProperty);
548551
}
@@ -558,7 +561,7 @@ public void RemoveChangedHandler()
558561

559562
private Freezable _resource;
560563
}
561-
#endregion
564+
#endregion
562565
}
563566

564567
/// <summary>

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,7 +1412,7 @@ private static IntPtr SystemThemeFilterMessage(IntPtr hwnd, int msg, IntPtr wPar
14121412
}
14131413

14141414
SystemParameters.InvalidateWindowFrameThicknessProperties();
1415-
1415+
14161416
if(ThemeManager.IsFluentThemeEnabled)
14171417
{
14181418
ThemeManager.ApplySystemTheme();
@@ -1739,8 +1739,7 @@ internal override object GetValue(BaseValueSourceInternal valueSource)
17391739
// the dictionary else just retun the cached value
17401740
if (_dictionary != null)
17411741
{
1742-
bool canCache;
1743-
object value = _dictionary.GetValue(_keyOrValue, out canCache);
1742+
object value = _dictionary.GetValue(_keyOrValue, out bool canCache);
17441743
if (canCache)
17451744
{
17461745
// Note that we are replacing the _keyorValue field
@@ -1796,8 +1795,7 @@ internal override Type GetValueType()
17961795
{
17971796
// Take a peek at the element type of the ElementStartRecord
17981797
// within the ResourceDictionary's deferred content.
1799-
bool found;
1800-
return _dictionary.GetValueType(_keyOrValue, out found);
1798+
return _dictionary.GetValueType(_keyOrValue, out bool _);
18011799
}
18021800
else
18031801
{
@@ -1806,7 +1804,7 @@ internal override Type GetValueType()
18061804
}
18071805

18081806
// remove this DeferredResourceReference from its ResourceDictionary
1809-
internal virtual void RemoveFromDictionary()
1807+
protected virtual void RemoveFromDictionary()
18101808
{
18111809
if (_dictionary != null)
18121810
{
@@ -1976,7 +1974,7 @@ internal override Type GetValueType()
19761974
}
19771975

19781976
// remove this DeferredResourceReference from its ResourceDictionary
1979-
internal override void RemoveFromDictionary()
1977+
protected override void RemoveFromDictionary()
19801978
{
19811979
// DeferredThemeResourceReferences are never added to the dictionary's
19821980
// list of deferred references, so they don't need to be removed.
@@ -2040,11 +2038,11 @@ internal override bool IsUnset
20402038
#endregion Properties
20412039
}
20422040

2043-
internal class DeferredResourceReferenceList
2041+
internal class DeferredResourceReferenceList : IEnumerable<DeferredResourceReference>
20442042
{
20452043
private readonly object _syncRoot = new();
20462044
private readonly Dictionary<object, WeakReference<DeferredResourceReference>> _entries = new();
2047-
private int _potentiallyDeadEntryCount = 0;
2045+
private int _potentiallyDeadEntryCount;
20482046

20492047
public void AddOrSet(DeferredResourceReference deferredResourceReference)
20502048
{
@@ -2117,6 +2115,11 @@ private void PurgeIfRequired()
21172115
}
21182116

21192117
private void Purge()
2118+
{
2119+
Purge(null);
2120+
}
2121+
2122+
private void Purge(List<DeferredResourceReference> aliveItems)
21202123
{
21212124
lock (_syncRoot)
21222125
{
@@ -2125,10 +2128,14 @@ private void Purge()
21252128

21262129
foreach (KeyValuePair<object, WeakReference<DeferredResourceReference>> entry in _entries)
21272130
{
2128-
if (entry.Value.TryGetTarget(out _) == false)
2131+
if (entry.Value.TryGetTarget(out var item) is false)
21292132
{
21302133
deadKeys.Add(entry.Key);
21312134
}
2135+
else
2136+
{
2137+
aliveItems?.Add(item);
2138+
}
21322139
}
21332140

21342141
foreach (object deadKey in deadKeys)
@@ -2137,5 +2144,17 @@ private void Purge()
21372144
}
21382145
}
21392146
}
2147+
2148+
public IEnumerator<DeferredResourceReference> GetEnumerator()
2149+
{
2150+
var aliveItems = new List<DeferredResourceReference>(_entries.Count);
2151+
Purge(aliveItems);
2152+
return aliveItems.GetEnumerator();
2153+
}
2154+
2155+
IEnumerator IEnumerable.GetEnumerator()
2156+
{
2157+
return GetEnumerator();
2158+
}
21402159
}
21412160
}

src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/DependencyObject.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ internal bool ProvideSelfAsInheritanceContext( DependencyObject doValue, Depende
863863
// on side-effects from setting the "Freezable context". Freezable's
864864
// implementation does its own checks of the conditions omitted here.
865865
// Enhancement suggestion: Freezable should follow the same rules for
866-
// InheritanceContext as everyone else
866+
// InheritanceContext as everyone else
867867

868868

869869
if (doValue != null &&
@@ -910,7 +910,7 @@ internal bool RemoveSelfAsInheritanceContext( DependencyObject doValue, Dependen
910910
// on side-effects from setting the "Freezable context". Freezable's
911911
// implementation does its own checks of the conditions omitted here.
912912
// Enhancement suggestion: Freezable should follow the same rules for
913-
// InheritanceContext as everyone else
913+
// InheritanceContext as everyone else
914914

915915

916916
if (doValue != null &&
@@ -3534,6 +3534,7 @@ internal enum UpdateResult
35343534
}
35353535

35363536
[FriendAccessAllowed] // Built into Base, also used by Framework.
3537+
[Flags]
35373538
internal enum RequestFlags
35383539
{
35393540
FullyResolved = 0x00,

0 commit comments

Comments
 (0)