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

Commit e4a573d

Browse files
committed
Merge pull request #470 from gumme/WpfDesignerCollectionUndoFix
Wpf designer fixes for explicit collection and dictionary
2 parents 428558d + 5ccc93f commit e4a573d

File tree

4 files changed

+279
-2
lines changed

4 files changed

+279
-2
lines changed

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

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,176 @@ public void UndoRedoChangeGroupTest()
149149
AssertLog("");
150150
}
151151

152+
[Test]
153+
public void UndoRedoImplicitList()
154+
{
155+
UndoRedoListInternal(false);
156+
}
157+
158+
[Test]
159+
public void UndoRedoExplicitList()
160+
{
161+
UndoRedoListInternal(true);
162+
}
163+
164+
void UndoRedoListInternal(bool useExplicitList)
165+
{
166+
DesignItem button = CreateCanvasContext("<Button/>");
167+
UndoService s = button.Context.Services.GetService<UndoService>();
168+
IComponentService component = button.Context.Services.Component;
169+
string expectedXamlWithList;
170+
DesignItemProperty otherListProp;
171+
172+
Assert.IsFalse(s.CanUndo);
173+
Assert.IsFalse(s.CanRedo);
174+
175+
using (ChangeGroup g = button.OpenGroup("UndoRedoListInternal test")) {
176+
DesignItem containerItem = component.RegisterComponentForDesigner(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassContainer());
177+
178+
otherListProp = containerItem.Properties["OtherList"];
179+
180+
if(useExplicitList) {
181+
otherListProp.SetValue(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassList());
182+
183+
expectedXamlWithList = @"<Button>
184+
<Button.Tag>
185+
<Controls0:ExampleClassContainer>
186+
<Controls0:ExampleClassContainer.OtherList>
187+
<Controls0:ExampleClassList>
188+
<Controls0:ExampleClass StringProp=""String value"" />
189+
</Controls0:ExampleClassList>
190+
</Controls0:ExampleClassContainer.OtherList>
191+
</Controls0:ExampleClassContainer>
192+
</Button.Tag>
193+
</Button>";
194+
} else {
195+
expectedXamlWithList = @"<Button>
196+
<Button.Tag>
197+
<Controls0:ExampleClassContainer>
198+
<Controls0:ExampleClassContainer.OtherList>
199+
<Controls0:ExampleClass StringProp=""String value"" />
200+
</Controls0:ExampleClassContainer.OtherList>
201+
</Controls0:ExampleClassContainer>
202+
</Button.Tag>
203+
</Button>";
204+
}
205+
206+
DesignItem exampleClassItem = component.RegisterComponentForDesigner(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClass());
207+
exampleClassItem.Properties["StringProp"].SetValue("String value");
208+
otherListProp.CollectionElements.Add(exampleClassItem);
209+
210+
button.Properties["Tag"].SetValue(containerItem);
211+
g.Commit();
212+
}
213+
214+
Assert.IsTrue(s.CanUndo);
215+
Assert.IsFalse(s.CanRedo);
216+
AssertCanvasDesignerOutput(expectedXamlWithList, button.Context, "xmlns:Controls0=\"" + ICSharpCode.WpfDesign.Tests.XamlDom.XamlTypeFinderTests.XamlDomTestsNamespace + "\"");
217+
218+
otherListProp = button.Properties["Tag"].Value.Properties["OtherList"];
219+
Assert.IsTrue(((ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassList)otherListProp.ValueOnInstance).Count == otherListProp.CollectionElements.Count);
220+
221+
s.Undo();
222+
Assert.IsFalse(s.CanUndo);
223+
Assert.IsTrue(s.CanRedo);
224+
AssertCanvasDesignerOutput("<Button>\n</Button>", button.Context, "xmlns:Controls0=\"" + ICSharpCode.WpfDesign.Tests.XamlDom.XamlTypeFinderTests.XamlDomTestsNamespace + "\"");
225+
226+
s.Redo();
227+
Assert.IsTrue(s.CanUndo);
228+
Assert.IsFalse(s.CanRedo);
229+
AssertCanvasDesignerOutput(expectedXamlWithList, button.Context, "xmlns:Controls0=\"" + ICSharpCode.WpfDesign.Tests.XamlDom.XamlTypeFinderTests.XamlDomTestsNamespace + "\"");
230+
231+
otherListProp = button.Properties["Tag"].Value.Properties["OtherList"];
232+
Assert.IsTrue(((ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassList)otherListProp.ValueOnInstance).Count == otherListProp.CollectionElements.Count);
233+
234+
AssertLog("");
235+
}
236+
237+
[Test]
238+
public void UndoRedoImplicitDictionary()
239+
{
240+
UndoRedoDictionaryInternal(false);
241+
}
242+
243+
[Test]
244+
public void UndoRedoExplicitDictionary()
245+
{
246+
UndoRedoDictionaryInternal(true);
247+
}
248+
249+
void UndoRedoDictionaryInternal(bool useExplicitDictionary)
250+
{
251+
DesignItem button = CreateCanvasContext("<Button/>");
252+
UndoService s = button.Context.Services.GetService<UndoService>();
253+
IComponentService component = button.Context.Services.Component;
254+
string expectedXamlWithDictionary;
255+
DesignItemProperty dictionaryProp;
256+
257+
Assert.IsFalse(s.CanUndo);
258+
Assert.IsFalse(s.CanRedo);
259+
260+
using (ChangeGroup g = button.OpenGroup("UndoRedoDictionaryInternal test")) {
261+
DesignItem containerItem = component.RegisterComponentForDesigner(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassContainer());
262+
263+
dictionaryProp = containerItem.Properties["Dictionary"];
264+
265+
if(useExplicitDictionary) {
266+
dictionaryProp.SetValue(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassDictionary());
267+
268+
expectedXamlWithDictionary = @"<Button>
269+
<Button.Tag>
270+
<Controls0:ExampleClassContainer>
271+
<Controls0:ExampleClassContainer.Dictionary>
272+
<Controls0:ExampleClassDictionary>
273+
<Controls0:ExampleClass x:Key=""testKey"" StringProp=""String value"" />
274+
</Controls0:ExampleClassDictionary>
275+
</Controls0:ExampleClassContainer.Dictionary>
276+
</Controls0:ExampleClassContainer>
277+
</Button.Tag>
278+
</Button>";
279+
} else {
280+
expectedXamlWithDictionary = @"<Button>
281+
<Button.Tag>
282+
<Controls0:ExampleClassContainer>
283+
<Controls0:ExampleClassContainer.Dictionary>
284+
<Controls0:ExampleClass x:Key=""testKey"" StringProp=""String value"" />
285+
</Controls0:ExampleClassContainer.Dictionary>
286+
</Controls0:ExampleClassContainer>
287+
</Button.Tag>
288+
</Button>";
289+
}
290+
291+
DesignItem exampleClassItem = component.RegisterComponentForDesigner(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClass());
292+
exampleClassItem.Key = "testKey";
293+
exampleClassItem.Properties["StringProp"].SetValue("String value");
294+
dictionaryProp.CollectionElements.Add(exampleClassItem);
295+
296+
button.Properties["Tag"].SetValue(containerItem);
297+
g.Commit();
298+
}
299+
300+
Assert.IsTrue(s.CanUndo);
301+
Assert.IsFalse(s.CanRedo);
302+
AssertCanvasDesignerOutput(expectedXamlWithDictionary, button.Context, "xmlns:Controls0=\"" + ICSharpCode.WpfDesign.Tests.XamlDom.XamlTypeFinderTests.XamlDomTestsNamespace + "\"");
303+
304+
dictionaryProp = button.Properties["Tag"].Value.Properties["Dictionary"];
305+
Assert.IsTrue(((ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassDictionary)dictionaryProp.ValueOnInstance).Count == dictionaryProp.CollectionElements.Count);
306+
307+
s.Undo();
308+
Assert.IsFalse(s.CanUndo);
309+
Assert.IsTrue(s.CanRedo);
310+
AssertCanvasDesignerOutput("<Button>\n</Button>", button.Context, "xmlns:Controls0=\"" + ICSharpCode.WpfDesign.Tests.XamlDom.XamlTypeFinderTests.XamlDomTestsNamespace + "\"");
311+
312+
s.Redo();
313+
Assert.IsTrue(s.CanUndo);
314+
Assert.IsFalse(s.CanRedo);
315+
AssertCanvasDesignerOutput(expectedXamlWithDictionary, button.Context, "xmlns:Controls0=\"" + ICSharpCode.WpfDesign.Tests.XamlDom.XamlTypeFinderTests.XamlDomTestsNamespace + "\"");
316+
317+
dictionaryProp = button.Properties["Tag"].Value.Properties["Dictionary"];
318+
Assert.IsTrue(((ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassDictionary)dictionaryProp.ValueOnInstance).Count == dictionaryProp.CollectionElements.Count);
319+
320+
AssertLog("");
321+
}
152322

153323
[Test]
154324
public void AddTextBoxToCanvas()
@@ -254,6 +424,89 @@ public void ClearImplicitChildCollection()
254424
AssertLog("");
255425
}
256426

427+
[Test]
428+
public void ClearExplicitList()
429+
{
430+
DesignItem button = CreateCanvasContext("<Button/>");
431+
432+
button.Properties["Tag"].SetValue(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassContainer());
433+
var containerItem = button.Properties["Tag"].Value;
434+
var otherListProp = containerItem.Properties["OtherList"];
435+
otherListProp.SetValue(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassList());
436+
437+
DesignItem exampleClassItem = button.Context.Services.Component.RegisterComponentForDesigner(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClass());
438+
exampleClassItem.Properties["StringProp"].SetValue("String value");
439+
otherListProp.CollectionElements.Add(exampleClassItem);
440+
441+
var listInstance = (ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassList)otherListProp.ValueOnInstance;
442+
Assert.AreEqual(1, listInstance.Count);
443+
Assert.AreEqual(1, otherListProp.CollectionElements.Count);
444+
445+
button.Properties["Tag"].Value.Properties["OtherList"].CollectionElements.Clear();
446+
447+
Assert.AreEqual(0, listInstance.Count);
448+
Assert.AreEqual(0, otherListProp.CollectionElements.Count);
449+
450+
AssertCanvasDesignerOutput(@"<Button>
451+
<Button.Tag>
452+
<Controls0:ExampleClassContainer>
453+
<Controls0:ExampleClassContainer.OtherList>
454+
<Controls0:ExampleClassList>
455+
</Controls0:ExampleClassList>
456+
</Controls0:ExampleClassContainer.OtherList>
457+
</Controls0:ExampleClassContainer>
458+
</Button.Tag>
459+
</Button>", button.Context, "xmlns:Controls0=\"" + ICSharpCode.WpfDesign.Tests.XamlDom.XamlTypeFinderTests.XamlDomTestsNamespace + "\"");
460+
AssertLog("");
461+
}
462+
463+
[Test]
464+
public void ClearExplicitDictionary()
465+
{
466+
DesignItem button = CreateCanvasContext("<Button/>");
467+
468+
button.Properties["Tag"].SetValue(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassContainer());
469+
var containerItem = button.Properties["Tag"].Value;
470+
var dictionaryProp = containerItem.Properties["Dictionary"];
471+
dictionaryProp.SetValue(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassDictionary());
472+
473+
DesignItem exampleClassItem = button.Context.Services.Component.RegisterComponentForDesigner(new ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClass());
474+
exampleClassItem.Key = "testKey";
475+
exampleClassItem.Properties["StringProp"].SetValue("String value");
476+
dictionaryProp.CollectionElements.Add(exampleClassItem);
477+
478+
var dictionaryInstance = (ICSharpCode.WpfDesign.Tests.XamlDom.ExampleClassDictionary)dictionaryProp.ValueOnInstance;
479+
Assert.AreEqual(1, dictionaryInstance.Count);
480+
Assert.AreEqual(1, dictionaryProp.CollectionElements.Count);
481+
482+
button.Properties["Tag"].Value.Properties["Dictionary"].CollectionElements.Clear();
483+
484+
Assert.AreEqual(0, dictionaryInstance.Count);
485+
Assert.AreEqual(0, dictionaryProp.CollectionElements.Count);
486+
487+
dictionaryProp.CollectionElements.Add(exampleClassItem);
488+
489+
Assert.AreEqual(1, dictionaryInstance.Count);
490+
Assert.AreEqual(1, dictionaryProp.CollectionElements.Count);
491+
492+
button.Properties["Tag"].Value.Properties["Dictionary"].CollectionElements.Clear();
493+
494+
Assert.AreEqual(0, dictionaryInstance.Count);
495+
Assert.AreEqual(0, dictionaryProp.CollectionElements.Count);
496+
497+
AssertCanvasDesignerOutput(@"<Button>
498+
<Button.Tag>
499+
<Controls0:ExampleClassContainer>
500+
<Controls0:ExampleClassContainer.Dictionary>
501+
<Controls0:ExampleClassDictionary>
502+
</Controls0:ExampleClassDictionary>
503+
</Controls0:ExampleClassContainer.Dictionary>
504+
</Controls0:ExampleClassContainer>
505+
</Button.Tag>
506+
</Button>", button.Context, "xmlns:Controls0=\"" + ICSharpCode.WpfDesign.Tests.XamlDom.XamlTypeFinderTests.XamlDomTestsNamespace + "\"");
507+
AssertLog("");
508+
}
509+
257510
[Test]
258511
public void AddMultiBindingToTextBox()
259512
{

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ protected override void RemoveItem(int index)
5454
XamlPropertyInfo info = property.propertyInfo;
5555
object collection = info.GetValue(property.ParentObject.Instance);
5656
if (!CollectionSupport.RemoveItemAt(info.ReturnType, collection, index)) {
57-
CollectionSupport.RemoveItem(info.ReturnType, collection, this[index].GetValueFor(info));
57+
var propertyValue = this[index];
58+
CollectionSupport.RemoveItem(info.ReturnType, collection, propertyValue.GetValueFor(info), propertyValue);
5859
}
5960

6061
this[index].RemoveNodeFromParent();

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,20 @@ public static void RemoveItem(Type collectionType, object collectionInstance, ob
154154
new object[] { item },
155155
CultureInfo.InvariantCulture);
156156
}
157+
158+
/// <summary>
159+
/// Removes an item instance from the specified collection.
160+
/// </summary>
161+
internal static void RemoveItem(Type collectionType, object collectionInstance, object item, XamlPropertyValue element)
162+
{
163+
var dictionary = collectionInstance as IDictionary;
164+
var xamlObject = element as XamlObject;
165+
166+
if (dictionary != null && xamlObject != null) {
167+
dictionary.Remove(xamlObject.GetXamlAttribute("Key"));
168+
} else {
169+
RemoveItem(collectionType, collectionInstance, item);
170+
}
171+
}
157172
}
158173
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,13 +254,21 @@ public void Reset()
254254

255255
void ResetInternal()
256256
{
257+
bool isExplicitCollection = false;
258+
257259
if (propertyValue != null) {
260+
isExplicitCollection = IsCollection;
261+
258262
propertyValue.RemoveNodeFromParent();
259263
propertyValue.ParentProperty = null;
260264
propertyValue = null;
261265
}
262266
if (_propertyElement != null) {
263-
_propertyElement.ParentNode.RemoveChild(_propertyElement);
267+
Debug.Assert(!isExplicitCollection || _propertyElement.ParentNode == null);
268+
269+
if (!isExplicitCollection) {
270+
_propertyElement.ParentNode.RemoveChild(_propertyElement);
271+
}
264272
_propertyElement = null;
265273
}
266274
}

0 commit comments

Comments
 (0)