Skip to content
This repository was archived by the owner on Oct 16, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@

namespace ICSharpCode.WpfDesign.Tests.XamlDom
{
public class ExampleClassList : List<ExampleClass>
{
}

public class ExampleClassDictionary : Dictionary<string, ExampleClass>
{
}

[ContentProperty("List")]
public class ExampleClassContainer : ExampleClass
{
Expand All @@ -18,5 +26,31 @@ public List<ExampleClass> List {
return list;
}
}

ExampleClassList otherList = new ExampleClassList();

public ExampleClassList OtherList {
get {
TestHelperLog.Log("OtherList.get " + Identity);
return otherList;
}
set {
TestHelperLog.Log("OtherList.set " + Identity);
otherList = value;
}
}

ExampleClassDictionary dictionary = new ExampleClassDictionary();

public ExampleClassDictionary Dictionary {
get {
TestHelperLog.Log("Dictionary.get " + Identity);
return dictionary;
}
set {
TestHelperLog.Log("Dictionary.set " + Identity);
dictionary = value;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ public static void SetExample(DependencyObject element, string value)
TestHelperLog.Log("ExampleService.SetExample");
element.SetValue(ExampleProperty, value);
}

public static readonly DependencyProperty ExampleCollectionProperty = DependencyProperty.RegisterAttached(
"ExampleCollection", typeof(ExampleClassList), typeof(ExampleService)
);

public static ExampleClassList GetExampleCollection(DependencyObject element)
{
TestHelperLog.Log("ExampleService.GetExampleCollection");
return (ExampleClassList)element.GetValue(ExampleCollectionProperty);
}

public static void SetExampleCollection(DependencyObject element, ExampleClassList value)
{
TestHelperLog.Log("ExampleService.SetExampleCollection");
element.SetValue(ExampleCollectionProperty, value);
}
}

public class ExampleDependencyObject : DependencyObject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,106 @@ public void ContainerImplicitList()
");
}

[Test]
public void ContainerExplicitList()
{
TestLoading(@"
<ExampleClassContainer
xmlns=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<ExampleClassContainer.OtherList>
<ExampleClassList>
<ExampleClass OtherProp=""a""> </ExampleClass>
<ExampleClass OtherProp=""b"" />
<ExampleClass OtherProp=""c"" />
</ExampleClassList>
</ExampleClassContainer.OtherList>
</ExampleClassContainer>
");
}

[Test]
public void ContainerImplicitDictionary()
{
TestLoading(@"
<ExampleClassContainer
xmlns=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<ExampleClassContainer.Dictionary>
<ExampleClass x:Key=""key1"" OtherProp=""a""> </ExampleClass>
<ExampleClass x:Key=""key2"" OtherProp=""b"" />
<ExampleClass x:Key=""key3"" OtherProp=""c"" />
</ExampleClassContainer.Dictionary>
</ExampleClassContainer>
");
}

[Test]
public void ContainerExplicitDictionary()
{
TestLoading(@"
<ExampleClassContainer
xmlns=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<ExampleClassContainer.Dictionary>
<ExampleClassDictionary>
<ExampleClass x:Key=""key1"" OtherProp=""a""> </ExampleClass>
<ExampleClass x:Key=""key2"" OtherProp=""b"" />
<ExampleClass x:Key=""key3"" OtherProp=""c"" />
</ExampleClassDictionary>
</ExampleClassContainer.Dictionary>
</ExampleClassContainer>
");
}

[Test]
public void ResourceDictionaryImplicit()
{
TestLoading(@"
<Window
xmlns=""http://schemas.microsoft.com/netfx/2007/xaml/presentation""
xmlns:t=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<Window.Resources>
<t:ExampleClass x:Key=""key1"" OtherProp=""a""> </t:ExampleClass>
<t:ExampleClass x:Key=""key2"" OtherProp=""b"" />
</Window.Resources>
</Window>
");
}

[Test]
public void ResourceDictionaryExplicitWinfx2006()
{
ResourceDictionaryExplicitInternal("http://schemas.microsoft.com/winfx/2006/xaml/presentation");
}

[Test]
[Ignore("Own XamlParser should handle different namespaces pointing to same types, because builtin XamlReader does.")]
public void ResourceDictionaryExplicitNetfx2007()
{
// The reason this test case fails is because own XamlParser cannot always handle the case where multiple xmlns points to the same type.
// In this test case the default xmlns is set to netfx/20007 (compare with the test above that uses winfx/2006 and is successfully executed).
ResourceDictionaryExplicitInternal("http://schemas.microsoft.com/netfx/2007/xaml/presentation");
}

void ResourceDictionaryExplicitInternal(string defaultXmlns)
{
TestLoading(@"
<Window
xmlns=""" + defaultXmlns + @"""
xmlns:t=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<Window.Resources>
<ResourceDictionary>
<t:ExampleClass x:Key=""key1"" OtherProp=""a""> </t:ExampleClass>
<t:ExampleClass x:Key=""key2"" OtherProp=""b"" />
</ResourceDictionary>
</Window.Resources>
</Window>
");
}

[Test]
public void ExampleServiceTest()
{
Expand All @@ -203,6 +303,25 @@ public void ExampleServiceTest()
");
}

[Test]
public void ExampleServiceCollectionTest()
{
TestLoading(@"
<t:ExampleDependencyObject
xmlns=""http://schemas.microsoft.com/netfx/2007/xaml/presentation""
xmlns:t=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<t:ExampleService.ExampleCollection>
<t:ExampleClassList>
<t:ExampleClass OtherProp=""a""> </t:ExampleClass>
<t:ExampleClass OtherProp=""b"" />
<t:ExampleClass OtherProp=""c"" />
</t:ExampleClassList>
</t:ExampleService.ExampleCollection>
</t:ExampleDependencyObject>
");
}

[Test]
public void ExampleClassObjectPropWithStringValue()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static bool IsCollectionType(Type type)
return typeof(IList).IsAssignableFrom(type)
|| type.IsArray
|| typeof(IAddChild).IsAssignableFrom(type)
|| typeof(ResourceDictionary).IsAssignableFrom(type);
|| typeof(IDictionary).IsAssignableFrom(type);
}

/// <summary>
Expand Down Expand Up @@ -65,7 +65,7 @@ public static void AddToCollection(Type collectionType, object collectionInstanc
} else {
addChild.AddChild(newElement.GetValueFor(null));
}
} else if (collectionInstance is ResourceDictionary) {
} else if (collectionInstance is IDictionary) {
object val = newElement.GetValueFor(null);
object key = newElement is XamlObject ? ((XamlObject)newElement).GetXamlAttribute("Key") : null;
//if (key == null || key == "") {
Expand All @@ -74,7 +74,7 @@ public static void AddToCollection(Type collectionType, object collectionInstanc
//}
if (key == null || key == "")
key = val;
((ResourceDictionary)collectionInstance).Add(key, val);
((IDictionary)collectionInstance).Add(key, val);
} else {
collectionType.InvokeMember(
"Add", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,32 +285,51 @@ XamlObject ParseObject(XmlElement element)
void ParseObjectContent(XamlObject obj, XmlElement element, XamlPropertyInfo defaultProperty, XamlTextValue initializeFromTextValueInsteadOfConstructor)
{
bool isDefaultValueSet = false;
object defaultPropertyValue = null;
XamlProperty defaultCollectionProperty = null;

if (defaultProperty != null && defaultProperty.IsCollection && !element.IsEmpty) {
defaultPropertyValue = defaultProperty.GetValue(obj.Instance);
obj.AddProperty(defaultCollectionProperty = new XamlProperty(obj, defaultProperty));
XamlProperty collectionProperty = null;
object collectionInstance = null;
Type collectionType = null;
XmlElement collectionPropertyElement = null;
var elementChildNodes = GetNormalizedChildNodes(element);

if (defaultProperty == null && obj.Instance != null && CollectionSupport.IsCollectionType(obj.Instance.GetType())) {
XamlObject parentObj = obj.ParentObject;
var parentElement = element.ParentNode;
XamlPropertyInfo propertyInfo = GetPropertyInfo(settings.TypeFinder, parentObj.Instance, parentObj.ElementType, parentElement.NamespaceURI, parentElement.LocalName);
collectionProperty = FindExistingXamlProperty(parentObj, propertyInfo);
collectionInstance = obj.Instance;
collectionType = obj.ElementType;
collectionPropertyElement = element;
} else if (defaultProperty != null && defaultProperty.IsCollection && !element.IsEmpty) {
foreach (XmlNode childNode in elementChildNodes) {
XmlElement childElement = childNode as XmlElement;
if (childElement == null || !ObjectChildElementIsPropertyElement(childElement)) {
obj.AddProperty(collectionProperty = new XamlProperty(obj, defaultProperty));
collectionType = defaultProperty.ReturnType;
collectionInstance = defaultProperty.GetValue(obj.Instance);
break;
}
}
}

foreach (XmlNode childNode in GetNormalizedChildNodes(element)) {
foreach (XmlNode childNode in elementChildNodes) {
XmlElement childElement = childNode as XmlElement;
if (childElement != null) {
if (childElement.NamespaceURI == XamlConstants.XamlNamespace)
continue;

if (ObjectChildElementIsPropertyElement(childElement)) {
ParseObjectChildElementAsPropertyElement(obj, childElement, defaultProperty, defaultPropertyValue);
ParseObjectChildElementAsPropertyElement(obj, childElement, defaultProperty);
continue;
}
}
if (initializeFromTextValueInsteadOfConstructor != null)
continue;
XamlPropertyValue childValue = ParseValue(childNode);
if (childValue != null) {
if (defaultProperty != null && defaultProperty.IsCollection) {
defaultCollectionProperty.ParserAddCollectionElement(null, childValue);
CollectionSupport.AddToCollection(defaultProperty.ReturnType, defaultPropertyValue, childValue);
if (collectionProperty != null) {
collectionProperty.ParserAddCollectionElement(collectionPropertyElement, childValue);
CollectionSupport.AddToCollection(collectionType, collectionInstance, childValue);
} else {
if (defaultProperty == null)
throw new XamlLoadException("This element does not have a default value, cannot assign to it");
Expand Down Expand Up @@ -386,6 +405,16 @@ XamlPropertyValue ParseValueCore(XmlNode childNode)
return null;
}

static XamlProperty FindExistingXamlProperty(XamlObject obj, XamlPropertyInfo propertyInfo)
{
foreach (XamlProperty existing in obj.Properties) {
if (existing.propertyInfo.FullyQualifiedName == propertyInfo.FullyQualifiedName)
return existing;
}

throw new XamlLoadException("Existing XamlProperty " + propertyInfo.FullyQualifiedName + " not found.");
}

static XamlPropertyInfo GetDefaultProperty(Type elementType)
{
foreach (ContentPropertyAttribute cpa in elementType.GetCustomAttributes(typeof(ContentPropertyAttribute), true)) {
Expand Down Expand Up @@ -531,7 +560,12 @@ static bool ObjectChildElementIsPropertyElement(XmlElement element)
return element.LocalName.Contains(".");
}

void ParseObjectChildElementAsPropertyElement(XamlObject obj, XmlElement element, XamlPropertyInfo defaultProperty, object defaultPropertyValue)
static bool IsElementChildACollectionForProperty(XamlTypeFinder typeFinder, XmlElement element, XamlPropertyInfo propertyInfo)
{
return element.ChildNodes.Count == 1 && propertyInfo.ReturnType.IsAssignableFrom(FindType(typeFinder, element.FirstChild.NamespaceURI, element.FirstChild.LocalName));
}

void ParseObjectChildElementAsPropertyElement(XamlObject obj, XmlElement element, XamlPropertyInfo defaultProperty)
{
Debug.Assert(element.LocalName.Contains("."));
// this is a element property syntax
Expand All @@ -540,23 +574,29 @@ void ParseObjectChildElementAsPropertyElement(XamlObject obj, XmlElement element
bool valueWasSet = false;

object collectionInstance = null;
bool isElementChildACollectionForProperty = false;
XamlProperty collectionProperty = null;
if (propertyInfo.IsCollection) {
if (defaultProperty != null && defaultProperty.FullyQualifiedName == propertyInfo.FullyQualifiedName) {
collectionInstance = defaultPropertyValue;
foreach (XamlProperty existing in obj.Properties) {
if (existing.propertyInfo == defaultProperty) {
collectionProperty = existing;
break;
}
}
} else {
collectionInstance = propertyInfo.GetValue(obj.Instance);
}

if (collectionProperty == null) {
obj.AddProperty(collectionProperty = new XamlProperty(obj, propertyInfo));
}
collectionProperty.ParserSetPropertyElement(element);

isElementChildACollectionForProperty = IsElementChildACollectionForProperty(settings.TypeFinder, element, propertyInfo);
if (isElementChildACollectionForProperty)
collectionProperty.ParserSetPropertyElement((XmlElement)element.FirstChild);
else {
collectionInstance = collectionProperty.propertyInfo.GetValue(obj.Instance);
collectionProperty.ParserSetPropertyElement(element);
}
}

XmlSpace oldXmlSpace = currentXmlSpace;
Expand All @@ -568,7 +608,10 @@ void ParseObjectChildElementAsPropertyElement(XamlObject obj, XmlElement element
XamlPropertyValue childValue = ParseValue(childNode);
if (childValue != null) {
if (propertyInfo.IsCollection) {
if (collectionInstance!=null) {
if (isElementChildACollectionForProperty) {
collectionProperty.PropertyValue = childValue;
}
else {
CollectionSupport.AddToCollection(propertyInfo.ReturnType, collectionInstance, childValue);
collectionProperty.ParserAddCollectionElement(element, childValue);
}
Expand Down
Loading