зеркало из https://github.com/DeGsoft/maui-linux.git
[X] Allow tracking the assignment of StaticResource (#6868)
This allows advanced tracking of SR for debugging purposes. It doesn't affects apps running without the debugger. See also https://docs.microsoft.com/en-us/dotnet/api/system.windows.diagnostics.resourcedictionarydiagnostics
This commit is contained in:
Родитель
88577eac76
Коммит
897242a003
|
@ -260,19 +260,25 @@ namespace Xamarin.Forms
|
|||
}
|
||||
|
||||
public bool TryGetValue(string key, out object value)
|
||||
=> TryGetValueAndSource(key, out value, out _);
|
||||
|
||||
internal bool TryGetValueAndSource(string key, out object value, out ResourceDictionary source)
|
||||
{
|
||||
source = this;
|
||||
return _innerDictionary.TryGetValue(key, out value)
|
||||
|| (_mergedInstance != null && _mergedInstance.TryGetValue(key, out value))
|
||||
|| (MergedDictionaries != null && TryGetMergedDictionaryValue(key, out value));
|
||||
|| (_mergedInstance != null && _mergedInstance.TryGetValueAndSource(key, out value, out source))
|
||||
|| (MergedDictionaries != null && TryGetMergedDictionaryValue(key, out value, out source));
|
||||
}
|
||||
|
||||
bool TryGetMergedDictionaryValue(string key, out object value)
|
||||
bool TryGetMergedDictionaryValue(string key, out object value, out ResourceDictionary source)
|
||||
{
|
||||
foreach (var dictionary in MergedDictionaries.Reverse())
|
||||
if (dictionary.TryGetValue(key, out value))
|
||||
if (dictionary.TryGetValue(key, out value)) {
|
||||
source = dictionary;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = null;
|
||||
value = null; source = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
namespace Xamarin.Forms.Xaml.Diagnostics
|
||||
{
|
||||
internal static class ResourceDictionaryDiagnostics
|
||||
{
|
||||
internal static void OnStaticResourceResolved(ResourceDictionary resourceDictionary, string key, object targetObject, object targetProperty)
|
||||
=> StaticResourceResolved?.Invoke(resourceDictionary, new StaticResourceResolvedEventArgs(resourceDictionary, key, targetObject, targetProperty));
|
||||
public static event EventHandler<StaticResourceResolvedEventArgs> StaticResourceResolved;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
namespace Xamarin.Forms.Xaml.Diagnostics
|
||||
{
|
||||
internal class StaticResourceResolvedEventArgs : EventArgs
|
||||
{
|
||||
internal StaticResourceResolvedEventArgs(ResourceDictionary resourceDictionary, string key, object targetObject, object targetProperty)
|
||||
{
|
||||
ResourceDictionary = resourceDictionary;
|
||||
Key = key;
|
||||
TargetObject = targetObject;
|
||||
TargetProperty = targetProperty;
|
||||
}
|
||||
|
||||
public ResourceDictionary ResourceDictionary { get; }
|
||||
public string Key { get; }
|
||||
public object TargetObject { get; }
|
||||
public object TargetProperty { get; }
|
||||
}
|
||||
}
|
|
@ -3,14 +3,16 @@ using System.Reflection;
|
|||
using System.Xml;
|
||||
using System.Linq;
|
||||
using Xamarin.Forms.Internals;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Xamarin.Forms.Xaml
|
||||
{
|
||||
[ContentProperty(nameof(Key))]
|
||||
public sealed class StaticResourceExtension : IMarkupExtension
|
||||
{
|
||||
public string Key { get; set; }
|
||||
internal static bool _mockDebuggerIsAttached; //for unit testing
|
||||
|
||||
public string Key { get; set; }
|
||||
public object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
if (serviceProvider == null)
|
||||
|
@ -21,64 +23,86 @@ namespace Xamarin.Forms.Xaml
|
|||
throw new ArgumentException();
|
||||
|
||||
var xmlLineInfo = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) is IXmlLineInfoProvider xmlLineInfoProvider ? xmlLineInfoProvider.XmlLineInfo : null;
|
||||
object resource = null;
|
||||
|
||||
foreach (var p in valueProvider.ParentObjects) {
|
||||
var resDict = p is IResourcesProvider irp && irp.IsResourcesCreated ? irp.Resources : p as ResourceDictionary;
|
||||
if (resDict == null)
|
||||
continue;
|
||||
if (resDict.TryGetValue(Key, out resource))
|
||||
break;
|
||||
}
|
||||
resource = resource ?? GetApplicationLevelResource(Key, xmlLineInfo);
|
||||
if ( !TryGetResource(Key, valueProvider.ParentObjects, out var resource, out var resourceDictionary)
|
||||
&& !TryGetApplicationLevelResource(Key, out resource, out resourceDictionary))
|
||||
throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo);
|
||||
|
||||
var bp = valueProvider.TargetProperty as BindableProperty;
|
||||
var pi = valueProvider.TargetProperty as PropertyInfo;
|
||||
if (System.Diagnostics.Debugger.IsAttached || _mockDebuggerIsAttached)
|
||||
Diagnostics.ResourceDictionaryDiagnostics.OnStaticResourceResolved(resourceDictionary, Key, valueProvider.TargetObject, valueProvider.TargetProperty);
|
||||
|
||||
return CastTo(resource, valueProvider.TargetProperty);
|
||||
}
|
||||
|
||||
//used by X.HR.F
|
||||
internal static object CastTo(object value, object targetProperty)
|
||||
{
|
||||
var bp = targetProperty as BindableProperty;
|
||||
var pi = targetProperty as PropertyInfo;
|
||||
var propertyType = bp?.ReturnType ?? pi?.PropertyType;
|
||||
if (propertyType == null) {
|
||||
if (resource.GetType().GetTypeInfo().IsGenericType && (resource.GetType().GetGenericTypeDefinition() == typeof(OnPlatform<>) || resource.GetType().GetGenericTypeDefinition() == typeof(OnIdiom<>))) {
|
||||
if (propertyType == null)
|
||||
{
|
||||
if (value.GetType().GetTypeInfo().IsGenericType && (value.GetType().GetGenericTypeDefinition() == typeof(OnPlatform<>) || value.GetType().GetGenericTypeDefinition() == typeof(OnIdiom<>)))
|
||||
{
|
||||
// This is only there to support our backward compat story with pre 2.3.3 compiled Xaml project who was not providing TargetProperty
|
||||
var method = resource.GetType().GetRuntimeMethod("op_Implicit", new[] { resource.GetType() });
|
||||
resource = method.Invoke(null, new[] { resource });
|
||||
var method = value.GetType().GetRuntimeMethod("op_Implicit", new[] { value.GetType() });
|
||||
value = method.Invoke(null, new[] { value });
|
||||
}
|
||||
return resource;
|
||||
return value;
|
||||
}
|
||||
if (propertyType.IsAssignableFrom(resource.GetType()))
|
||||
return resource;
|
||||
var implicit_op = resource.GetType().GetImplicitConversionOperator(fromType: resource.GetType(), toType: propertyType)
|
||||
?? propertyType.GetImplicitConversionOperator(fromType: resource.GetType(), toType: propertyType);
|
||||
if (propertyType.IsAssignableFrom(value.GetType()))
|
||||
return value;
|
||||
var implicit_op = value.GetType().GetImplicitConversionOperator(fromType: value.GetType(), toType: propertyType)
|
||||
?? propertyType.GetImplicitConversionOperator(fromType: value.GetType(), toType: propertyType);
|
||||
if (implicit_op != null)
|
||||
return implicit_op.Invoke(resource, new [] { resource });
|
||||
return implicit_op.Invoke(value, new[] { value });
|
||||
|
||||
//Special case for https://bugzilla.xamarin.com/show_bug.cgi?id=59818
|
||||
//On OnPlatform, check for an opImplicit from the targetType
|
||||
if ( Device.Flags != null
|
||||
if (Device.Flags != null
|
||||
&& Device.Flags.Contains("xamlDoubleImplicitOpHack")
|
||||
&& resource.GetType().GetTypeInfo().IsGenericType
|
||||
&& (resource.GetType().GetGenericTypeDefinition() == typeof(OnPlatform<>))) {
|
||||
var tType = resource.GetType().GenericTypeArguments[0];
|
||||
&& value.GetType().GetTypeInfo().IsGenericType
|
||||
&& (value.GetType().GetGenericTypeDefinition() == typeof(OnPlatform<>)))
|
||||
{
|
||||
var tType = value.GetType().GenericTypeArguments[0];
|
||||
var opImplicit = tType.GetImplicitConversionOperator(fromType: tType, toType: propertyType)
|
||||
?? propertyType.GetImplicitConversionOperator(fromType: tType, toType: propertyType);
|
||||
|
||||
if (opImplicit != null) {
|
||||
if (opImplicit != null)
|
||||
{
|
||||
//convert the OnPlatform<T> to T
|
||||
var opPlatformImplicitConversionOperator = resource.GetType().GetImplicitConversionOperator(fromType: resource.GetType(), toType: tType);
|
||||
resource = opPlatformImplicitConversionOperator.Invoke(null, new[] { resource });
|
||||
var opPlatformImplicitConversionOperator = value.GetType().GetImplicitConversionOperator(fromType: value.GetType(), toType: tType);
|
||||
value = opPlatformImplicitConversionOperator.Invoke(null, new[] { value });
|
||||
|
||||
//and convert to toType
|
||||
resource = opImplicit.Invoke(null, new[] { resource });
|
||||
return resource;
|
||||
value = opImplicit.Invoke(null, new[] { value });
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return resource;
|
||||
return value;
|
||||
}
|
||||
|
||||
internal object GetApplicationLevelResource(string key, IXmlLineInfo xmlLineInfo)
|
||||
bool TryGetResource(string key, IEnumerable<object> parentObjects, out object resource, out ResourceDictionary resourceDictionary)
|
||||
{
|
||||
if (Application.Current == null || !((IResourcesProvider)Application.Current).IsResourcesCreated || !Application.Current.Resources.TryGetValue(Key, out object resource))
|
||||
throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo);
|
||||
return resource;
|
||||
resource = null;
|
||||
resourceDictionary = null;
|
||||
|
||||
foreach (var p in parentObjects) {
|
||||
var resDict = p is IResourcesProvider irp && irp.IsResourcesCreated ? irp.Resources : p as ResourceDictionary;
|
||||
if (resDict == null)
|
||||
continue;
|
||||
if (resDict.TryGetValueAndSource(Key, out resource, out resourceDictionary))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryGetApplicationLevelResource(string key, out object resource, out ResourceDictionary resourceDictionary)
|
||||
{
|
||||
resource = null;
|
||||
resourceDictionary = null;
|
||||
return Application.Current != null && ((IResourcesProvider)Application.Current).IsResourcesCreated && Application.Current.Resources.TryGetValueAndSource (key, out resource, out resourceDictionary);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using Xamarin.Forms.Internals;
|
|||
[assembly: InternalsVisibleTo("Xamarin.Forms.Xaml.Design")]
|
||||
[assembly: InternalsVisibleTo("Xamarin.Forms.Loader")]// Xamarin.Forms.Loader.dll Xamarin.Forms.Xaml.XamlLoader.Load(object, string), kzu@microsoft.com
|
||||
[assembly: InternalsVisibleTo("Xamarin.HotReload.Forms")]
|
||||
[assembly: InternalsVisibleTo("Xamarin.HotReload.UnitTests")]
|
||||
[assembly: Preserve]
|
||||
|
||||
[assembly: XmlnsDefinition("http://xamarin.com/schemas/2014/forms", "Xamarin.Forms.Xaml")]
|
||||
|
|
|
@ -10,4 +10,7 @@
|
|||
<ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Diagnostics\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
Загрузка…
Ссылка в новой задаче