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

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

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

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

@ -25,7 +25,7 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -25,7 +25,7 @@ namespace ICSharpCode.WpfDesign.XamlDom
return typeof(IList).IsAssignableFrom(type)
|| type.IsArray
|| typeof(IAddChild).IsAssignableFrom(type)
|| typeof(ResourceDictionary).IsAssignableFrom(type);
|| typeof(IDictionary).IsAssignableFrom(type);
}
/// <summary>
@ -65,7 +65,7 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -65,7 +65,7 @@ namespace ICSharpCode.WpfDesign.XamlDom
} 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 == "") {
@ -74,7 +74,7 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -74,7 +74,7 @@ namespace ICSharpCode.WpfDesign.XamlDom
//}
if (key == null || (key as string) == "")
key = val;
((ResourceDictionary)collectionInstance).Add(key, val);
((IDictionary)collectionInstance).Add(key, val);
} else {
collectionType.InvokeMember(
"Add", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,

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

@ -285,22 +285,41 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -285,22 +285,41 @@ namespace ICSharpCode.WpfDesign.XamlDom
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;
}
}
@ -308,9 +327,9 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -308,9 +327,9 @@ namespace ICSharpCode.WpfDesign.XamlDom
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");
@ -386,6 +405,16 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -386,6 +405,16 @@ namespace ICSharpCode.WpfDesign.XamlDom
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)) {
@ -533,7 +562,12 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -533,7 +562,12 @@ namespace ICSharpCode.WpfDesign.XamlDom
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
@ -542,23 +576,29 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -542,23 +576,29 @@ namespace ICSharpCode.WpfDesign.XamlDom
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;
@ -570,7 +610,10 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -570,7 +610,10 @@ namespace ICSharpCode.WpfDesign.XamlDom
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);
}

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

@ -276,21 +276,60 @@ namespace ICSharpCode.WpfDesign.XamlDom @@ -276,21 +276,60 @@ namespace ICSharpCode.WpfDesign.XamlDom
obj.Properties.Where((prop) => prop.IsResources).FirstOrDefault() != null;
}
XmlElement CreatePropertyElement()
{
string ns = parentObject.OwnerDocument.GetNamespaceFor(parentObject.ElementType);
return parentObject.OwnerDocument.XmlDocument.CreateElement(
parentObject.OwnerDocument.GetPrefixForNamespace(ns),
parentObject.ElementType.Name + "." + this.PropertyName,
ns
);
}
XmlElement CreatePropertyElement()
{
Type propertyElementType = GetPropertyElementType();
string ns = parentObject.OwnerDocument.GetNamespaceFor(propertyElementType);
return parentObject.OwnerDocument.XmlDocument.CreateElement(
parentObject.OwnerDocument.GetPrefixForNamespace(ns),
propertyElementType.Name + "." + this.PropertyName,
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)
{
if (this.IsCollection) {
// this is the default collection
InsertNodeInCollection(newChildNode, collectionElements.Count);
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
InsertNodeInCollection(newChildNode, collectionElements.Count);
}
return;
}
if (_propertyElement == null) {

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

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

Loading…
Cancel
Save