Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

Commit 4dc9761

Browse files
[X] Add a TrySetPropertyValue overload for HR (#12043)
- fixes AB#1192221
1 parent a8a617c commit 4dc9761

File tree

2 files changed

+61
-48
lines changed

2 files changed

+61
-48
lines changed

Xamarin.Forms.Xaml.UnitTests/Issues/Bz47950.xaml.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using NUnit.Framework;
44
using Xamarin.Forms;
5+
using Xamarin.Forms.Core.UnitTests;
56

67
namespace Xamarin.Forms.Xaml.UnitTests
78
{
@@ -29,9 +30,11 @@ public Bz47950(bool useCompiledXaml)
2930
[TestFixture]
3031
class Tests
3132
{
32-
[TestCase(true)]
33-
[TestCase(false)]
34-
public void BehaviorAndStaticResource(bool useCompiledXaml)
33+
[SetUp] public void Setup() => Device.PlatformServices = new MockPlatformServices();
34+
[TearDown] public void TearDown() => Device.PlatformServices = null;
35+
36+
[Test]
37+
public void BehaviorAndStaticResource([Values(false, true)] bool useCompiledXaml)
3538
{
3639
var page = new Bz47950(useCompiledXaml);
3740
}

Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public void Visit(ElementNode node, INode parentNode)
158158
Exception xpe = null;
159159
var xKey = node.Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)node.Properties[XmlName.xKey]).Value as string : null;
160160

161-
var collection = GetPropertyValue(source, parentList.XmlName, Context, parentList, out _, out _) as IEnumerable;
161+
var collection = GetPropertyValue(source, parentList.XmlName, Context.RootElement, parentList, out _, out _) as IEnumerable;
162162
if (collection == null)
163163
xpe = new XamlParseException($"Property {parentList.XmlName.LocalName} is null or is not IEnumerable", node);
164164

@@ -235,7 +235,7 @@ void ProvideValue(ref object value, ElementNode node, object source, XmlName pro
235235
serviceProvider = new XamlServiceProvider(node, Context);
236236

237237
if (serviceProvider != null && propertyName != XmlName.Empty)
238-
((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = GetTargetProperty(source, propertyName, Context, node);
238+
((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = GetTargetProperty(source, propertyName, Context.RootElement, node);
239239

240240
try {
241241
if (markupExtension != null)
@@ -262,15 +262,15 @@ static string GetContentPropertyName(IEnumerable<CustomAttributeData> attributes
262262
}
263263

264264
static bool GetRealNameAndType(ref Type elementType, string namespaceURI, ref string localname,
265-
HydrationContext context, IXmlLineInfo lineInfo)
265+
object rootElement, IXmlLineInfo lineInfo)
266266
{
267267
var dotIdx = localname.IndexOf('.');
268268
if (dotIdx > 0) {
269269
var typename = localname.Substring(0, dotIdx);
270270
localname = localname.Substring(dotIdx + 1);
271271
XamlParseException xpe;
272272
elementType = XamlParser.GetElementType(new XmlType(namespaceURI, typename, null), lineInfo,
273-
context.RootElement.GetType().GetTypeInfo().Assembly, out xpe);
273+
rootElement.GetType().GetTypeInfo().Assembly, out xpe);
274274

275275
if (xpe != null)
276276
throw xpe;
@@ -304,12 +304,12 @@ static BindableProperty GetBindableProperty(Type elementType, string localName,
304304
return null;
305305
}
306306

307-
static object GetTargetProperty(object xamlelement, XmlName propertyName, HydrationContext context, IXmlLineInfo lineInfo)
307+
static object GetTargetProperty(object xamlelement, XmlName propertyName, object rootElement, IXmlLineInfo lineInfo)
308308
{
309309
var localName = propertyName.LocalName;
310310
//If it's an attached BP, update elementType and propertyName
311311
var bpOwnerType = xamlelement.GetType();
312-
GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
312+
GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, rootElement, lineInfo);
313313
var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
314314

315315
if (property != null)
@@ -322,64 +322,75 @@ static object GetTargetProperty(object xamlelement, XmlName propertyName, Hydrat
322322

323323
public static void SetPropertyValue(object xamlelement, XmlName propertyName, object value, object rootElement, INode node, HydrationContext context, IXmlLineInfo lineInfo)
324324
{
325-
var localName = propertyName.LocalName;
326325
var serviceProvider = new XamlServiceProvider(node, context);
327-
Exception xpe = null;
328326
var xKey = node is IElementNode && ((IElementNode)node).Properties.ContainsKey(XmlName.xKey) ? ((ValueNode)((IElementNode)node).Properties[XmlName.xKey]).Value as string : null;
329327

328+
if (TrySetPropertyValue(xamlelement, propertyName, xKey, value, rootElement, lineInfo, serviceProvider, out var xpe))
329+
return;
330+
331+
if (context.ExceptionHandler != null)
332+
context.ExceptionHandler(xpe);
333+
else
334+
throw xpe;
335+
336+
}
337+
338+
//Used by HotReload, do not change signature
339+
public static bool TrySetPropertyValue(object element, XmlName propertyName, string xKey, object value, object rootElement, IXmlLineInfo lineInfo, IServiceProvider serviceProvider, out Exception xpe)
340+
{
341+
var localName = propertyName.LocalName;
342+
xpe = null;
343+
330344
//If it's an attached BP, update elementType and propertyName
331-
var bpOwnerType = xamlelement.GetType();
332-
var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
345+
var bpOwnerType = element.GetType();
346+
var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, rootElement, lineInfo);
333347
var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
334348

335349
//If the target is an event, connect
336-
if (xpe == null && TryConnectEvent(xamlelement, localName, attached, value, rootElement, lineInfo, out xpe))
337-
return;
350+
if (xpe == null && TryConnectEvent(element, localName, attached, value, rootElement, lineInfo, out xpe))
351+
return true;
338352

339353
//If Value is DynamicResource and it's a BP, SetDynamicResource
340-
if (xpe == null && TrySetDynamicResource(xamlelement, property, value, lineInfo, out xpe))
341-
return;
354+
if (xpe == null && TrySetDynamicResource(element, property, value, lineInfo, out xpe))
355+
return true;
342356

343357
//If value is BindingBase, SetBinding
344-
if (xpe == null && TrySetBinding(xamlelement, property, localName, value, lineInfo, out xpe))
345-
return;
358+
if (xpe == null && TrySetBinding(element, property, localName, value, lineInfo, out xpe))
359+
return true;
346360

347361
//If it's a BindableProberty, SetValue
348-
if (xpe == null && TrySetValue(xamlelement, property, attached, value, lineInfo, serviceProvider, out xpe))
349-
return;
362+
if (xpe == null && TrySetValue(element, property, attached, value, lineInfo, serviceProvider, out xpe))
363+
return true;
350364

351365
//If we can assign that value to a normal property, let's do it
352-
if (xpe == null && TrySetProperty(xamlelement, localName, value, lineInfo, serviceProvider, context, out xpe))
353-
return;
366+
if (xpe == null && TrySetProperty(element, localName, value, lineInfo, serviceProvider, rootElement, out xpe))
367+
return true;
354368

355369
//If it's an already initialized property, add to it
356-
if (xpe == null && TryAddToProperty(xamlelement, propertyName, value, xKey, lineInfo, serviceProvider, context, out xpe))
357-
return;
370+
if (xpe == null && TryAddToProperty(element, propertyName, value, xKey, lineInfo, serviceProvider, rootElement, out xpe))
371+
return true;
358372

359373
xpe = xpe ?? new XamlParseException($"Cannot assign property \"{localName}\": Property does not exist, or is not assignable, or mismatching type between value and property", lineInfo);
360-
if (context.ExceptionHandler != null)
361-
context.ExceptionHandler(xpe);
362-
else
363-
throw xpe;
374+
return false;
364375
}
365376

366-
public static object GetPropertyValue(object xamlElement, XmlName propertyName, HydrationContext context, IXmlLineInfo lineInfo, out Exception xpe, out object targetProperty)
377+
public static object GetPropertyValue(object xamlElement, XmlName propertyName, object rootElement, IXmlLineInfo lineInfo, out Exception xpe, out object targetProperty)
367378
{
368379
var localName = propertyName.LocalName;
369380
xpe = null;
370381
targetProperty = null;
371382

372383
//If it's an attached BP, update elementType and propertyName
373384
var bpOwnerType = xamlElement.GetType();
374-
var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, context, lineInfo);
385+
var attached = GetRealNameAndType(ref bpOwnerType, propertyName.NamespaceURI, ref localName, rootElement, lineInfo);
375386
var property = GetBindableProperty(bpOwnerType, localName, lineInfo, false);
376387

377388
//If it's a BindableProberty, GetValue
378389
if (xpe == null && TryGetValue(xamlElement, property, attached, out var value, lineInfo, out xpe, out targetProperty))
379390
return value;
380391

381392
//If it's a normal property, get it
382-
if (xpe == null && TryGetProperty(xamlElement, localName, out value, lineInfo, context, out xpe, out targetProperty))
393+
if (xpe == null && TryGetProperty(xamlElement, localName, out value, lineInfo, rootElement, out xpe, out targetProperty))
383394
return value;
384395

385396
xpe = xpe ?? new XamlParseException($"Property {localName} is not found or does not have an accessible getter", lineInfo);
@@ -467,19 +478,18 @@ static bool TrySetBinding(object element, BindableProperty property, string loca
467478
return false;
468479
}
469480

470-
static bool TrySetValue(object element, BindableProperty property, bool attached, object value, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, out Exception exception)
481+
static bool TrySetValue(object element, BindableProperty property, bool attached, object value, IXmlLineInfo lineInfo, IServiceProvider serviceProvider, out Exception exception)
471482
{
472483
exception = null;
473484

474485
var elementType = element.GetType();
475-
var bindable = element as BindableObject;
476486
var nativeBindingService = DependencyService.Get<INativeBindingService>();
477487

478488
if (property == null)
479489
return false;
480490

481-
if (serviceProvider != null && serviceProvider.IProvideValueTarget != null)
482-
((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = property;
491+
if (serviceProvider?.GetService<IProvideValueTarget>() is XamlValueTargetProvider valueTargetProvider)
492+
valueTargetProvider.TargetProperty = property;
483493

484494
Func<MemberInfo> minforetriever;
485495
if (attached)
@@ -504,7 +514,7 @@ static bool TrySetValue(object element, BindableProperty property, bool attached
504514
if (exception != null)
505515
return false;
506516

507-
if (bindable != null) {
517+
if (element is BindableObject bindable) {
508518
//SetValue doesn't throw on mismatching type, so check before to get a chance to try the property setting or the collection adding
509519
var nullable = property.ReturnTypeInfo.IsGenericType &&
510520
property.ReturnTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>);
@@ -549,7 +559,7 @@ static bool TryGetValue(object element, BindableProperty property, bool attached
549559
return true;
550560
}
551561

552-
static bool TrySetProperty(object element, string localName, object value, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, HydrationContext context, out Exception exception)
562+
static bool TrySetProperty(object element, string localName, object value, IXmlLineInfo lineInfo, IServiceProvider serviceProvider, object rootElement, out Exception exception)
553563
{
554564
exception = null;
555565

@@ -559,11 +569,11 @@ static bool TrySetProperty(object element, string localName, object value, IXmlL
559569
if (propertyInfo == null || !propertyInfo.CanWrite || (setter = propertyInfo.SetMethod) == null)
560570
return false;
561571

562-
if (!IsVisibleFrom(setter, context.RootElement))
572+
if (!IsVisibleFrom(setter, rootElement))
563573
return false;
564574

565-
if (serviceProvider != null && serviceProvider.IProvideValueTarget != null)
566-
((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = propertyInfo;
575+
if(serviceProvider?.GetService<IProvideValueTarget>() is XamlValueTargetProvider valueTargetProvider)
576+
valueTargetProvider.TargetProperty = propertyInfo;
567577

568578
object convertedValue = value.ConvertTo(propertyInfo.PropertyType, () => propertyInfo, serviceProvider, out exception);
569579
if (exception != null || (convertedValue != null && !propertyInfo.PropertyType.IsInstanceOfType(convertedValue)))
@@ -579,7 +589,7 @@ static bool TrySetProperty(object element, string localName, object value, IXmlL
579589
}
580590
}
581591

582-
static bool TryGetProperty(object element, string localName, out object value, IXmlLineInfo lineInfo, HydrationContext context, out Exception exception, out object targetProperty)
592+
static bool TryGetProperty(object element, string localName, out object value, IXmlLineInfo lineInfo, object rootElement, out Exception exception, out object targetProperty)
583593
{
584594
exception = null;
585595
value = null;
@@ -610,7 +620,7 @@ static bool TryGetProperty(object element, string localName, out object value, I
610620
if (propertyInfo == null || !propertyInfo.CanRead || (getter = propertyInfo.GetMethod) == null)
611621
return false;
612622

613-
if (!IsVisibleFrom(getter, context.RootElement))
623+
if (!IsVisibleFrom(getter, rootElement))
614624
return false;
615625

616626
value = getter.Invoke(element, new object[] { });
@@ -630,10 +640,10 @@ static bool IsVisibleFrom(MethodInfo method, object rootElement)
630640
return false;
631641
}
632642

633-
static bool TryAddToProperty(object element, XmlName propertyName, object value, string xKey, IXmlLineInfo lineInfo, XamlServiceProvider serviceProvider, HydrationContext context, out Exception exception)
643+
static bool TryAddToProperty(object element, XmlName propertyName, object value, string xKey, IXmlLineInfo lineInfo, IServiceProvider serviceProvider, object rootElement, out Exception exception)
634644
{
635645
exception = null;
636-
if (!(GetPropertyValue(element, propertyName, context, lineInfo, out _, out var targetProperty) is IEnumerable collection))
646+
if (!(GetPropertyValue(element, propertyName, rootElement, lineInfo, out _, out var targetProperty) is IEnumerable collection))
637647
return false;
638648

639649
if (exception == null && TryAddToResourceDictionary(collection as ResourceDictionary, value, xKey, lineInfo, out exception))
@@ -646,8 +656,8 @@ static bool TryAddToProperty(object element, XmlName propertyName, object value,
646656
if (addMethod == null)
647657
return false;
648658

649-
if (serviceProvider != null)
650-
((XamlValueTargetProvider)serviceProvider.IProvideValueTarget).TargetProperty = targetProperty;
659+
if (serviceProvider?.GetService<IProvideValueTarget>() is XamlValueTargetProvider valueTargetProvider)
660+
valueTargetProvider.TargetProperty = targetProperty;
651661

652662
try {
653663
addMethod.Invoke(collection, new[] { value.ConvertTo(addMethod.GetParameters()[0].ParameterType, (Func<TypeConverter>)null, serviceProvider, out exception) });
@@ -698,7 +708,7 @@ void SetTemplate(ElementTemplate dt, INode node)
698708
};
699709
}
700710

701-
static bool TryAddValue(BindableObject bindable, BindableProperty property, object value, XamlServiceProvider serviceProvider, out Exception exception)
711+
static bool TryAddValue(BindableObject bindable, BindableProperty property, object value, IServiceProvider serviceProvider, out Exception exception)
702712
{
703713
exception = null;
704714

0 commit comments

Comments
 (0)