[Xaml[C]] Do not instantiate DataTemplate Content at parsing time (#683)
* [Xaml] rename VisitChildrenFirst * [Xaml] rework SkipChildren in XamlNode * [Xaml] fix 45179 * fix
This commit is contained in:
Родитель
ebb24b7edd
Коммит
f30aa8ae2c
|
@ -21,20 +21,11 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
ModuleDefinition Module { get; }
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
|
||||
public bool StopOnDataTemplate => true;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => false;
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -24,20 +24,10 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
ILContext Context { get; }
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
|
||||
public bool StopOnDataTemplate => false;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => true;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -13,20 +13,10 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
public ILContext Context { get; }
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
|
||||
public bool StopOnDataTemplate => true;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => false;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -16,20 +16,10 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
ILContext Context { get; }
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
|
||||
public bool StopOnDataTemplate => true;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => false;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -37,8 +37,9 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
public ILContext Context { get; }
|
||||
public bool StopOnResourceDictionary { get; }
|
||||
public bool VisitChildrenFirst { get; } = true;
|
||||
public bool StopOnDataTemplate { get; } = true;
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
|
||||
public bool StopOnDataTemplate => true;
|
||||
public bool VisitNodeOnDataTemplate => true;
|
||||
|
||||
ModuleDefinition Module { get; }
|
||||
|
||||
|
@ -88,6 +89,11 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
if ((propertyName != XmlName.Empty || TryGetPropertyName(node, parentNode, out propertyName)) && skips.Contains(propertyName))
|
||||
return;
|
||||
|
||||
if (propertyName == XmlName._CreateContent) {
|
||||
SetDataTemplate((IElementNode)parentNode, node, Context, node);
|
||||
return;
|
||||
}
|
||||
|
||||
//if this node is an IMarkupExtension, invoke ProvideValue() and replace the variable
|
||||
var vardef = Context.Variables[node];
|
||||
var vardefref = new VariableDefinitionReference(vardef);
|
||||
|
@ -114,11 +120,8 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
return;
|
||||
if (parentNode is IElementNode && ((IElementNode)parentNode).SkipProperties.Contains (propertyName))
|
||||
return;
|
||||
|
||||
if (propertyName == XmlName._CreateContent)
|
||||
SetDataTemplate((IElementNode)parentNode, node, Context, node);
|
||||
else
|
||||
Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node));
|
||||
|
||||
Context.IL.Append(SetPropertyValue(Context.Variables[(IElementNode)parentNode], propertyName, node, Context, node));
|
||||
}
|
||||
else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
@ -20,26 +19,15 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
|
||||
ModuleDefinition Module { get; }
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
|
||||
public bool StopOnDataTemplate => true;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => false;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
XmlName propertyName;
|
||||
if (!SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName))
|
||||
{
|
||||
if (!SetPropertiesVisitor.TryGetPropertyName(node, parentNode, out propertyName)) {
|
||||
if (!IsCollectionItem(node, parentNode))
|
||||
return;
|
||||
string contentProperty;
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||
using Xamarin.Forms;
|
||||
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
|
@ -22,6 +23,18 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
[TestFixture]
|
||||
class Tests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Device.PlatformServices = null;
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void DataTemplateInResourceDictionaries (bool useCompiledXaml)
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||
using Xamarin.Forms;
|
||||
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
|
@ -26,6 +27,18 @@ namespace Xamarin.Forms.Xaml.UnitTests
|
|||
[TestFixture]
|
||||
class Tests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Device.PlatformServices = null;
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void BaseClassIdentifiersAreValidForResources (bool useCompiledXaml)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:local="clr-namespace:Xamarin.Forms.Xaml.UnitTests"
|
||||
x:Class="Xamarin.Forms.Xaml.UnitTests.Bz45179">
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<DataTemplate x:Key="dt0">
|
||||
<local:Bz45179_0/>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
</ContentPage>
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
public class Bz45179_0 : ContentView {
|
||||
public static int creator_count;
|
||||
public Bz45179_0()
|
||||
{
|
||||
creator_count++;
|
||||
}
|
||||
|
||||
}
|
||||
public partial class Bz45179 : ContentPage
|
||||
{
|
||||
public Bz45179()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public Bz45179(bool useCompiledXaml)
|
||||
{
|
||||
//this stub will be replaced at compile time
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
class Tests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Device.PlatformServices = null;
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void DTDoNotInstantiateTheirContent(bool useCompiledXaml)
|
||||
{
|
||||
Bz45179_0.creator_count = 0;
|
||||
Assume.That(Bz45179_0.creator_count, Is.EqualTo(0));
|
||||
var page = new Bz45179(useCompiledXaml);
|
||||
Assert.That(Bz45179_0.creator_count, Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -406,6 +406,9 @@
|
|||
<Compile Include="Issues\Unreported008.xaml.cs">
|
||||
<DependentUpon>Unreported008.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Issues\Bz45179.xaml.cs">
|
||||
<DependentUpon>Bz45179.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
|
||||
|
@ -733,6 +736,9 @@
|
|||
<EmbeddedResource Include="Issues\Unreported008.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Issues\Bz45179.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
|
|
|
@ -35,15 +35,10 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
HydratationContext Context { get; }
|
||||
|
||||
public bool VisitChildrenFirst {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
|
||||
public bool StopOnDataTemplate => true;
|
||||
public bool StopOnResourceDictionary { get; }
|
||||
public bool VisitNodeOnDataTemplate => true;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
@ -81,6 +76,15 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
public void Visit(ElementNode node, INode parentNode)
|
||||
{
|
||||
var propertyName = XmlName.Empty;
|
||||
if (TryGetPropertyName(node, parentNode, out propertyName) && propertyName == XmlName._CreateContent){
|
||||
var s0 = Values[parentNode];
|
||||
if (s0 is ElementTemplate) {
|
||||
SetTemplate(s0 as ElementTemplate, node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var value = Values [node];
|
||||
var parentElement = parentNode as IElementNode;
|
||||
var markupExtension = value as IMarkupExtension;
|
||||
|
@ -96,7 +100,7 @@ namespace Xamarin.Forms.Xaml
|
|||
value = valueProvider.ProvideValue(serviceProvider);
|
||||
}
|
||||
|
||||
XmlName propertyName = XmlName.Empty;
|
||||
propertyName = XmlName.Empty;
|
||||
|
||||
//Simplify ListNodes with single elements
|
||||
var pList = parentNode as ListNode;
|
||||
|
@ -113,11 +117,7 @@ namespace Xamarin.Forms.Xaml
|
|||
return;
|
||||
|
||||
var source = Values [parentNode];
|
||||
|
||||
if (propertyName == XmlName._CreateContent && source is ElementTemplate)
|
||||
SetTemplate(source as ElementTemplate, node);
|
||||
else
|
||||
SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node);
|
||||
SetPropertyValue(source, propertyName, value, Context.RootElement, node, Context, node);
|
||||
} else if (IsCollectionItem(node, parentNode) && parentNode is IElementNode) {
|
||||
// Collection element, implicit content, or implicit collection element.
|
||||
string contentProperty;
|
||||
|
|
|
@ -23,20 +23,10 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
HydratationContext Context { get; }
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
|
||||
public bool StopOnDataTemplate => true;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => false;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -28,20 +28,10 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
HydratationContext Context { get; }
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.BottomUp;
|
||||
public bool StopOnDataTemplate => false;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => true;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -20,20 +20,10 @@ namespace Xamarin.Forms.Xaml
|
|||
get { return Context.Values; }
|
||||
}
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
|
||||
public bool StopOnDataTemplate => true;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => false;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace Xamarin.Forms.Xaml
|
|||
}
|
||||
if (resource == null && (Application.Current == null || Application.Current.Resources == null ||
|
||||
!Application.Current.Resources.TryGetMergedValue(Key, out resource)))
|
||||
throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo);
|
||||
throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo);
|
||||
|
||||
var bp = valueProvider.TargetProperty as BindableProperty;
|
||||
var pi = valueProvider.TargetProperty as PropertyInfo;
|
||||
|
|
|
@ -14,20 +14,10 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
Dictionary<INode, object> Values { get; set; }
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
|
||||
public bool StopOnDataTemplate => false;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => true;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -5,17 +5,10 @@ namespace Xamarin.Forms.Xaml
|
|||
{
|
||||
class PruneIgnoredNodesVisitor : IXamlNodeVisitor
|
||||
{
|
||||
public bool StopOnDataTemplate {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool VisitChildrenFirst {
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
|
||||
public bool StopOnDataTemplate => false;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => true;
|
||||
|
||||
public void Visit(ElementNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -12,20 +12,10 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
Dictionary<INode, object> Values { get; }
|
||||
|
||||
public bool VisitChildrenFirst
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool StopOnDataTemplate
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool StopOnResourceDictionary
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public TreeVisitingMode VisitingMode => TreeVisitingMode.TopDown;
|
||||
public bool StopOnDataTemplate => true;
|
||||
public bool StopOnResourceDictionary => false;
|
||||
public bool VisitNodeOnDataTemplate => false;
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
@ -7,36 +6,30 @@ using Xamarin.Forms.Internals;
|
|||
|
||||
namespace Xamarin.Forms.Xaml
|
||||
{
|
||||
internal interface INode
|
||||
interface INode
|
||||
{
|
||||
List<string> IgnorablePrefixes { get; set; }
|
||||
|
||||
IXmlNamespaceResolver NamespaceResolver { get; }
|
||||
|
||||
INode Parent { get; set; }
|
||||
|
||||
void Accept(IXamlNodeVisitor visitor, INode parentNode);
|
||||
INode Clone();
|
||||
}
|
||||
|
||||
internal interface IValueNode : INode
|
||||
interface IValueNode : INode
|
||||
{
|
||||
}
|
||||
|
||||
internal interface IElementNode : INode, IListNode
|
||||
interface IElementNode : INode, IListNode
|
||||
{
|
||||
Dictionary<XmlName, INode> Properties { get; }
|
||||
|
||||
List<XmlName> SkipProperties { get; }
|
||||
|
||||
INameScope Namescope { get; }
|
||||
|
||||
XmlType XmlType { get; }
|
||||
|
||||
string NamespaceURI { get; }
|
||||
}
|
||||
|
||||
internal interface IListNode : INode
|
||||
interface IListNode : INode
|
||||
{
|
||||
List<INode> CollectionItems { get; }
|
||||
}
|
||||
|
@ -56,7 +49,7 @@ namespace Xamarin.Forms.Xaml
|
|||
public IList<XmlType> TypeArguments { get; }
|
||||
}
|
||||
|
||||
internal abstract class BaseNode : IXmlLineInfo, INode
|
||||
abstract class BaseNode : IXmlLineInfo, INode
|
||||
{
|
||||
protected BaseNode(IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
|
||||
{
|
||||
|
@ -66,27 +59,19 @@ namespace Xamarin.Forms.Xaml
|
|||
}
|
||||
|
||||
public IXmlNamespaceResolver NamespaceResolver { get; }
|
||||
|
||||
public abstract void Accept(IXamlNodeVisitor visitor, INode parentNode);
|
||||
|
||||
public INode Parent { get; set; }
|
||||
|
||||
public List<string> IgnorablePrefixes { get; set; }
|
||||
|
||||
public bool HasLineInfo()
|
||||
{
|
||||
return LineNumber >= 0 && LinePosition >= 0;
|
||||
}
|
||||
|
||||
public int LineNumber { get; set; }
|
||||
|
||||
public int LinePosition { get; set; }
|
||||
|
||||
public bool HasLineInfo() => LineNumber >= 0 && LinePosition >= 0;
|
||||
|
||||
public abstract void Accept(IXamlNodeVisitor visitor, INode parentNode);
|
||||
public abstract INode Clone();
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{Value}")]
|
||||
internal class ValueNode : BaseNode, IValueNode
|
||||
class ValueNode : BaseNode, IValueNode
|
||||
{
|
||||
public ValueNode(object value, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
|
||||
: base(namespaceResolver, linenumber, lineposition)
|
||||
|
@ -101,19 +86,15 @@ namespace Xamarin.Forms.Xaml
|
|||
visitor.Visit(this, parentNode);
|
||||
}
|
||||
|
||||
public override INode Clone()
|
||||
{
|
||||
return new ValueNode(Value, NamespaceResolver, LineNumber, LinePosition) {
|
||||
IgnorablePrefixes = IgnorablePrefixes
|
||||
};
|
||||
}
|
||||
public override INode Clone() => new ValueNode(Value, NamespaceResolver, LineNumber, LinePosition) {
|
||||
IgnorablePrefixes = IgnorablePrefixes
|
||||
};
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{MarkupString}")]
|
||||
internal class MarkupNode : BaseNode, IValueNode
|
||||
class MarkupNode : BaseNode, IValueNode
|
||||
{
|
||||
public MarkupNode(string markupString, IXmlNamespaceResolver namespaceResolver, int linenumber = -1,
|
||||
int lineposition = -1)
|
||||
public MarkupNode(string markupString, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
|
||||
: base(namespaceResolver, linenumber, lineposition)
|
||||
{
|
||||
MarkupString = markupString;
|
||||
|
@ -126,15 +107,13 @@ namespace Xamarin.Forms.Xaml
|
|||
visitor.Visit(this, parentNode);
|
||||
}
|
||||
|
||||
public override INode Clone()
|
||||
{
|
||||
return new MarkupNode(MarkupString, NamespaceResolver, LineNumber, LinePosition) {
|
||||
IgnorablePrefixes = IgnorablePrefixes
|
||||
};
|
||||
}
|
||||
public override INode Clone() => new MarkupNode(MarkupString, NamespaceResolver, LineNumber, LinePosition) {
|
||||
IgnorablePrefixes = IgnorablePrefixes
|
||||
};
|
||||
}
|
||||
|
||||
internal class ElementNode : BaseNode, IValueNode, IElementNode
|
||||
[DebuggerDisplay("{XmlType.Name}")]
|
||||
class ElementNode : BaseNode, IValueNode, IElementNode
|
||||
{
|
||||
public ElementNode(XmlType type, string namespaceURI, IXmlNamespaceResolver namespaceResolver, int linenumber = -1,
|
||||
int lineposition = -1)
|
||||
|
@ -148,48 +127,48 @@ namespace Xamarin.Forms.Xaml
|
|||
}
|
||||
|
||||
public Dictionary<XmlName, INode> Properties { get; }
|
||||
|
||||
public List<XmlName> SkipProperties { get; }
|
||||
|
||||
public List<INode> CollectionItems { get; }
|
||||
|
||||
public XmlType XmlType { get; }
|
||||
|
||||
public string NamespaceURI { get; }
|
||||
|
||||
public INameScope Namescope { get; set; }
|
||||
|
||||
public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
|
||||
{
|
||||
if (!visitor.VisitChildrenFirst)
|
||||
if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown)
|
||||
visitor.Visit(this, parentNode);
|
||||
if ((!visitor.StopOnDataTemplate || !IsDataTemplate(this, parentNode)) &&
|
||||
(!visitor.StopOnResourceDictionary || !IsResourceDictionary(this, parentNode)))
|
||||
{
|
||||
|
||||
if (!SkipChildren(visitor, parentNode)) {
|
||||
foreach (var node in Properties.Values.ToList())
|
||||
node.Accept(visitor, this);
|
||||
foreach (var node in CollectionItems)
|
||||
node.Accept(visitor, this);
|
||||
}
|
||||
if (visitor.VisitChildrenFirst)
|
||||
|
||||
if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp)
|
||||
visitor.Visit(this, parentNode);
|
||||
|
||||
}
|
||||
|
||||
internal static bool IsDataTemplate(INode node, INode parentNode)
|
||||
bool IsDataTemplate(INode parentNode)
|
||||
{
|
||||
var parentElement = parentNode as IElementNode;
|
||||
INode createContent;
|
||||
if (parentElement != null && parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
|
||||
createContent == node)
|
||||
if (parentElement != null &&
|
||||
parentElement.Properties.TryGetValue(XmlName._CreateContent, out createContent) &&
|
||||
createContent == this)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsResourceDictionary(INode node, INode parentNode)
|
||||
{
|
||||
var enode = node as ElementNode;
|
||||
return enode.XmlType.Name == "ResourceDictionary";
|
||||
}
|
||||
bool IsResourceDictionary() => XmlType.Name == "ResourceDictionary";
|
||||
|
||||
protected bool SkipChildren(IXamlNodeVisitor visitor, INode parentNode) =>
|
||||
(visitor.StopOnDataTemplate && IsDataTemplate(parentNode)) ||
|
||||
(visitor.StopOnResourceDictionary && IsResourceDictionary());
|
||||
|
||||
protected bool SkipVisitNode(IXamlNodeVisitor visitor, INode parentNode) =>
|
||||
!visitor.VisitNodeOnDataTemplate && IsDataTemplate(parentNode);
|
||||
|
||||
public override INode Clone()
|
||||
{
|
||||
|
@ -206,7 +185,7 @@ namespace Xamarin.Forms.Xaml
|
|||
}
|
||||
}
|
||||
|
||||
internal abstract class RootNode : ElementNode
|
||||
abstract class RootNode : ElementNode
|
||||
{
|
||||
protected RootNode(XmlType xmlType, IXmlNamespaceResolver nsResolver) : base(xmlType, xmlType.NamespaceUri, nsResolver)
|
||||
{
|
||||
|
@ -214,40 +193,39 @@ namespace Xamarin.Forms.Xaml
|
|||
|
||||
public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
|
||||
{
|
||||
if (!visitor.VisitChildrenFirst)
|
||||
if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.TopDown)
|
||||
visitor.Visit(this, parentNode);
|
||||
if ((!visitor.StopOnDataTemplate || !IsDataTemplate(this, parentNode)) &&
|
||||
(!visitor.StopOnResourceDictionary || !IsResourceDictionary(this, parentNode)))
|
||||
{
|
||||
|
||||
if (!SkipChildren(visitor, parentNode)) {
|
||||
foreach (var node in Properties.Values.ToList())
|
||||
node.Accept(visitor, this);
|
||||
foreach (var node in CollectionItems)
|
||||
node.Accept(visitor, this);
|
||||
}
|
||||
if (visitor.VisitChildrenFirst)
|
||||
|
||||
if (!SkipVisitNode(visitor, parentNode) && visitor.VisitingMode == TreeVisitingMode.BottomUp)
|
||||
visitor.Visit(this, parentNode);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ListNode : BaseNode, IListNode, IValueNode
|
||||
class ListNode : BaseNode, IListNode, IValueNode
|
||||
{
|
||||
public ListNode(IList<INode> nodes, IXmlNamespaceResolver namespaceResolver, int linenumber = -1,
|
||||
int lineposition = -1) : base(namespaceResolver, linenumber, lineposition)
|
||||
public ListNode(IList<INode> nodes, IXmlNamespaceResolver namespaceResolver, int linenumber = -1, int lineposition = -1)
|
||||
: base(namespaceResolver, linenumber, lineposition)
|
||||
{
|
||||
CollectionItems = nodes.ToList();
|
||||
}
|
||||
|
||||
public XmlName XmlName { get; set; }
|
||||
|
||||
public List<INode> CollectionItems { get; set; }
|
||||
|
||||
public override void Accept(IXamlNodeVisitor visitor, INode parentNode)
|
||||
{
|
||||
if (!visitor.VisitChildrenFirst)
|
||||
if (visitor.VisitingMode == TreeVisitingMode.TopDown)
|
||||
visitor.Visit(this, parentNode);
|
||||
foreach (var node in CollectionItems)
|
||||
node.Accept(visitor, this);
|
||||
if (visitor.VisitChildrenFirst)
|
||||
if (visitor.VisitingMode == TreeVisitingMode.BottomUp)
|
||||
visitor.Visit(this, parentNode);
|
||||
}
|
||||
|
||||
|
@ -262,12 +240,11 @@ namespace Xamarin.Forms.Xaml
|
|||
}
|
||||
}
|
||||
|
||||
internal static class INodeExtensions
|
||||
static class INodeExtensions
|
||||
{
|
||||
public static bool SkipPrefix(this INode node, string prefix)
|
||||
{
|
||||
do
|
||||
{
|
||||
do {
|
||||
if (node.IgnorablePrefixes != null && node.IgnorablePrefixes.Contains(prefix))
|
||||
return true;
|
||||
node = node.Parent;
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
|
||||
namespace Xamarin.Forms.Xaml
|
||||
{
|
||||
internal interface IXamlNodeVisitor
|
||||
interface IXamlNodeVisitor
|
||||
{
|
||||
bool VisitChildrenFirst { get; }
|
||||
|
||||
TreeVisitingMode VisitingMode { get; }
|
||||
bool StopOnDataTemplate { get; }
|
||||
|
||||
bool VisitNodeOnDataTemplate { get; }
|
||||
bool StopOnResourceDictionary { get; }
|
||||
|
||||
void Visit(ValueNode node, INode parentNode);
|
||||
|
@ -17,22 +16,27 @@ namespace Xamarin.Forms.Xaml
|
|||
void Visit(ListNode node, INode parentNode);
|
||||
}
|
||||
|
||||
internal class XamlNodeVisitor : IXamlNodeVisitor
|
||||
enum TreeVisitingMode {
|
||||
TopDown,
|
||||
BottomUp
|
||||
}
|
||||
|
||||
class XamlNodeVisitor : IXamlNodeVisitor
|
||||
{
|
||||
readonly Action<INode, INode> action;
|
||||
|
||||
public XamlNodeVisitor(Action<INode, INode> action, bool visitChildrenFirst = false, bool stopOnDataTemplate = false)
|
||||
public XamlNodeVisitor(Action<INode, INode> action, TreeVisitingMode visitingMode = TreeVisitingMode.TopDown, bool stopOnDataTemplate = false, bool visitNodeOnDataTemplate = true)
|
||||
{
|
||||
this.action = action;
|
||||
VisitChildrenFirst = visitChildrenFirst;
|
||||
VisitingMode = visitingMode;
|
||||
StopOnDataTemplate = stopOnDataTemplate;
|
||||
VisitNodeOnDataTemplate = visitNodeOnDataTemplate;
|
||||
}
|
||||
|
||||
public bool VisitChildrenFirst { get; }
|
||||
|
||||
public TreeVisitingMode VisitingMode { get; }
|
||||
public bool StopOnDataTemplate { get; }
|
||||
|
||||
public bool StopOnResourceDictionary { get; private set; }
|
||||
public bool VisitNodeOnDataTemplate { get; }
|
||||
|
||||
public void Visit(ValueNode node, INode parentNode)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче