Skip to content
This repository was archived by the owner on Oct 16, 2020. It is now read-only.

Commit aa1e6a4

Browse files
Merge pull request #77 from gumme/WpfDesignerParserFixes
Wpf designer parser fixes
2 parents b5b1573 + 91ef4ab commit aa1e6a4

7 files changed

Lines changed: 286 additions & 31 deletions

File tree

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77

88
namespace ICSharpCode.WpfDesign.Tests.XamlDom
99
{
10+
public class ExampleClassList : List<ExampleClass>
11+
{
12+
}
13+
14+
public class ExampleClassDictionary : Dictionary<string, ExampleClass>
15+
{
16+
}
17+
1018
[ContentProperty("List")]
1119
public class ExampleClassContainer : ExampleClass
1220
{
@@ -18,5 +26,31 @@ public List<ExampleClass> List {
1826
return list;
1927
}
2028
}
29+
30+
ExampleClassList otherList = new ExampleClassList();
31+
32+
public ExampleClassList OtherList {
33+
get {
34+
TestHelperLog.Log("OtherList.get " + Identity);
35+
return otherList;
36+
}
37+
set {
38+
TestHelperLog.Log("OtherList.set " + Identity);
39+
otherList = value;
40+
}
41+
}
42+
43+
ExampleClassDictionary dictionary = new ExampleClassDictionary();
44+
45+
public ExampleClassDictionary Dictionary {
46+
get {
47+
TestHelperLog.Log("Dictionary.get " + Identity);
48+
return dictionary;
49+
}
50+
set {
51+
TestHelperLog.Log("Dictionary.set " + Identity);
52+
dictionary = value;
53+
}
54+
}
2155
}
2256
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ public static void SetExample(DependencyObject element, string value)
2626
TestHelperLog.Log("ExampleService.SetExample");
2727
element.SetValue(ExampleProperty, value);
2828
}
29+
30+
public static readonly DependencyProperty ExampleCollectionProperty = DependencyProperty.RegisterAttached(
31+
"ExampleCollection", typeof(ExampleClassList), typeof(ExampleService)
32+
);
33+
34+
public static ExampleClassList GetExampleCollection(DependencyObject element)
35+
{
36+
TestHelperLog.Log("ExampleService.GetExampleCollection");
37+
return (ExampleClassList)element.GetValue(ExampleCollectionProperty);
38+
}
39+
40+
public static void SetExampleCollection(DependencyObject element, ExampleClassList value)
41+
{
42+
TestHelperLog.Log("ExampleService.SetExampleCollection");
43+
element.SetValue(ExampleCollectionProperty, value);
44+
}
2945
}
3046

3147
public class ExampleDependencyObject : DependencyObject

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

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,106 @@ public void ContainerImplicitList()
190190
");
191191
}
192192

193+
[Test]
194+
public void ContainerExplicitList()
195+
{
196+
TestLoading(@"
197+
<ExampleClassContainer
198+
xmlns=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
199+
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
200+
<ExampleClassContainer.OtherList>
201+
<ExampleClassList>
202+
<ExampleClass OtherProp=""a""> </ExampleClass>
203+
<ExampleClass OtherProp=""b"" />
204+
<ExampleClass OtherProp=""c"" />
205+
</ExampleClassList>
206+
</ExampleClassContainer.OtherList>
207+
</ExampleClassContainer>
208+
");
209+
}
210+
211+
[Test]
212+
public void ContainerImplicitDictionary()
213+
{
214+
TestLoading(@"
215+
<ExampleClassContainer
216+
xmlns=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
217+
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
218+
<ExampleClassContainer.Dictionary>
219+
<ExampleClass x:Key=""key1"" OtherProp=""a""> </ExampleClass>
220+
<ExampleClass x:Key=""key2"" OtherProp=""b"" />
221+
<ExampleClass x:Key=""key3"" OtherProp=""c"" />
222+
</ExampleClassContainer.Dictionary>
223+
</ExampleClassContainer>
224+
");
225+
}
226+
227+
[Test]
228+
public void ContainerExplicitDictionary()
229+
{
230+
TestLoading(@"
231+
<ExampleClassContainer
232+
xmlns=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
233+
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
234+
<ExampleClassContainer.Dictionary>
235+
<ExampleClassDictionary>
236+
<ExampleClass x:Key=""key1"" OtherProp=""a""> </ExampleClass>
237+
<ExampleClass x:Key=""key2"" OtherProp=""b"" />
238+
<ExampleClass x:Key=""key3"" OtherProp=""c"" />
239+
</ExampleClassDictionary>
240+
</ExampleClassContainer.Dictionary>
241+
</ExampleClassContainer>
242+
");
243+
}
244+
245+
[Test]
246+
public void ResourceDictionaryImplicit()
247+
{
248+
TestLoading(@"
249+
<Window
250+
xmlns=""http://schemas.microsoft.com/netfx/2007/xaml/presentation""
251+
xmlns:t=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
252+
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
253+
<Window.Resources>
254+
<t:ExampleClass x:Key=""key1"" OtherProp=""a""> </t:ExampleClass>
255+
<t:ExampleClass x:Key=""key2"" OtherProp=""b"" />
256+
</Window.Resources>
257+
</Window>
258+
");
259+
}
260+
261+
[Test]
262+
public void ResourceDictionaryExplicitWinfx2006()
263+
{
264+
ResourceDictionaryExplicitInternal("http://schemas.microsoft.com/winfx/2006/xaml/presentation");
265+
}
266+
267+
[Test]
268+
[Ignore("Own XamlParser should handle different namespaces pointing to same types, because builtin XamlReader does.")]
269+
public void ResourceDictionaryExplicitNetfx2007()
270+
{
271+
// The reason this test case fails is because own XamlParser cannot always handle the case where multiple xmlns points to the same type.
272+
// 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).
273+
ResourceDictionaryExplicitInternal("http://schemas.microsoft.com/netfx/2007/xaml/presentation");
274+
}
275+
276+
void ResourceDictionaryExplicitInternal(string defaultXmlns)
277+
{
278+
TestLoading(@"
279+
<Window
280+
xmlns=""" + defaultXmlns + @"""
281+
xmlns:t=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
282+
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
283+
<Window.Resources>
284+
<ResourceDictionary>
285+
<t:ExampleClass x:Key=""key1"" OtherProp=""a""> </t:ExampleClass>
286+
<t:ExampleClass x:Key=""key2"" OtherProp=""b"" />
287+
</ResourceDictionary>
288+
</Window.Resources>
289+
</Window>
290+
");
291+
}
292+
193293
[Test]
194294
public void ExampleServiceTest()
195295
{
@@ -203,6 +303,25 @@ public void ExampleServiceTest()
203303
");
204304
}
205305

306+
[Test]
307+
public void ExampleServiceCollectionTest()
308+
{
309+
TestLoading(@"
310+
<t:ExampleDependencyObject
311+
xmlns=""http://schemas.microsoft.com/netfx/2007/xaml/presentation""
312+
xmlns:t=""" + XamlTypeFinderTests.XamlDomTestsNamespace + @"""
313+
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
314+
<t:ExampleService.ExampleCollection>
315+
<t:ExampleClassList>
316+
<t:ExampleClass OtherProp=""a""> </t:ExampleClass>
317+
<t:ExampleClass OtherProp=""b"" />
318+
<t:ExampleClass OtherProp=""c"" />
319+
</t:ExampleClassList>
320+
</t:ExampleService.ExampleCollection>
321+
</t:ExampleDependencyObject>
322+
");
323+
}
324+
206325
[Test]
207326
public void ExampleClassObjectPropWithStringValue()
208327
{

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public static bool IsCollectionType(Type type)
2525
return typeof(IList).IsAssignableFrom(type)
2626
|| type.IsArray
2727
|| typeof(IAddChild).IsAssignableFrom(type)
28-
|| typeof(ResourceDictionary).IsAssignableFrom(type);
28+
|| typeof(IDictionary).IsAssignableFrom(type);
2929
}
3030

3131
/// <summary>
@@ -65,7 +65,7 @@ public static void AddToCollection(Type collectionType, object collectionInstanc
6565
} else {
6666
addChild.AddChild(newElement.GetValueFor(null));
6767
}
68-
} else if (collectionInstance is ResourceDictionary) {
68+
} else if (collectionInstance is IDictionary) {
6969
object val = newElement.GetValueFor(null);
7070
object key = newElement is XamlObject ? ((XamlObject)newElement).GetXamlAttribute("Key") : null;
7171
//if (key == null || key == "") {
@@ -74,7 +74,7 @@ public static void AddToCollection(Type collectionType, object collectionInstanc
7474
//}
7575
if (key == null || (key as string) == "")
7676
key = val;
77-
((ResourceDictionary)collectionInstance).Add(key, val);
77+
((IDictionary)collectionInstance).Add(key, val);
7878
} else {
7979
collectionType.InvokeMember(
8080
"Add", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance,

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

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -285,32 +285,51 @@ XamlObject ParseObject(XmlElement element)
285285
void ParseObjectContent(XamlObject obj, XmlElement element, XamlPropertyInfo defaultProperty, XamlTextValue initializeFromTextValueInsteadOfConstructor)
286286
{
287287
bool isDefaultValueSet = false;
288-
object defaultPropertyValue = null;
289-
XamlProperty defaultCollectionProperty = null;
290288

291-
if (defaultProperty != null && defaultProperty.IsCollection && !element.IsEmpty) {
292-
defaultPropertyValue = defaultProperty.GetValue(obj.Instance);
293-
obj.AddProperty(defaultCollectionProperty = new XamlProperty(obj, defaultProperty));
289+
XamlProperty collectionProperty = null;
290+
object collectionInstance = null;
291+
Type collectionType = null;
292+
XmlElement collectionPropertyElement = null;
293+
var elementChildNodes = GetNormalizedChildNodes(element);
294+
295+
if (defaultProperty == null && obj.Instance != null && CollectionSupport.IsCollectionType(obj.Instance.GetType())) {
296+
XamlObject parentObj = obj.ParentObject;
297+
var parentElement = element.ParentNode;
298+
XamlPropertyInfo propertyInfo = GetPropertyInfo(settings.TypeFinder, parentObj.Instance, parentObj.ElementType, parentElement.NamespaceURI, parentElement.LocalName);
299+
collectionProperty = FindExistingXamlProperty(parentObj, propertyInfo);
300+
collectionInstance = obj.Instance;
301+
collectionType = obj.ElementType;
302+
collectionPropertyElement = element;
303+
} else if (defaultProperty != null && defaultProperty.IsCollection && !element.IsEmpty) {
304+
foreach (XmlNode childNode in elementChildNodes) {
305+
XmlElement childElement = childNode as XmlElement;
306+
if (childElement == null || !ObjectChildElementIsPropertyElement(childElement)) {
307+
obj.AddProperty(collectionProperty = new XamlProperty(obj, defaultProperty));
308+
collectionType = defaultProperty.ReturnType;
309+
collectionInstance = defaultProperty.GetValue(obj.Instance);
310+
break;
311+
}
312+
}
294313
}
295314

296-
foreach (XmlNode childNode in GetNormalizedChildNodes(element)) {
315+
foreach (XmlNode childNode in elementChildNodes) {
297316
XmlElement childElement = childNode as XmlElement;
298317
if (childElement != null) {
299318
if (childElement.NamespaceURI == XamlConstants.XamlNamespace)
300319
continue;
301320

302321
if (ObjectChildElementIsPropertyElement(childElement)) {
303-
ParseObjectChildElementAsPropertyElement(obj, childElement, defaultProperty, defaultPropertyValue);
322+
ParseObjectChildElementAsPropertyElement(obj, childElement, defaultProperty);
304323
continue;
305324
}
306325
}
307326
if (initializeFromTextValueInsteadOfConstructor != null)
308327
continue;
309328
XamlPropertyValue childValue = ParseValue(childNode);
310329
if (childValue != null) {
311-
if (defaultProperty != null && defaultProperty.IsCollection) {
312-
defaultCollectionProperty.ParserAddCollectionElement(null, childValue);
313-
CollectionSupport.AddToCollection(defaultProperty.ReturnType, defaultPropertyValue, childValue);
330+
if (collectionProperty != null) {
331+
collectionProperty.ParserAddCollectionElement(collectionPropertyElement, childValue);
332+
CollectionSupport.AddToCollection(collectionType, collectionInstance, childValue);
314333
} else {
315334
if (defaultProperty == null)
316335
throw new XamlLoadException("This element does not have a default value, cannot assign to it");
@@ -386,6 +405,16 @@ XamlPropertyValue ParseValueCore(XmlNode childNode)
386405
return null;
387406
}
388407

408+
static XamlProperty FindExistingXamlProperty(XamlObject obj, XamlPropertyInfo propertyInfo)
409+
{
410+
foreach (XamlProperty existing in obj.Properties) {
411+
if (existing.propertyInfo.FullyQualifiedName == propertyInfo.FullyQualifiedName)
412+
return existing;
413+
}
414+
415+
throw new XamlLoadException("Existing XamlProperty " + propertyInfo.FullyQualifiedName + " not found.");
416+
}
417+
389418
static XamlPropertyInfo GetDefaultProperty(Type elementType)
390419
{
391420
foreach (ContentPropertyAttribute cpa in elementType.GetCustomAttributes(typeof(ContentPropertyAttribute), true)) {
@@ -533,7 +562,12 @@ static bool ObjectChildElementIsPropertyElement(XmlElement element)
533562
return element.LocalName.Contains(".");
534563
}
535564

536-
void ParseObjectChildElementAsPropertyElement(XamlObject obj, XmlElement element, XamlPropertyInfo defaultProperty, object defaultPropertyValue)
565+
static bool IsElementChildACollectionForProperty(XamlTypeFinder typeFinder, XmlElement element, XamlPropertyInfo propertyInfo)
566+
{
567+
return element.ChildNodes.Count == 1 && propertyInfo.ReturnType.IsAssignableFrom(FindType(typeFinder, element.FirstChild.NamespaceURI, element.FirstChild.LocalName));
568+
}
569+
570+
void ParseObjectChildElementAsPropertyElement(XamlObject obj, XmlElement element, XamlPropertyInfo defaultProperty)
537571
{
538572
Debug.Assert(element.LocalName.Contains("."));
539573
// this is a element property syntax
@@ -542,23 +576,29 @@ void ParseObjectChildElementAsPropertyElement(XamlObject obj, XmlElement element
542576
bool valueWasSet = false;
543577

544578
object collectionInstance = null;
579+
bool isElementChildACollectionForProperty = false;
545580
XamlProperty collectionProperty = null;
546581
if (propertyInfo.IsCollection) {
547582
if (defaultProperty != null && defaultProperty.FullyQualifiedName == propertyInfo.FullyQualifiedName) {
548-
collectionInstance = defaultPropertyValue;
549583
foreach (XamlProperty existing in obj.Properties) {
550584
if (existing.propertyInfo == defaultProperty) {
551585
collectionProperty = existing;
552586
break;
553587
}
554588
}
555-
} else {
556-
collectionInstance = propertyInfo.GetValue(obj.Instance);
557589
}
590+
558591
if (collectionProperty == null) {
559592
obj.AddProperty(collectionProperty = new XamlProperty(obj, propertyInfo));
560593
}
561-
collectionProperty.ParserSetPropertyElement(element);
594+
595+
isElementChildACollectionForProperty = IsElementChildACollectionForProperty(settings.TypeFinder, element, propertyInfo);
596+
if (isElementChildACollectionForProperty)
597+
collectionProperty.ParserSetPropertyElement((XmlElement)element.FirstChild);
598+
else {
599+
collectionInstance = collectionProperty.propertyInfo.GetValue(obj.Instance);
600+
collectionProperty.ParserSetPropertyElement(element);
601+
}
562602
}
563603

564604
XmlSpace oldXmlSpace = currentXmlSpace;
@@ -570,7 +610,10 @@ void ParseObjectChildElementAsPropertyElement(XamlObject obj, XmlElement element
570610
XamlPropertyValue childValue = ParseValue(childNode);
571611
if (childValue != null) {
572612
if (propertyInfo.IsCollection) {
573-
if (collectionInstance!=null) {
613+
if (isElementChildACollectionForProperty) {
614+
collectionProperty.PropertyValue = childValue;
615+
}
616+
else {
574617
CollectionSupport.AddToCollection(propertyInfo.ReturnType, collectionInstance, childValue);
575618
collectionProperty.ParserAddCollectionElement(element, childValue);
576619
}

0 commit comments

Comments
 (0)