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

Commit 8de9207

Browse files
committed
Fixed so markup extensions where property is set by a StaticResource checks if the referenced resource is declared locally, and it its declared locally it forces the markup extension to be printed as element and not as shorthand XAML markup extension code. This is so the StaticResource can find the correct resource.
Also fixed so Resources element is always left first among the child elements when inserting new child.
1 parent 6b34ee0 commit 8de9207

4 files changed

Lines changed: 173 additions & 15 deletions

File tree

src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Tests/Designer/ModelTests.cs

Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
22
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
33

4-
using System;
5-
using System.Text;
6-
using System.IO;
7-
using System.Xml;
8-
using System.Diagnostics;
9-
using System.Windows;
10-
using System.Windows.Controls;
11-
using System.Windows.Data;
12-
using NUnit.Framework;
13-
using ICSharpCode.WpfDesign.Designer;
14-
using ICSharpCode.WpfDesign.Designer.Xaml;
15-
using ICSharpCode.WpfDesign.Designer.Services;
4+
using System;
5+
using System.Text;
6+
using System.IO;
7+
using System.Xml;
8+
using System.Diagnostics;
9+
using System.Windows;
10+
using System.Windows.Controls;
11+
using System.Windows.Data;
12+
using NUnit.Framework;
13+
using ICSharpCode.WpfDesign.Designer;
14+
using ICSharpCode.WpfDesign.Designer.Xaml;
15+
using ICSharpCode.WpfDesign.Designer.Services;
1616

1717
namespace ICSharpCode.WpfDesign.Tests.Designer
1818
{
@@ -270,6 +270,86 @@ public void AddMultiBindingToTextBox()
270270
AssertCanvasDesignerOutput(expectedXaml, button.Context);
271271
AssertLog("");
272272
}
273+
274+
[Test]
275+
public void AddSimpleBinding()
276+
{
277+
DesignItem button = CreateCanvasContext("<Button/>");
278+
DesignItem canvas = button.Parent;
279+
DesignItem textBox = canvas.Services.Component.RegisterComponentForDesigner(new TextBox());
280+
canvas.Properties["Children"].CollectionElements.Add(textBox);
281+
282+
textBox.Properties[TextBox.TextProperty].SetValue(new Binding());
283+
textBox.Properties[TextBox.TextProperty].Value.Properties["Path"].SetValue("SomeProperty");
284+
285+
string expectedXaml = "<Button />\n" +
286+
"<TextBox Text=\"{Binding Path=SomeProperty}\" />\n";
287+
288+
AssertCanvasDesignerOutput(expectedXaml, button.Context);
289+
AssertLog("");
290+
}
291+
292+
[Test]
293+
public void AddBindingWithStaticResource()
294+
{
295+
DesignItem button = CreateCanvasContext("<Button/>");
296+
DesignItem canvas = button.Parent;
297+
DesignItem textBox = canvas.Services.Component.RegisterComponentForDesigner(new TextBox());
298+
canvas.Properties["Children"].CollectionElements.Add(textBox);
299+
300+
DesignItemProperty resProp = canvas.Properties.GetProperty("Resources");
301+
Assert.IsTrue(resProp.IsCollection);
302+
DesignItem exampleClassItem = canvas.Services.Component.RegisterComponentForDesigner(new ExampleClass());
303+
exampleClassItem.Key = "bindingSource";
304+
resProp.CollectionElements.Add(exampleClassItem);
305+
306+
DesignItem bindingItem = canvas.Services.Component.RegisterComponentForDesigner(new Binding());
307+
textBox.Properties[TextBox.TextProperty].SetValue(bindingItem);
308+
bindingItem.Properties["Path"].SetValue("StringProp");
309+
bindingItem.Properties["Source"].SetValue(new StaticResourceExtension());
310+
bindingItem.Properties["Source"].Value.Properties["ResourceKey"].SetValue("bindingSource");
311+
312+
string expectedXaml = "<Canvas.Resources>\n" +
313+
" <t:ExampleClass x:Key=\"bindingSource\" />\n" +
314+
"</Canvas.Resources>\n" +
315+
"<Button />\n" +
316+
"<TextBox Text=\"{Binding Path=StringProp, Source={StaticResource ResourceKey=bindingSource}}\" />";
317+
318+
AssertCanvasDesignerOutput(expectedXaml, button.Context);
319+
AssertLog("");
320+
}
321+
322+
[Test]
323+
public void AddBindingWithStaticResourceWhereResourceOnSameElement()
324+
{
325+
DesignItem button = CreateCanvasContext("<Button/>");
326+
DesignItem canvas = button.Parent;
327+
DesignItem textBox = canvas.Services.Component.RegisterComponentForDesigner(new TextBox());
328+
canvas.Properties["Children"].CollectionElements.Add(textBox);
329+
330+
DesignItemProperty resProp = textBox.Properties.GetProperty("Resources");
331+
Assert.IsTrue(resProp.IsCollection);
332+
DesignItem exampleClassItem = canvas.Services.Component.RegisterComponentForDesigner(new ExampleClass());
333+
exampleClassItem.Key = "bindingSource";
334+
resProp.CollectionElements.Add(exampleClassItem);
335+
336+
DesignItem bindingItem = canvas.Services.Component.RegisterComponentForDesigner(new Binding());
337+
bindingItem.Properties["Path"].SetValue("StringProp");
338+
bindingItem.Properties["Source"].SetValue(new StaticResourceExtension());
339+
bindingItem.Properties["Source"].Value.Properties["ResourceKey"].SetValue("bindingSource");
340+
textBox.Properties[TextBox.TextProperty].SetValue(bindingItem);
341+
342+
string expectedXaml = "<Button />\n" +
343+
"<TextBox>\n" +
344+
" <TextBox.Resources>\n" +
345+
" <t:ExampleClass x:Key=\"bindingSource\" />\n" +
346+
" </TextBox.Resources>\n" +
347+
" <Binding Path=\"StringProp\" Source=\"{StaticResource ResourceKey=bindingSource}\" />\n" +
348+
"</TextBox>";
349+
350+
AssertCanvasDesignerOutput(expectedXaml, button.Context);
351+
AssertLog("");
352+
}
273353
}
274354

275355
public class MyMultiConverter : IMultiValueConverter
@@ -284,4 +364,14 @@ public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
284364
return new object[targetTypes.Length];
285365
}
286366
}
367+
368+
public class ExampleClass
369+
{
370+
string stringProp;
371+
372+
public string StringProp {
373+
get { return stringProp; }
374+
set { stringProp = value; }
375+
}
376+
}
287377
}

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,35 @@ public static bool CanPrint(XamlObject obj)
2222
obj.ElementType == typeof(System.Windows.Data.PriorityBinding)) {
2323
return false;
2424
}
25+
26+
foreach (var property in obj.Properties.Where((prop) => prop.IsSet))
27+
{
28+
var value = property.PropertyValue;
29+
if (value is XamlTextValue)
30+
continue;
31+
else
32+
{
33+
XamlObject xamlObject = value as XamlObject;
34+
if (xamlObject == null || !xamlObject.IsMarkupExtension)
35+
return false;
36+
else {
37+
var staticResource = xamlObject.Instance as System.Windows.StaticResourceExtension;
38+
if (staticResource != null &&
39+
staticResource.ResourceKey != null) {
40+
XamlObject parent = GetNonMarkupExtensionParent(xamlObject);
41+
42+
if (parent != null) {
43+
var parentLocalResource = parent.ServiceProvider.Resolver.FindLocalResource(staticResource.ResourceKey);
44+
45+
// If resource with the specified key is declared locally on the same object as the StaticResource is being used the markup extension
46+
// must be printed as element to find the resource, otherwise it will search from parent-parent and find none or another resource.
47+
if (parentLocalResource != null)
48+
return false;
49+
}
50+
}
51+
}
52+
}
53+
}
2554

2655
return true;
2756
}
@@ -58,5 +87,16 @@ public static string Print(XamlObject obj)
5887
sb.Append("}");
5988
return sb.ToString();
6089
}
90+
91+
private static XamlObject GetNonMarkupExtensionParent(XamlObject markupExtensionObject)
92+
{
93+
System.Diagnostics.Debug.Assert(markupExtensionObject.IsMarkupExtension);
94+
95+
XamlObject obj = markupExtensionObject;
96+
while (obj != null && obj.IsMarkupExtension) {
97+
obj = obj.ParentObject;
98+
}
99+
return obj;
100+
}
61101
}
62102
}

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.ComponentModel;
77
using System.Diagnostics;
8+
using System.Linq;
89
using System.Text;
910
using System.Xml;
1011
using System.Windows;
@@ -267,6 +268,13 @@ internal void ParserSetPropertyElement(XmlElement propertyElement)
267268
oldPropertyElement.ParentNode.RemoveChild(oldPropertyElement);
268269
}
269270
}
271+
272+
bool IsFirstChildResources(XamlObject obj)
273+
{
274+
return obj.XmlElement.FirstChild != null &&
275+
obj.XmlElement.FirstChild.Name.EndsWith("." + XamlConstants.ResourcesPropertyName) &&
276+
obj.Properties.Where((prop) => prop.IsResources).FirstOrDefault() != null;
277+
}
270278

271279
XmlElement CreatePropertyElement()
272280
{
@@ -287,11 +295,22 @@ internal void AddChildNodeToProperty(XmlNode newChildNode)
287295
}
288296
if (_propertyElement == null) {
289297
if (PropertyName == parentObject.ContentPropertyName) {
290-
parentObject.XmlElement.InsertBefore(newChildNode, parentObject.XmlElement.FirstChild);
298+
if (IsFirstChildResources(parentObject)) {
299+
// Resources element should always be first
300+
parentObject.XmlElement.InsertAfter(newChildNode, parentObject.XmlElement.FirstChild);
301+
}
302+
else
303+
parentObject.XmlElement.InsertBefore(newChildNode, parentObject.XmlElement.FirstChild);
291304
return;
292305
}
293-
_propertyElement = CreatePropertyElement();
294-
parentObject.XmlElement.InsertBefore(_propertyElement, parentObject.XmlElement.FirstChild);
306+
_propertyElement = CreatePropertyElement();
307+
308+
if (IsFirstChildResources(parentObject)) {
309+
// Resources element should always be first
310+
parentObject.XmlElement.InsertAfter(_propertyElement, parentObject.XmlElement.FirstChild);
311+
}
312+
else
313+
parentObject.XmlElement.InsertBefore(_propertyElement, parentObject.XmlElement.FirstChild);
295314
}
296315
_propertyElement.AppendChild(newChildNode);
297316
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,14 @@ public object FindResource(object key)
9696
}
9797
return null;
9898
}
99+
100+
public object FindLocalResource(object key)
101+
{
102+
FrameworkElement el = containingObject.Instance as FrameworkElement;
103+
if (el != null) {
104+
return el.Resources[key];
105+
}
106+
return null;
107+
}
99108
}
100109
}

0 commit comments

Comments
 (0)