Browse Source

Merge pull request #77 from gumme/WpfDesignerParserFixes

Wpf designer parser fixes
pull/315/head
Siegfried Pammer 12 years ago
parent
commit
aa1e6a4edc
  1. 34
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Tests/XamlDom/ExampleClassContainer.cs
  2. 16
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Tests/XamlDom/ExampleService.cs
  3. 119
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Tests/XamlDom/SimpleLoadTests.cs
  4. 6
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/CollectionSupport.cs
  5. 73
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs
  6. 43
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs
  7. 6
      src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlPropertyInfo.cs

34
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Tests/XamlDom/ExampleClassContainer.cs

@ -7,6 +7,14 @@ using System.Windows.Markup;
namespace ICSharpCode.WpfDesign.Tests.XamlDom namespace ICSharpCode.WpfDesign.Tests.XamlDom
{ {
public class ExampleClassList : List<ExampleClass>
{
}
public class ExampleClassDictionary : Dictionary<string, ExampleClass>
{
}
[ContentProperty("List")] [ContentProperty("List")]
public class ExampleClassContainer : ExampleClass public class ExampleClassContainer : ExampleClass
{ {
@ -18,5 +26,31 @@ namespace ICSharpCode.WpfDesign.Tests.XamlDom
return 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;
}
}
} }
} }

16
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Tests/XamlDom/ExampleService.cs

@ -26,6 +26,22 @@ namespace ICSharpCode.WpfDesign.Tests.XamlDom
TestHelperLog.Log("ExampleService.SetExample"); TestHelperLog.Log("ExampleService.SetExample");
element.SetValue(ExampleProperty, value); 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 public class ExampleDependencyObject : DependencyObject

119
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Tests/XamlDom/SimpleLoadTests.cs

@ -190,6 +190,106 @@ namespace ICSharpCode.WpfDesign.Tests.XamlDom
"); ");
} }
[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] [Test]
public void ExampleServiceTest() public void ExampleServiceTest()
{ {
@ -203,6 +303,25 @@ namespace ICSharpCode.WpfDesign.Tests.XamlDom
"); ");
} }
[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] [Test]
public void ExampleClassObjectPropWithStringValue() public void ExampleClassObjectPropWithStringValue()
{ {

6
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/CollectionSupport.cs

@ -25,7 +25,7 @@ namespace ICSharpCode.WpfDesign.XamlDom
return typeof(IList).IsAssignableFrom(type) return typeof(IList).IsAssignableFrom(type)
|| type.IsArray || type.IsArray
|| typeof(IAddChild).IsAssignableFrom(type) || typeof(IAddChild).IsAssignableFrom(type)
|| typeof(ResourceDictionary).IsAssignableFrom(type); || typeof(IDictionary).IsAssignableFrom(type);
} }
/// <summary> /// <summary>
@ -65,7 +65,7 @@ namespace ICSharpCode.WpfDesign.XamlDom
} else { } else {
addChild.AddChild(newElement.GetValueFor(null)); addChild.AddChild(newElement.GetValueFor(null));
} }
} else if (collectionInstance is ResourceDictionary) { } else if (collectionInstance is IDictionary) {
object val = newElement.GetValueFor(null); object val = newElement.GetValueFor(null);
object key = newElement is XamlObject ? ((XamlObject)newElement).GetXamlAttribute("Key") : null; object key = newElement is XamlObject ? ((XamlObject)newElement).GetXamlAttribute("Key") : null;
//if (key == null || key == "") { //if (key == null || key == "") {
@ -74,7 +74,7 @@ namespace ICSharpCode.WpfDesign.XamlDom
//} //}
if (key == null || (key as string) == "") if (key == null || (key as string) == "")
key = val; key = val;
((ResourceDictionary)collectionInstance).Add(key, val); ((IDictionary)collectionInstance).Add(key, val);
} else { } else {
collectionType.InvokeMember( collectionType.InvokeMember(
"Add", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance, "Add", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,

73
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs

@ -285,22 +285,41 @@ namespace ICSharpCode.WpfDesign.XamlDom
void ParseObjectContent(XamlObject obj, XmlElement element, XamlPropertyInfo defaultProperty, XamlTextValue initializeFromTextValueInsteadOfConstructor) void ParseObjectContent(XamlObject obj, XmlElement element, XamlPropertyInfo defaultProperty, XamlTextValue initializeFromTextValueInsteadOfConstructor)
{ {
bool isDefaultValueSet = false; bool isDefaultValueSet = false;
object defaultPropertyValue = null;
XamlProperty defaultCollectionProperty = null;
if (defaultProperty != null && defaultProperty.IsCollection && !element.IsEmpty) { XamlProperty collectionProperty = null;
defaultPropertyValue = defaultProperty.GetValue(obj.Instance); object collectionInstance = null;
obj.AddProperty(defaultCollectionProperty = new XamlProperty(obj, defaultProperty)); 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; XmlElement childElement = childNode as XmlElement;
if (childElement != null) { if (childElement != null) {
if (childElement.NamespaceURI == XamlConstants.XamlNamespace) if (childElement.NamespaceURI == XamlConstants.XamlNamespace)
continue; continue;
if (ObjectChildElementIsPropertyElement(childElement)) { if (ObjectChildElementIsPropertyElement(childElement)) {
ParseObjectChildElementAsPropertyElement(obj, childElement, defaultProperty, defaultPropertyValue); ParseObjectChildElementAsPropertyElement(obj, childElement, defaultProperty);
continue; continue;
} }
} }
@ -308,9 +327,9 @@ namespace ICSharpCode.WpfDesign.XamlDom
continue; continue;
XamlPropertyValue childValue = ParseValue(childNode); XamlPropertyValue childValue = ParseValue(childNode);
if (childValue != null) { if (childValue != null) {
if (defaultProperty != null && defaultProperty.IsCollection) { if (collectionProperty != null) {
defaultCollectionProperty.ParserAddCollectionElement(null, childValue); collectionProperty.ParserAddCollectionElement(collectionPropertyElement, childValue);
CollectionSupport.AddToCollection(defaultProperty.ReturnType, defaultPropertyValue, childValue); CollectionSupport.AddToCollection(collectionType, collectionInstance, childValue);
} else { } else {
if (defaultProperty == null) if (defaultProperty == null)
throw new XamlLoadException("This element does not have a default value, cannot assign to it"); throw new XamlLoadException("This element does not have a default value, cannot assign to it");
@ -386,6 +405,16 @@ namespace ICSharpCode.WpfDesign.XamlDom
return null; 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) static XamlPropertyInfo GetDefaultProperty(Type elementType)
{ {
foreach (ContentPropertyAttribute cpa in elementType.GetCustomAttributes(typeof(ContentPropertyAttribute), true)) { foreach (ContentPropertyAttribute cpa in elementType.GetCustomAttributes(typeof(ContentPropertyAttribute), true)) {
@ -533,7 +562,12 @@ namespace ICSharpCode.WpfDesign.XamlDom
return element.LocalName.Contains("."); 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(".")); Debug.Assert(element.LocalName.Contains("."));
// this is a element property syntax // this is a element property syntax
@ -542,24 +576,30 @@ namespace ICSharpCode.WpfDesign.XamlDom
bool valueWasSet = false; bool valueWasSet = false;
object collectionInstance = null; object collectionInstance = null;
bool isElementChildACollectionForProperty = false;
XamlProperty collectionProperty = null; XamlProperty collectionProperty = null;
if (propertyInfo.IsCollection) { if (propertyInfo.IsCollection) {
if (defaultProperty != null && defaultProperty.FullyQualifiedName == propertyInfo.FullyQualifiedName) { if (defaultProperty != null && defaultProperty.FullyQualifiedName == propertyInfo.FullyQualifiedName) {
collectionInstance = defaultPropertyValue;
foreach (XamlProperty existing in obj.Properties) { foreach (XamlProperty existing in obj.Properties) {
if (existing.propertyInfo == defaultProperty) { if (existing.propertyInfo == defaultProperty) {
collectionProperty = existing; collectionProperty = existing;
break; break;
} }
} }
} else {
collectionInstance = propertyInfo.GetValue(obj.Instance);
} }
if (collectionProperty == null) { if (collectionProperty == null) {
obj.AddProperty(collectionProperty = new XamlProperty(obj, propertyInfo)); obj.AddProperty(collectionProperty = new XamlProperty(obj, propertyInfo));
} }
isElementChildACollectionForProperty = IsElementChildACollectionForProperty(settings.TypeFinder, element, propertyInfo);
if (isElementChildACollectionForProperty)
collectionProperty.ParserSetPropertyElement((XmlElement)element.FirstChild);
else {
collectionInstance = collectionProperty.propertyInfo.GetValue(obj.Instance);
collectionProperty.ParserSetPropertyElement(element); collectionProperty.ParserSetPropertyElement(element);
} }
}
XmlSpace oldXmlSpace = currentXmlSpace; XmlSpace oldXmlSpace = currentXmlSpace;
if (element.HasAttribute("xml:space")) { if (element.HasAttribute("xml:space")) {
@ -570,7 +610,10 @@ namespace ICSharpCode.WpfDesign.XamlDom
XamlPropertyValue childValue = ParseValue(childNode); XamlPropertyValue childValue = ParseValue(childNode);
if (childValue != null) { if (childValue != null) {
if (propertyInfo.IsCollection) { if (propertyInfo.IsCollection) {
if (collectionInstance!=null) { if (isElementChildACollectionForProperty) {
collectionProperty.PropertyValue = childValue;
}
else {
CollectionSupport.AddToCollection(propertyInfo.ReturnType, collectionInstance, childValue); CollectionSupport.AddToCollection(propertyInfo.ReturnType, collectionInstance, childValue);
collectionProperty.ParserAddCollectionElement(element, childValue); collectionProperty.ParserAddCollectionElement(element, childValue);
} }

43
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs

@ -278,19 +278,58 @@ namespace ICSharpCode.WpfDesign.XamlDom
XmlElement CreatePropertyElement() XmlElement CreatePropertyElement()
{ {
string ns = parentObject.OwnerDocument.GetNamespaceFor(parentObject.ElementType); Type propertyElementType = GetPropertyElementType();
string ns = parentObject.OwnerDocument.GetNamespaceFor(propertyElementType);
return parentObject.OwnerDocument.XmlDocument.CreateElement( return parentObject.OwnerDocument.XmlDocument.CreateElement(
parentObject.OwnerDocument.GetPrefixForNamespace(ns), parentObject.OwnerDocument.GetPrefixForNamespace(ns),
parentObject.ElementType.Name + "." + this.PropertyName, propertyElementType.Name + "." + this.PropertyName,
ns ns
); );
} }
Type GetPropertyElementType()
{
return this.IsAttached ? this.PropertyTargetType : parentObject.ElementType;
}
static XmlNode FindChildNode(XmlNode node, string localName, string namespaceURI)
{
foreach (XmlNode childNode in node.ChildNodes) {
if (childNode.LocalName == localName && childNode.NamespaceURI == namespaceURI)
return childNode;
}
return null;
}
bool IsNodeCollectionForThisProperty(XmlNode node)
{
return _propertyElement == null && this.PropertyName != this.ParentObject.ContentPropertyName && this.ReturnType.IsAssignableFrom(this.ParentObject.OwnerDocument.TypeFinder.GetType(node.NamespaceURI, node.LocalName));
}
internal void AddChildNodeToProperty(XmlNode newChildNode) internal void AddChildNodeToProperty(XmlNode newChildNode)
{ {
if (this.IsCollection) { if (this.IsCollection) {
if (IsNodeCollectionForThisProperty(newChildNode)) {
Type propertyElementType = GetPropertyElementType();
XmlNode parentNode = FindChildNode(parentObject.XmlElement, propertyElementType.Name + "." + this.PropertyName, parentObject.OwnerDocument.GetNamespaceFor(propertyElementType));
if (parentNode == null) {
parentNode = CreatePropertyElement();
parentObject.XmlElement.AppendChild(parentNode);
}
else if (parentNode.ChildNodes.Count > 0)
throw new XamlLoadException("Collection property node must have no children when adding collection element.");
parentNode.AppendChild(newChildNode);
_propertyElement = (XmlElement)newChildNode;
}
else {
// this is the default collection // this is the default collection
InsertNodeInCollection(newChildNode, collectionElements.Count); InsertNodeInCollection(newChildNode, collectionElements.Count);
}
return; return;
} }
if (_propertyElement == null) { if (_propertyElement == null) {

6
src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlPropertyInfo.cs

@ -39,6 +39,7 @@ namespace ICSharpCode.WpfDesign.XamlDom
{ {
readonly DependencyProperty property; readonly DependencyProperty property;
readonly bool isAttached; readonly bool isAttached;
readonly bool isCollection;
public override DependencyProperty DependencyProperty { public override DependencyProperty DependencyProperty {
get { return property; } get { return property; }
@ -49,6 +50,7 @@ namespace ICSharpCode.WpfDesign.XamlDom
Debug.Assert(property != null); Debug.Assert(property != null);
this.property = property; this.property = property;
this.isAttached = isAttached; this.isAttached = isAttached;
this.isCollection = CollectionSupport.IsCollectionType(property.PropertyType);
} }
public override TypeConverter TypeConverter { public override TypeConverter TypeConverter {
@ -84,7 +86,9 @@ namespace ICSharpCode.WpfDesign.XamlDom
} }
public override bool IsCollection { public override bool IsCollection {
get { return false; } get {
return isCollection;
}
} }
public override object GetValue(object instance) public override object GetValue(object instance)

Loading…
Cancel
Save