Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/xamarin/Xamarin.PropertyEditing.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Maupin <me@ermau.com>2018-12-14 01:13:09 +0300
committerGitHub <noreply@github.com>2018-12-14 01:13:09 +0300
commit24f851b9c3abe3cfe86182d2c9151b4ec61c883f (patch)
tree05d69b3a3beaeea69be4e97359fe8d6ad61e4460
parentcd248d8fa5306f3ab05fabc72d6e869dd616ba9a (diff)
parent6ae9f0fbd0a55b6bf9b927b1ae9bb38ed3e0741b (diff)
Merge pull request #462 from xamarin/ermau-low-freq
Support low-frequency properties
-rw-r--r--Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs23
-rw-r--r--Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs29
-rw-r--r--Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs4
-rw-r--r--Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs4
-rw-r--r--Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockBrushPropertyInfo.cs2
-rw-r--r--Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs4
-rw-r--r--Xamarin.PropertyEditing.Tests/OrderedDictionaryTests.cs1351
-rw-r--r--Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs38
-rw-r--r--Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj1
-rw-r--r--Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs2
-rw-r--r--Xamarin.PropertyEditing.Windows/Themes/PropertyEditorPanelStyle.xaml91
-rw-r--r--Xamarin.PropertyEditing.Windows/Themes/Resources.xaml36
-rw-r--r--Xamarin.PropertyEditing/IPropertyInfo.cs8
-rw-r--r--Xamarin.PropertyEditing/OrderedDictionary.cs120
-rw-r--r--Xamarin.PropertyEditing/Reflection/ReflectionPropertyInfo.cs2
-rw-r--r--Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs202
16 files changed, 1769 insertions, 148 deletions
diff --git a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs
index 2334a6e..da40034 100644
--- a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs
+++ b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs
@@ -30,12 +30,14 @@ namespace Xamarin.PropertyEditing.Mac
var childCount = 0;
if (this.vm.ArrangeMode == PropertyArrangeMode.Name)
- childCount = Filtering ? this.vm.ArrangedEditors[0].Count : this.vm.ArrangedEditors[0].Count + 1;
+ childCount = Filtering ? this.vm.ArrangedEditors[0].Editors.Count : this.vm.ArrangedEditors[0].Editors.Count + 1;
else {
if (item == null)
childCount = Filtering ? this.vm.ArrangedEditors.Count : this.vm.ArrangedEditors.Count + 1;
- else
- childCount = ((IGroupingList<string, EditorViewModel>)((NSObjectFacade)item).Target).Count;
+ else {
+ var group = (PanelGroupViewModel)((NSObjectFacade)item).Target;
+ childCount = group.Editors.Count + group.UncommonEditors.Count;
+ }
}
return childCount;
@@ -50,12 +52,19 @@ namespace Xamarin.PropertyEditing.Mac
element = null;
else {
if (this.vm.ArrangeMode == PropertyArrangeMode.Name)
- element = Filtering ? this.vm.ArrangedEditors[0][(int)childIndex] : this.vm.ArrangedEditors[0][(int)childIndex - 1];
+ element = Filtering ? this.vm.ArrangedEditors[0].Editors[(int)childIndex] : this.vm.ArrangedEditors[0].Editors[(int)childIndex - 1];
else {
if (item == null)
element = Filtering ? this.vm.ArrangedEditors[(int)childIndex] : this.vm.ArrangedEditors[(int)childIndex - 1];
else {
- element = ((IGroupingList<string, EditorViewModel>)((NSObjectFacade)item).Target)[(int)childIndex];
+ var group = (PanelGroupViewModel)((NSObjectFacade)item).Target;
+ var list = group.Editors;
+ if (childIndex >= list.Count) {
+ childIndex -= list.Count;
+ list = group.UncommonEditors;
+ }
+
+ element = list[(int)childIndex];
}
}
}
@@ -68,13 +77,13 @@ namespace Xamarin.PropertyEditing.Mac
if (this.vm.ArrangeMode == PropertyArrangeMode.Name)
return false;
- return ((NSObjectFacade)item).Target is IGroupingList<string, EditorViewModel>;
+ return ((NSObjectFacade)item).Target is PanelGroupViewModel;
}
public NSObject GetFacade (object element)
{
NSObject facade;
- if (element is IGrouping<string, PropertyViewModel>) {
+ if (element is PanelGroupViewModel) {
if (!this.groupFacades.TryGetValue (element, out facade)) {
this.groupFacades[element] = facade = new NSObjectFacade (element);
}
diff --git a/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs b/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs
index 471d1d4..7a0209a 100644
--- a/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs
+++ b/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs
@@ -24,12 +24,12 @@ namespace Xamarin.PropertyEditing.Mac
if (!String.IsNullOrWhiteSpace (this.dataSource.DataContext.FilterText)) {
outlineView.ExpandItem (null, true);
} else {
- foreach (IGrouping<string, EditorViewModel> g in this.dataSource.DataContext.ArrangedEditors) {
+ foreach (PanelGroupViewModel g in this.dataSource.DataContext.ArrangedEditors) {
NSObject item;
if (!this.dataSource.TryGetFacade (g, out item))
continue;
- if (this.dataSource.DataContext.GetIsExpanded (g.Key))
+ if (this.dataSource.DataContext.GetIsExpanded (g.Category))
outlineView.ExpandItem (item);
else
outlineView.CollapseItem (item);
@@ -41,7 +41,8 @@ namespace Xamarin.PropertyEditing.Mac
public override NSView GetView (NSOutlineView outlineView, NSTableColumn tableColumn, NSObject item)
{
EditorViewModel evm;
- IGroupingList<string, EditorViewModel> group;
+ PropertyViewModel vm;
+ PanelGroupViewModel group;
string cellIdentifier;
GetVMGroupCellItendifiterFromFacade (item, out evm, out group, out cellIdentifier);
@@ -53,9 +54,9 @@ namespace Xamarin.PropertyEditing.Mac
};
}
- labelContainer.StringValue = group.Key;
+ labelContainer.StringValue = group.Category;
- if (this.dataSource.DataContext.GetIsExpanded (group.Key)) {
+ if (this.dataSource.DataContext.GetIsExpanded (group.Category)) {
SynchronizationContext.Current.Post (s => {
outlineView.ExpandItem (item);
}, null);
@@ -103,7 +104,7 @@ namespace Xamarin.PropertyEditing.Mac
public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item)
{
- return (!(item is NSObjectFacade) || !(((NSObjectFacade)item).Target is IGroupingList<string, EditorViewModel>));
+ return (!(item is NSObjectFacade) || !(((NSObjectFacade)item).Target is PanelGroupViewModel));
}
public override void ItemDidExpand (NSNotification notification)
@@ -112,9 +113,9 @@ namespace Xamarin.PropertyEditing.Mac
return;
NSObjectFacade facade = notification.UserInfo.Values[0] as NSObjectFacade;
- var group = facade.Target as IGroupingList<string, EditorViewModel>;
+ var group = facade.Target as PanelGroupViewModel;
if (group != null)
- this.dataSource.DataContext.SetIsExpanded (group.Key, isExpanded: true);
+ this.dataSource.DataContext.SetIsExpanded (group.Category, isExpanded: true);
}
public override void ItemDidCollapse (NSNotification notification)
@@ -123,15 +124,15 @@ namespace Xamarin.PropertyEditing.Mac
return;
NSObjectFacade facade = notification.UserInfo.Values[0] as NSObjectFacade;
- var group = facade.Target as IGroupingList<string, EditorViewModel>;
+ var group = facade.Target as PanelGroupViewModel;
if (group != null)
- this.dataSource.DataContext.SetIsExpanded (group.Key, isExpanded: false);
+ this.dataSource.DataContext.SetIsExpanded (group.Category, isExpanded: false);
}
public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item)
{
EditorViewModel vm;
- IGroupingList<string, EditorViewModel> group;
+ PanelGroupViewModel group;
string cellIdentifier;
GetVMGroupCellItendifiterFromFacade (item, out vm, out group, out cellIdentifier);
@@ -211,14 +212,14 @@ namespace Xamarin.PropertyEditing.Mac
return new PanelHeaderEditorControl ();
}
- private void GetVMGroupCellItendifiterFromFacade (NSObject item, out EditorViewModel vm, out IGroupingList<string, EditorViewModel> group, out string cellIdentifier)
+ private void GetVMGroupCellItendifiterFromFacade (NSObject item, out EditorViewModel vm, out PanelGroupViewModel group, out string cellIdentifier)
{
var facade = (NSObjectFacade)item;
vm = facade.Target as EditorViewModel;
- group = facade.Target as IGroupingList<string, EditorViewModel>;
+ group = facade.Target as PanelGroupViewModel;
cellIdentifier = facade.Target == null
? nameof (PanelHeaderEditorControl)
- : (group == null) ? vm.GetType ().FullName : group.Key;
+ : (group == null) ? vm.GetType ().FullName : group.Category;
}
}
}
diff --git a/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs b/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs
index 07859ef..66795a8 100644
--- a/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs
+++ b/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs
@@ -15,7 +15,7 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
bool canWrite = true, bool flag = false,
IEnumerable<Type> converterTypes = null,
string description = null, bool constrained = true, ValueSources valueSources = ValueSources.Local | ValueSources.Default | ValueSources.Binding,
- IReadOnlyList<InputMode> inputModes = null, PropertyVariationOption[] options = null)
+ IReadOnlyList<InputMode> inputModes = null, PropertyVariationOption[] options = null, bool isUncommon = false)
{
IPropertyInfo propertyInfo;
if (typeof(T).IsEnum) {
@@ -26,7 +26,7 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
} else if (inputModes != null) {
propertyInfo = new MockPropertyInfoWithInputTypes<T> (name, inputModes, description, category, canWrite, converterTypes, valueSources, options);
} else {
- propertyInfo = new MockPropertyInfo<T> (name, description, category, canWrite, converterTypes, valueSources, options);
+ propertyInfo = new MockPropertyInfo<T> (name, description, category, canWrite, converterTypes, valueSources, options, isUncommon);
}
AddProperty<T> (propertyInfo);
diff --git a/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs b/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs
index c724cc1..aa0f609 100644
--- a/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs
+++ b/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs
@@ -30,8 +30,8 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
});
AddProperty<FlagsNoValues> ("FlagsNoValues", ReadWrite, canWrite: true, flag: true);
AddProperty<FlagsWithValues> ("FlagsWithValues", ReadWrite, canWrite: true, flag: true);
- AddProperty<CommonPoint> ("Point", ReadWrite);
- AddProperty<CommonSize> ("Size", ReadWrite);
+ AddProperty<CommonPoint> ("Point", ReadWrite, isUncommon: true);
+ AddProperty<CommonSize> ("Size", ReadWrite, isUncommon: true);
AddProperty<CommonRectangle> ("Rectangle", ReadWrite);
AddProperty<CommonRatio> ("Ratio", ReadWrite);
AddProperty<CommonThickness> ("Thickness", ReadWrite);
diff --git a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockBrushPropertyInfo.cs b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockBrushPropertyInfo.cs
index cdc8697..74ad06e 100644
--- a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockBrushPropertyInfo.cs
+++ b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockBrushPropertyInfo.cs
@@ -35,6 +35,8 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
public bool CanWrite { get; }
+ public bool IsUncommon { get; }
+
public ValueSources ValueSources { get; }
public IReadOnlyList<PropertyVariationOption> Variations { get; }
diff --git a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs
index 9961161..9a3ea41 100644
--- a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs
+++ b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs
@@ -23,12 +23,13 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
public class MockPropertyInfo<T> : IPropertyInfo, IPropertyConverter, IEquatable<MockPropertyInfo<T>>
{
- public MockPropertyInfo (string name, string description = null, string category = null, bool canWrite = true, IEnumerable<Type> converterTypes = null, ValueSources valueSources = ValueSources.Local | ValueSources.Default, PropertyVariationOption[] options = null)
+ public MockPropertyInfo (string name, string description = null, string category = null, bool canWrite = true, IEnumerable<Type> converterTypes = null, ValueSources valueSources = ValueSources.Local | ValueSources.Default, PropertyVariationOption[] options = null, bool isUncommon = false)
{
Name = name;
Description = description;
Category = category;
CanWrite = canWrite;
+ IsUncommon = isUncommon;
ValueSources = valueSources;
if (converterTypes != null) {
this.typeConverters = converterTypes
@@ -52,6 +53,7 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
public string Category { get; }
public bool CanWrite { get; }
+ public bool IsUncommon { get; }
public ValueSources ValueSources { get; }
static readonly PropertyVariationOption[] EmptyVariationOptions = new PropertyVariationOption[0];
diff --git a/Xamarin.PropertyEditing.Tests/OrderedDictionaryTests.cs b/Xamarin.PropertyEditing.Tests/OrderedDictionaryTests.cs
new file mode 100644
index 0000000..12726a9
--- /dev/null
+++ b/Xamarin.PropertyEditing.Tests/OrderedDictionaryTests.cs
@@ -0,0 +1,1351 @@
+//
+// OrderedDictionaryTest.cs
+//
+// Author:
+// Eric Maupin <me@ermau.com>
+//
+// Copyright (c) 2009 Eric Maupin (http://www.ermau.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+
+namespace Cadenza.Collections.Tests
+{
+ [TestFixture]
+ public class OrderedDictionaryTest
+ {
+ [Test]
+ public void Ctor_DictNull ()
+ {
+ Assert.Throws<ArgumentNullException> (() => {
+ Dictionary<string, string> foo = null;
+ new OrderedDictionary<string, string> (foo);
+ });
+ }
+
+ [Test]
+ public void Ctor_CapacityOutOfRange ()
+ {
+ Assert.Throws<ArgumentOutOfRangeException> (() => new OrderedDictionary<string, string> (-1));
+ }
+
+ [Test]
+ public void Ctor_CapacityOutOfRangeWithEquality ()
+ {
+ Assert.Throws<ArgumentOutOfRangeException> (() => new OrderedDictionary<string, string> (-1, null));
+ }
+
+ [Test]
+ public void KeyIndexer ()
+ {
+ var dict = new OrderedDictionary<string, string> { { "foo", "bar" }, { "baz", "monkeys" } };
+ Assert.AreEqual ("bar", dict["foo"]);
+ Assert.AreEqual ("monkeys", dict["baz"]);
+ }
+
+ [Test]
+ public void KeyIndexer_KeyNotFound ()
+ {
+ var dict = new OrderedDictionary<string, string>
+ { { "foo", "bar" }, { "baz", "monkeys" } };
+
+ Assert.Throws<KeyNotFoundException> (() => dict["wee"].ToString ());
+ }
+
+ [Test]
+ public void KeyIndexerSet ()
+ {
+ var dict = new OrderedDictionary<uint, int> ();
+ dict[(uint)1] = 1;
+ dict[(uint)2] = 2;
+ dict[(uint)3] = 3;
+ dict.Remove (2);
+ dict[(uint)4] = 4;
+
+ Assert.AreEqual (1, dict[(int)0]);
+ Assert.AreEqual (3, dict[(int)1]);
+ Assert.AreEqual (4, dict[(int)2]);
+ }
+
+ [Test]
+ public void KeyIndexerGet_KeyNull ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ Assert.Throws<ArgumentNullException> (() => dict[null].ToString ());
+ }
+
+ [Test]
+ public void KeyIndexerSet_KeyNull ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+
+ Assert.Throws<ArgumentNullException> (() => dict[null] = "foo");
+ }
+
+ [Test]
+ public void IndexIndexer ()
+ {
+ var dict = new OrderedDictionary<uint, int> ();
+ dict.Add (1, 1);
+ dict.Add (2, 2);
+ dict.Add (3, 3);
+ dict.Remove (2);
+ dict.Add (4, 4);
+
+ Assert.AreEqual (1, dict[(int)0]);
+ Assert.AreEqual (3, dict[(int)1]);
+ Assert.AreEqual (4, dict[(int)2]);
+ }
+
+ [Test]
+ public void IndexIndexerSet_New ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ dict.Add ("A", "B");
+ dict.Add ("C", "D");
+
+ var list = (IList<KeyValuePair<string, string>>)dict;
+ list[1] = new KeyValuePair<string, string> ("E", "F");
+
+ Assert.That (list.Count, Is.EqualTo (2));
+ Assert.That (dict["E"], Is.EqualTo ("F"));
+ Assert.That (dict.ContainsKey ("C"), Is.False);
+ }
+
+ [Test]
+ public void IndexIndexerSet_Existing ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ dict.Add ("A", "B");
+ dict.Add ("C", "D");
+
+ var list = (IList<KeyValuePair<string, string>>)dict;
+ list[1] = new KeyValuePair<string, string> ("C", "F");
+
+ Assert.That (list.Count, Is.EqualTo (2));
+ Assert.That (dict["C"], Is.EqualTo ("F"));
+ }
+
+ [Test]
+ [Description ("You can't duplicate a key by entering it via indexed set.")]
+ public void IndexIndexerSet_ReplicatedKey ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ dict.Add ("A", "B");
+ dict.Add ("C", "D");
+
+ var list = (IList<KeyValuePair<string, string>>) dict;
+ Assert.That (() => list[0] = new KeyValuePair<string, string> ("C", "E"), Throws.ArgumentException);
+ }
+
+ [Test]
+ public void Indexer_IndexOutOfRangeLower ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ Assert.Throws<ArgumentOutOfRangeException> (() => dict[-1].ToString ());
+ }
+
+ [Test]
+ public void Indexer_IndexOutOfRangeUpper ()
+ {
+ var dict = new OrderedDictionary<string, string>
+ { { "foo", "bar" }, { "baz", "monkeys" } };
+
+ Assert.Throws<ArgumentOutOfRangeException> (() => dict[2].ToString ());
+ }
+
+ [Test]
+ public void EnumerableOrder ()
+ {
+ var dict = new OrderedDictionary<uint, int> ();
+ dict.Add (1, 1);
+ dict.Add (2, 2);
+ dict.Add (3, 3);
+ dict.Remove (2);
+ dict.Add (4, 4);
+
+ using (var enumerator = dict.GetEnumerator ()) {
+ Assert.IsTrue (enumerator.MoveNext ());
+ Assert.AreEqual (1, enumerator.Current.Value);
+ Assert.IsTrue (enumerator.MoveNext ());
+ Assert.AreEqual (3, enumerator.Current.Value);
+ Assert.IsTrue (enumerator.MoveNext ());
+ Assert.AreEqual (4, enumerator.Current.Value);
+ }
+ }
+
+ [Test]
+ public void Values_EnumerableOrder ()
+ {
+ var dict = new OrderedDictionary<uint, int> ();
+ dict.Add (1, 1);
+ dict.Add (2, 2);
+ dict.Add (3, 3);
+ dict.Remove (2);
+ dict.Add (4, 4);
+
+ Assert.AreEqual (1, dict.Values.ElementAt (0));
+ Assert.AreEqual (3, dict.Values.ElementAt (1));
+ Assert.AreEqual (4, dict.Values.ElementAt (2));
+ }
+
+ [Test]
+ public void CopyTo ()
+ {
+ var dict = new OrderedDictionary<uint, int> ();
+ dict.Add (1, 1);
+ dict.Add (2, 2);
+ dict.Add (3, 3);
+ dict.Remove (2);
+ dict.Add (4, 4);
+
+ KeyValuePair<uint, int>[] a = new KeyValuePair<uint, int>[13];
+
+ ((ICollection<KeyValuePair<uint, int>>)dict).CopyTo (a, 10);
+
+ for (int i = 0; i < 10; ++i) {
+ if (i < 10)
+ Assert.AreEqual (default (KeyValuePair<uint, int>), a[i]);
+ }
+
+ Assert.AreEqual (1, a[10].Value);
+ Assert.AreEqual (3, a[11].Value);
+ Assert.AreEqual (4, a[12].Value);
+ }
+
+ [Test]
+ public void CopyTo_NullArray ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ KeyValuePair<string, string>[] a = null;
+
+ Assert.Throws<ArgumentNullException> (
+ () => ((ICollection<KeyValuePair<string, string>>) dict).CopyTo (a, 0));
+ }
+
+ [Test]
+ public void CopyTo_ArrayTooSmall ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ for (int i = 0; i < 1000; ++i)
+ dict.Add (i.ToString (), (i + 1).ToString ());
+
+ KeyValuePair<string, string>[] a = new KeyValuePair<string, string>[1];
+ Assert.Throws<ArgumentException> (() => ((ICollection<KeyValuePair<string, string>>) dict).CopyTo (a, 0));
+ }
+
+ [Test]
+ public void CopyTo_IndexOutOfRange ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+
+ Assert.Throws<ArgumentOutOfRangeException> (() =>
+ ((ICollection<KeyValuePair<string, string>>) dict).CopyTo (new KeyValuePair<string, string>[10], -1));
+ }
+
+ [Test]
+ public void Values_CopyTo ()
+ {
+ var dict = new OrderedDictionary<uint, int> ();
+ dict.Add (1, 1);
+ dict.Add (2, 2);
+ dict.Add (3, 3);
+ dict.Remove (2);
+ dict.Add (4, 4);
+
+ int[] a = new int[13];
+
+ dict.Values.CopyTo (a, 10);
+
+ for (int i = 0; i < 10; ++i) {
+ if (i < 10)
+ Assert.AreEqual (default (int), a[i]);
+ }
+
+ Assert.AreEqual (1, a[10]);
+ Assert.AreEqual (3, a[11]);
+ Assert.AreEqual (4, a[12]);
+ }
+
+ [Test]
+ public void ValuesCopyTo_NullArray ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ string[] a = null;
+
+ Assert.Throws<ArgumentNullException> (() => dict.Values.CopyTo (a, 0));
+ }
+
+ [Test]
+ public void ValuesCopyTo_ArrayTooSmall ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ for (int i = 0; i < 1000; ++i)
+ dict.Add (i.ToString (), (i + 1).ToString ());
+
+ Assert.Throws<ArgumentException> (() => dict.Values.CopyTo (new string[1], 0));
+ }
+
+ [Test]
+ public void ValuesCopyTo_IndexOutOfRange ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+
+ Assert.Throws<ArgumentOutOfRangeException> (() => dict.Values.CopyTo (new string[1], -1));
+ }
+
+ [Test]
+ public void IsReadOnly ()
+ {
+ Assert.IsFalse (((ICollection<KeyValuePair<int, int>>)new OrderedDictionary<int, int> ()).IsReadOnly);
+ }
+
+ [Test]
+ public void Values_IsReadOnly ()
+ {
+ Assert.IsTrue (new OrderedDictionary<int, int> ().Values.IsReadOnly);
+ }
+
+ [Test]
+ public void Clear ()
+ {
+ var dict = new OrderedDictionary<int, int> { { 1, 2 }, { 2, 3 }, { 3, 4 }, { 4, 5 } };
+
+ dict.Clear ();
+
+ Assert.AreEqual (0, dict.Count);
+ Assert.AreEqual (0, dict.Values.Count);
+ Assert.IsFalse (dict.ContainsKey (1));
+ Assert.IsFalse (dict.ContainsValue (2));
+ }
+
+ [Test]
+ public void Values_Clear ()
+ {
+ var dict = new OrderedDictionary<int, int> ();
+
+ Assert.Throws<NotSupportedException> (dict.Values.Clear);
+ }
+
+ [Test]
+ public void Add ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("1", 2);
+ dict.Add ("2", 3);
+
+ Assert.AreEqual (dict[0], 2);
+ Assert.AreEqual (dict[1], 3);
+ }
+
+ [Test]
+ public void Add_KeyNull ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ Assert.Throws<ArgumentNullException> (() => dict.Add (null, 1));
+ }
+
+ [Test]
+ public void Add_KeyExists ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+
+ Assert.Throws<ArgumentException> (() => dict.Add ("foo", 1));
+ }
+
+ [Test]
+ public void KVP_Add ()
+ {
+ var dict = new OrderedDictionary<uint, int> ();
+ ((ICollection<KeyValuePair<uint, int>>)dict).Add (new KeyValuePair<uint, int> (1, 1));
+ ((ICollection<KeyValuePair<uint, int>>)dict).Add (new KeyValuePair<uint, int> (2, 2));
+ ((ICollection<KeyValuePair<uint, int>>)dict).Add (new KeyValuePair<uint, int> (3, 3));
+ ((ICollection<KeyValuePair<uint, int>>)dict).Remove (new KeyValuePair<uint, int> (2, 2));
+ ((ICollection<KeyValuePair<uint, int>>)dict).Add (new KeyValuePair<uint, int> (4, 4));
+
+ Assert.AreEqual (3, dict.Count);
+ Assert.AreEqual (dict[0], 1);
+ Assert.AreEqual (dict[1], 3);
+ Assert.AreEqual (dict[2], 4);
+ }
+
+ [Test]
+ public void Values_Add ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ Assert.Throws<NotSupportedException> (() => dict.Values.Add (1));
+ }
+
+ [Test]
+ public void Insert ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("1", 2);
+ dict.Add ("3", 4);
+
+ dict.Insert (1, "2", 3);
+
+ Assert.AreEqual (dict[0], 2);
+ Assert.AreEqual (dict[1], 3);
+ Assert.AreEqual (dict[2], 4);
+ }
+
+ [Test]
+ public void Insert_KeyExists ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("1", 2);
+ dict.Add ("3", 4);
+
+ Assert.Throws<ArgumentException> (() => dict.Insert (1, "3", 3));
+ }
+
+ [Test]
+ public void KVP_Insert ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("1", 2);
+ dict.Add ("3", 4);
+
+ ((IList<KeyValuePair<string, int>>)dict).Insert (1,
+ new KeyValuePair<string, int> ("2", 3));
+
+ Assert.AreEqual (dict[0], 2);
+ Assert.AreEqual (dict[1], 3);
+ Assert.AreEqual (dict[2], 4);
+ }
+
+ [Test]
+ public void Values_Insert ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ Assert.Throws<NotSupportedException> (() => ((IList<int>) dict.Values).Insert (1, 1));
+ }
+
+ [Test]
+ public void Remove ()
+ {
+ var dict = new OrderedDictionary<string, int> { { "1", 2 }, { "2", 3 }, { "3", 4 } };
+
+ Assert.IsTrue (dict.Remove ("2"));
+ Assert.IsFalse (dict.ContainsKey ("2"));
+ Assert.IsFalse (dict.Values.Contains (3));
+ Assert.AreEqual (dict[1], 4);
+
+ Assert.IsFalse (dict.Remove ("2"));
+ }
+
+ [Test]
+ public void Remove_KeyNull ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ Assert.Throws<ArgumentNullException> (() => dict.Remove (null));
+ }
+
+ [Test]
+ public void KVP_Remove ()
+ {
+ var dict = new OrderedDictionary<string, string> ();
+ dict.Add ("foo", "bar");
+
+ var kvp = new KeyValuePair<string, string> ("foo", "bar");
+ Assert.IsTrue (((ICollection<KeyValuePair<string, string>>)dict).Remove (kvp));
+ Assert.AreEqual (0, dict.Count);
+ }
+
+ [Test]
+ public void Values_Remove ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ Assert.Throws<NotSupportedException> (() => dict.Values.Remove (1));
+ }
+
+ [Test]
+ public void ContainsKey ()
+ {
+ var dict = new OrderedDictionary<string, int> { { "1", 2 }, { "2", 3 }, { "3", 4 } };
+
+ Assert.IsFalse (dict.ContainsKey ("0"));
+ Assert.IsTrue (dict.ContainsKey ("1"));
+ Assert.IsTrue (dict.ContainsKey ("2"));
+ Assert.IsTrue (dict.ContainsKey ("3"));
+ Assert.IsFalse (dict.ContainsKey ("4"));
+ }
+
+ [Test]
+ public void ContainsKey_KeyNull ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ Assert.Throws<ArgumentNullException> (() => dict.ContainsKey (null));
+ }
+
+ [Test]
+ public void ContainsValue ()
+ {
+ var dict = new OrderedDictionary<string, int> { { "1", 2 }, { "2", 3 }, { "3", 4 } };
+
+ Assert.IsFalse (dict.ContainsValue (1));
+ Assert.IsTrue (dict.ContainsValue (2));
+ Assert.IsTrue (dict.ContainsValue (3));
+ Assert.IsTrue (dict.ContainsValue (4));
+ Assert.IsFalse (dict.ContainsValue (5));
+ }
+
+ [Test]
+ public void KVP_Contains ()
+ {
+ var dict = new OrderedDictionary<string, int> { { "1", 2 }, { "2", 3 }, { "3", 4 } };
+ var co = (ICollection<KeyValuePair<string, int>>)dict;
+
+ Assert.IsFalse (co.Contains (new KeyValuePair<string, int> ("0", 1)));
+ Assert.IsTrue (co.Contains (new KeyValuePair<string, int> ("1", 2)));
+ Assert.IsTrue (co.Contains (new KeyValuePair<string, int> ("2", 3)));
+ Assert.IsTrue (co.Contains (new KeyValuePair<string, int> ("3", 4)));
+ Assert.IsFalse (co.Contains (new KeyValuePair<string, int> ("4", 5)));
+ }
+
+ [Test]
+ public void Values_Contains ()
+ {
+ var dict = new OrderedDictionary<string, int> { { "1", 2 }, { "2", 3 }, { "3", 4 } };
+
+ Assert.IsFalse (dict.Values.Contains (1));
+ Assert.IsTrue (dict.Values.Contains (2));
+ Assert.IsTrue (dict.Values.Contains (3));
+ Assert.IsTrue (dict.Values.Contains (4));
+ Assert.IsFalse (dict.Values.Contains (5));
+ }
+
+ [Test]
+ public void Count ()
+ {
+ var dict = new OrderedDictionary<string, int> { { "1", 2 }, { "2", 3 }, { "3", 4 } };
+
+ Assert.AreEqual (3, dict.Count);
+
+ dict.Add ("4", 5);
+ dict.Add ("5", 6);
+
+ Assert.AreEqual (5, dict.Count);
+
+ dict.Clear ();
+
+ Assert.AreEqual (0, dict.Count);
+ }
+
+ [Test]
+ public void Values_Count ()
+ {
+ var dict = new OrderedDictionary<string, int> { { "1", 2 }, { "2", 3 }, { "3", 4 } };
+
+ Assert.AreEqual (3, dict.Values.Count);
+
+ dict.Add ("4", 5);
+ dict.Add ("5", 6);
+
+ Assert.AreEqual (5, dict.Values.Count);
+
+ dict.Clear ();
+
+ Assert.AreEqual (0, dict.Values.Count);
+ }
+
+ [Test]
+ public void TryGetValue_NullKey ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+
+ int i;
+ Assert.Throws<ArgumentNullException> (() => dict.TryGetValue (null, out i));
+ }
+
+ [Test]
+ public void TryGetValue ()
+ {
+ var dict = new OrderedDictionary<string, int> { { "1", 2 }, { "2", 3 }, { "3", 4 } };
+
+ int v;
+ Assert.IsTrue (dict.TryGetValue ("1", out v));
+ Assert.AreEqual (2, v);
+ }
+
+ [Test]
+ public void TryGetValue_NotFound ()
+ {
+ var dict = new OrderedDictionary<string, int> { { "1", 2 }, { "2", 3 }, { "3", 4 } };
+
+ int v;
+ Assert.IsFalse (dict.TryGetValue ("4", out v));
+ }
+
+ [Test]
+ public void RemoveAt ()
+ {
+ var dict = new OrderedDictionary<uint, int> ();
+ dict.Add (1, 1);
+ dict.Add (2, 2);
+ dict.Add (3, 3);
+ dict.Remove (2);
+ dict.Add (4, 4);
+
+ dict.RemoveAt (1);
+
+ Assert.AreEqual (1, dict[(int)0]);
+ Assert.AreEqual (4, dict[(int)1]);
+ }
+
+ [Test]
+ public void RemoveAt_IndexOutOfRangeLower ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+
+ Assert.Throws<ArgumentOutOfRangeException> (() => dict.RemoveAt (-1));
+ }
+
+ [Test]
+ public void Values_RemoveAt ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+
+ Assert.Throws<NotSupportedException> (() => ((IList<int>) dict.Values).RemoveAt (0));
+ }
+
+ [Test]
+ public void IndexOf ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+
+ Assert.AreEqual (1, dict.IndexOf ("bar"));
+ }
+
+ [Test]
+ public void IndexOf_NotFound ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+
+ Assert.AreEqual (-1, dict.IndexOf ("baz"));
+ }
+
+ [Test]
+ public void IndexOf_KeyNull ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+
+ Assert.Throws<ArgumentNullException> (() => dict.IndexOf (null));
+ }
+
+ [Test]
+ public void IndexOf_StartIndex ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.AreEqual (2, dict.IndexOf ("baz", 1));
+ Assert.AreEqual (2, dict.IndexOf ("baz", 2));
+ }
+
+ [Test]
+ public void IndexOf_StartIndex_NotFound ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.AreEqual (-1, dict.IndexOf ("asdf", 2));
+ Assert.AreEqual (-1, dict.IndexOf ("bar", 2));
+ }
+
+ [Test]
+ public void IndexOf_StartIndex_KeyNull ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.Throws<ArgumentNullException> (() => dict.IndexOf (null, 1));
+ }
+
+ [Test]
+ public void IndexOf_StartIndex_IndexOutOfRangeLower ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.Throws<ArgumentOutOfRangeException> (() => dict.IndexOf ("monkeys", -1));
+ }
+
+ [Test]
+ public void IndexOf_StartIndex_IndexOutOfRangeUpper ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.Throws<ArgumentOutOfRangeException> (() => dict.IndexOf ("monkeys", 5));
+ }
+
+ [Test]
+ public void IndexOf_StartIndexAndCount ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.AreEqual (1, dict.IndexOf ("bar", 1, 1));
+ Assert.AreEqual (2, dict.IndexOf ("baz", 0, 3));
+ }
+
+ [Test]
+ public void IndexOf_StartIndexAndCount_NotFound ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.AreEqual (-1, dict.IndexOf ("bar", 2, 1));
+ Assert.AreEqual (-1, dict.IndexOf ("baz", 0, 2));
+ }
+
+ [Test]
+ public void IndexOf_StartIndexAndCount_KeyNull ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.Throws<ArgumentNullException> (() => dict.IndexOf (null, 1, 2));
+ }
+
+ [Test]
+ public void IndexOf_StartIndexAndCount_IndexOutOfRangeLower ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.Throws<ArgumentOutOfRangeException> (() => dict.IndexOf ("monkeys", -1, 1));
+ }
+
+ [Test]
+ public void IndexOf_StartIndexAndCount_IndexOutOfRangeUpper ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.Throws<ArgumentOutOfRangeException> (() => dict.IndexOf ("monkeys", 5, 1));
+ }
+
+ [Test]
+ public void IndexOf_StartIndexAndCount_CountOutOfRange ()
+ {
+ var dict = new OrderedDictionary<string, int> ();
+ dict.Add ("foo", 0);
+ dict.Add ("bar", 1);
+ dict.Add ("baz", 2);
+ dict.Add ("monkeys", 3);
+
+ Assert.Throws<ArgumentOutOfRangeException> (() => dict.IndexOf ("monkeys", 2, 3));
+ }
+ }
+
+ [TestFixture]
+ public class OrderedDictionaryListContractTests : ListContract<KeyValuePair<string, string>>
+ {
+ protected override ICollection<KeyValuePair<string, string>> CreateCollection (IEnumerable<KeyValuePair<string, string>> values)
+ {
+ var d = new OrderedDictionary<string, string> ();
+ foreach (var v in values)
+ d.Add (v.Key, v.Value);
+ return d;
+ }
+
+ protected override KeyValuePair<string, string> CreateValueA ()
+ {
+ return new KeyValuePair<string, string> ("A", "1");
+ }
+
+ protected override KeyValuePair<string, string> CreateValueB ()
+ {
+ return new KeyValuePair<string, string> ("B", "2");
+ }
+
+ protected override KeyValuePair<string, string> CreateValueC ()
+ {
+ return new KeyValuePair<string, string> ("C", "3");
+ }
+ }
+
+ [TestFixture]
+ public class OrderedDictionaryDictionaryContractTests : DictionaryContract
+ {
+ protected override IDictionary<string, string> CreateDictionary (IEnumerable<KeyValuePair<string, string>> values)
+ {
+ var d = new OrderedDictionary<string, string> ();
+ foreach (var v in values)
+ d.Add (v.Key, v.Value);
+ return d;
+ }
+ }
+
+//
+// IEnumerableContract.cs
+//
+// Author:
+// Jonathan Pryor <jpryor@novell.com>
+//
+// Copyright (c) 2010 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+ public abstract class DictionaryContract
+ {
+
+ protected abstract IDictionary<string, string> CreateDictionary (IEnumerable<KeyValuePair<string, string>> values);
+
+ [Test]
+ public void Add ()
+ {
+ var d = CreateDictionary (new KeyValuePair<string, string>[0]);
+
+ var n = d.Count;
+ Assert.AreEqual (n, d.Keys.Count);
+ Assert.AreEqual (n, d.Values.Count);
+
+ // key cannot be null
+ try {
+ d.Add ("key", "value");
+ Assert.IsTrue (d.ContainsKey ("key"));
+ Assert.IsFalse (d.ContainsKey ("value"));
+ Assert.AreEqual (n + 1, d.Keys.Count);
+ Assert.AreEqual (n + 1, d.Values.Count);
+ Assert.IsTrue (d.Keys.Contains ("key"));
+ Assert.IsTrue (d.Values.Contains ("value"));
+
+ // Cannot use Add() w/ the same key
+ Assert.Throws<ArgumentException> (() => d.Add ("key", "value2"));
+
+ Assert.Throws<ArgumentNullException> (() => d.Add (null, null));
+ } catch (NotSupportedException) {
+ Assert.IsTrue (d.IsReadOnly);
+ }
+ }
+
+ [Test]
+ public void ContainsKey ()
+ {
+ var d = CreateDictionary (new KeyValuePair<string, string>[]{
+ new KeyValuePair<string, string> ("another-key", "another-value"),
+ });
+ Assert.Throws<ArgumentNullException> (() => d.ContainsKey (null));
+ Assert.IsFalse (d.ContainsKey ("key"));
+ Assert.IsTrue (d.ContainsKey ("another-key"));
+ Assert.IsTrue (d.Keys.Contains ("another-key"));
+ }
+
+ [Test]
+ public void Remove ()
+ {
+ var d = CreateDictionary (new KeyValuePair<string, string>[]{
+ new KeyValuePair<string, string> ("another-key", "another-value"),
+ });
+ var n = d.Count;
+ try {
+ Assert.IsFalse (d.Remove ("key"));
+ Assert.AreEqual (n, d.Count);
+ Assert.IsTrue (d.Remove ("another-key"));
+ Assert.AreEqual (n - 1, d.Count);
+ Assert.AreEqual (n - 1, d.Keys.Count);
+ Assert.AreEqual (n - 1, d.Values.Count);
+ Assert.IsFalse (d.Keys.Contains ("another-key"));
+ Assert.IsFalse (d.Values.Contains ("another-value"));
+
+ Assert.Throws<ArgumentNullException> (() => d.Remove (null));
+ } catch (NotSupportedException) {
+ Assert.IsTrue (d.IsReadOnly);
+ }
+ }
+
+ [Test]
+ public void TryGetValue ()
+ {
+ var d = CreateDictionary (new KeyValuePair<string, string>[]{
+ new KeyValuePair<string, string> ("key", "value"),
+ });
+ string v = null;
+ Assert.Throws<ArgumentNullException> (() => d.TryGetValue (null, out v));
+ Assert.IsFalse (d.TryGetValue ("another-key", out v));
+ Assert.IsTrue (d.TryGetValue ("key", out v));
+ Assert.AreEqual ("value", v);
+ }
+
+ [Test]
+ public void Item ()
+ {
+ var d = CreateDictionary (new KeyValuePair<string, string>[]{
+ new KeyValuePair<string, string> ("key", "value"),
+ });
+#pragma warning disable 0168
+ Assert.Throws<ArgumentNullException> (() => { var _ = d[null]; });
+ Assert.Throws<KeyNotFoundException> (() => { var _ = d["another-key"]; });
+#pragma warning restore
+ try {
+ d["key"] = "another-value";
+ Assert.IsFalse (d.Values.Contains ("value"));
+ Assert.IsTrue (d.Values.Contains ("another-value"));
+ Assert.AreEqual ("another-value", d["key"]);
+ Assert.AreEqual (1, d.Keys.Count);
+ Assert.AreEqual (1, d.Values.Count);
+ } catch (NotSupportedException) {
+ Assert.IsTrue (d.IsReadOnly);
+ }
+ }
+
+ [Test]
+ public void Keys_And_Values_Order_Must_Match ()
+ {
+ var d = CreateDictionary (new KeyValuePair<string, string>[]{
+ new KeyValuePair<string, string> ("a", "1"),
+ new KeyValuePair<string, string> ("b", "2"),
+ new KeyValuePair<string, string> ("c", "3"),
+ });
+ Assert.AreEqual (IndexOf (d.Keys, "a"), IndexOf (d.Values, "1"));
+ Assert.AreEqual (IndexOf (d.Keys, "b"), IndexOf (d.Values, "2"));
+ Assert.AreEqual (IndexOf (d.Keys, "c"), IndexOf (d.Values, "3"));
+ }
+
+ private int IndexOf<T> (IEnumerable<T> items, T search)
+ {
+ int i = 0;
+ foreach (T element in items) {
+ if (Equals (element, search))
+ return i;
+
+ i++;
+ }
+
+ return -1;
+ }
+ }
+
+ [TestFixture]
+ public class OrderedDictionaryKeysTests
+ : SubCollectionContract
+ {
+ protected override ICollection<string> CreateCollection (IEnumerable<string> values)
+ {
+ var d = new OrderedDictionary<string, string> ();
+ foreach (var v in values.Select (v => new KeyValuePair<string, string> (v, v)))
+ d.Add (v.Key, v.Value);
+
+ var c = d.Keys;
+ Assert.IsTrue (c.IsReadOnly);
+ return c;
+ }
+ }
+
+ [TestFixture]
+ public class OrderedDictionaryValuesTests
+ : SubCollectionContract
+ {
+ protected override ICollection<string> CreateCollection (IEnumerable<string> values)
+ {
+ var d = new OrderedDictionary<string, string> ();
+ foreach (var v in values.Select (v => new KeyValuePair<string, string> (v, v)))
+ d.Add (v.Key, v.Value);
+
+ var c = d.Values;
+ Assert.IsTrue (c.IsReadOnly);
+ return c;
+ }
+ }
+
+ public abstract class SubCollectionContract
+ : CollectionContract<string>
+ {
+ protected override string CreateValueA ()
+ {
+ return "A";
+ }
+
+ protected override string CreateValueB ()
+ {
+ return "B";
+ }
+
+ protected override string CreateValueC ()
+ {
+ return "C";
+ }
+ }
+
+//
+// IListContract.cs
+//
+// Author:
+// Jonathan Pryor <jpryor@novell.com>
+//
+// Copyright (c) 2010 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+
+ public abstract class ListContract<T> : CollectionContract<T>
+ {
+
+ private IList<T> CreateList (IEnumerable<T> values)
+ {
+ return (IList<T>)CreateCollection (values);
+ }
+
+ [Test]
+ public void IndexOf ()
+ {
+ var a = CreateValueA ();
+ var b = CreateValueB ();
+
+ var list = CreateList (new T[0]);
+
+ Assert.AreEqual (-1, list.IndexOf (a));
+
+ try {
+ list.Add (a);
+ Assert.AreEqual (0, list.IndexOf (a));
+
+ list.Add (b);
+ Assert.AreEqual (1, list.IndexOf (b));
+
+ list.Remove (a);
+ Assert.AreEqual (-1, list.IndexOf (a));
+ Assert.AreEqual (0, list.IndexOf (b));
+
+ list.Remove (b);
+ Assert.AreEqual (-1, list.IndexOf (b));
+ } catch (NotSupportedException) {
+ Assert.IsTrue (list.IsReadOnly);
+ }
+ }
+
+ [Test]
+ public void Insert ()
+ {
+ var a = CreateValueA ();
+ var b = CreateValueB ();
+
+ var list = CreateList (new T[0]);
+
+ try {
+ Assert.Throws<ArgumentOutOfRangeException> (() => list.Insert (-1, a));
+ Assert.Throws<ArgumentOutOfRangeException> (() => list.Insert (1, a));
+
+ list.Insert (0, a);
+ Assert.AreEqual (0, list.IndexOf (a));
+
+ list.Insert (0, b);
+ Assert.AreEqual (2, list.Count);
+ Assert.AreEqual (0, list.IndexOf (b));
+ Assert.AreEqual (1, list.IndexOf (a));
+ } catch (NotSupportedException) {
+ Assert.IsTrue (list.IsReadOnly);
+ }
+ }
+
+ [Test]
+ public void RemoveAt ()
+ {
+ var a = CreateValueA ();
+ var b = CreateValueB ();
+
+ var list = CreateList (new T[0]);
+
+ try {
+ Assert.Throws<ArgumentOutOfRangeException> (() => list.RemoveAt (-1));
+ Assert.Throws<ArgumentOutOfRangeException> (() => list.RemoveAt (0));
+
+ list.Add (a);
+ Assert.AreEqual (1, list.Count);
+
+ list.RemoveAt (0);
+ Assert.AreEqual (0, list.Count);
+
+ list.Add (a);
+ list.Add (b);
+ list.RemoveAt (0);
+ Assert.AreEqual (1, list.Count);
+ Assert.AreEqual (0, list.IndexOf (b));
+ } catch (NotSupportedException) {
+ Assert.IsTrue (list.IsReadOnly);
+ }
+ }
+
+ [Test]
+ public void Item ()
+ {
+ var a = CreateValueA ();
+ var b = CreateValueB ();
+
+ var list = CreateList (new[] { a });
+
+ Assert.AreEqual (a, list[0]);
+ Assert.Throws<ArgumentOutOfRangeException> (() => list[-1].ToString());
+
+ try {
+ Assert.Throws<ArgumentOutOfRangeException> (() => list[-1] = a);
+ Assert.Throws<ArgumentOutOfRangeException> (() => list[1] = a);
+
+ list[0] = b;
+ Assert.AreEqual (-1, list.IndexOf (a));
+ Assert.AreEqual (0, list.IndexOf (b));
+ } catch (NotSupportedException) {
+ Assert.IsTrue (list.IsReadOnly);
+ }
+ }
+ }
+
+//
+// IEnumerableContract.cs
+//
+// Author:
+// Jonathan Pryor <jpryor@novell.com>
+//
+// Copyright (c) 2010 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+ // NOTE: when adding new tests to this type, add them to the
+ // RunAllTests() method as well.
+ // RunAllTests() is used by IDictionaryContract<T>.Keys()/.Values()
+ // to test the behavior of the .Keys/.Values read-only collections.
+ //
+ // NOTE: No test may use [ExpectedException]; use Assert.Throws<T> instead.
+ public abstract class CollectionContract<T>
+ {
+
+ protected abstract ICollection<T> CreateCollection (IEnumerable<T> values);
+ protected abstract T CreateValueA ();
+ protected abstract T CreateValueB ();
+ protected abstract T CreateValueC ();
+
+
+ [Test]
+ public void Ctor_Initial_Count_Is_Zero ()
+ {
+ var c = CreateCollection (new T[0]);
+ Assert.AreEqual (0, c.Count);
+ }
+
+ [Test]
+ public void Ctor_CopySequence ()
+ {
+ var c = CreateCollection (new[] { CreateValueA (), CreateValueB (), CreateValueC () });
+ Assert.AreEqual (3, c.Count);
+ }
+
+ [Test]
+ public void Add ()
+ {
+ var c = CreateCollection (new T[0]);
+ var n = c.Count;
+ try {
+ c.Add (CreateValueA ());
+ Assert.AreEqual (n + 1, c.Count);
+ } catch (NotSupportedException) {
+ Assert.IsTrue (c.IsReadOnly);
+ }
+ }
+
+ [Test]
+ public void Clear ()
+ {
+ var c = CreateCollection (new[] { CreateValueA () });
+ try {
+ c.Clear ();
+ Assert.AreEqual (0, c.Count);
+ } catch (NotSupportedException) {
+ Assert.IsTrue (c.IsReadOnly);
+ }
+ }
+
+ [Test]
+ public void Contains ()
+ {
+ var a = CreateValueA ();
+ var b = CreateValueB ();
+
+ var c = CreateCollection (new[] { a, b });
+ Assert.IsTrue (c.Contains (a));
+ Assert.IsTrue (c.Contains (b));
+ Assert.IsFalse (c.Contains (CreateValueC ()));
+ }
+
+ [Test]
+ public void CopyTo_Exceptions ()
+ {
+ var c = CreateCollection (new[] { CreateValueA (), CreateValueB (), CreateValueC () });
+ Assert.Throws<ArgumentNullException> (() => c.CopyTo (null, 0));
+ Assert.Throws<ArgumentOutOfRangeException> (() => c.CopyTo (new T[3], -1));
+ var d = new T[5];
+ // not enough space from d[3..d.Length-1] to hold c.Count elements.
+ Assert.Throws<ArgumentException> (() => c.CopyTo (d, 3));
+ Assert.Throws<ArgumentException> (() => c.CopyTo (new T[0], 0));
+ }
+
+ // can fail for IDictionary<TKey,TValue> implementations; override if appropriate.
+ [Test]
+ public virtual void CopyTo_SequenceComparison ()
+ {
+ var a = CreateValueA ();
+ var b = CreateValueB ();
+ var c = CreateValueC ();
+
+ var coll = CreateCollection (new[] { a, b, c });
+ var d = new T[5];
+ coll.CopyTo (d, 1);
+ Assert.IsTrue (new[]{
+ default (T), a, b, c, default (T),
+ }.SequenceEqual (d));
+ }
+
+ [Test]
+ public void CopyTo ()
+ {
+ var a = CreateValueA ();
+ var b = CreateValueB ();
+ var c = CreateValueC ();
+
+ var coll = CreateCollection (new[] { a, b, c });
+ var d = new T[5];
+ coll.CopyTo (d, 1);
+ Assert.IsTrue (Array.IndexOf (d, a) >= 0);
+ Assert.IsTrue (Array.IndexOf (d, b) >= 0);
+ Assert.IsTrue (Array.IndexOf (d, c) >= 0);
+ }
+
+ [Test]
+ public void Remove ()
+ {
+ var a = CreateValueA ();
+ var b = CreateValueB ();
+ var c = CreateValueC ();
+
+ var coll = CreateCollection (new[] { a, b });
+ int n = coll.Count;
+ try {
+ Assert.IsFalse (coll.Remove (c));
+ Assert.AreEqual (n, coll.Count);
+ Assert.IsTrue (coll.Remove (a));
+ Assert.AreEqual (n - 1, coll.Count);
+ } catch (NotSupportedException) {
+ Assert.IsTrue (coll.IsReadOnly);
+ }
+ }
+ }
+}
diff --git a/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs b/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs
index 6d39f22..0626626 100644
--- a/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs
+++ b/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs
@@ -74,10 +74,10 @@ namespace Xamarin.PropertyEditing.Tests
vm.SelectedObjects.Add (obj);
Assume.That (vm.ArrangedEditors, Is.Not.Empty);
- Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2));
+ Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (2));
vm.FilterText = "sub";
- Assert.That (vm.ArrangedEditors[0].Count, Is.EqualTo (1));
+ Assert.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (1));
}
[Test]
@@ -93,7 +93,7 @@ namespace Xamarin.PropertyEditing.Tests
vm.SelectedObjects.Add (obj);
Assume.That (vm.ArrangedEditors, Is.Not.Empty);
- Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2));
+ Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (2));
Assume.That (vm.IsFiltering, Is.False);
bool changed = false;
@@ -104,7 +104,7 @@ namespace Xamarin.PropertyEditing.Tests
};
vm.FilterText = "sub";
- Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (1));
+ Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (1));
Assert.That (vm.IsFiltering, Is.True);
Assert.That (changed, Is.True);
changed = false;
@@ -128,13 +128,13 @@ namespace Xamarin.PropertyEditing.Tests
vm.SelectedObjects.Add (obj);
Assume.That (vm.ArrangedEditors, Is.Not.Empty);
- Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2));
+ Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (2));
vm.FilterText = "sub";
- Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (1));
+ Assume.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (1));
vm.FilterText = String.Empty;
- Assert.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2));
+ Assert.That (vm.ArrangedEditors[0].Editors.Count, Is.EqualTo (2));
}
[Test]
@@ -149,7 +149,7 @@ namespace Xamarin.PropertyEditing.Tests
vm.SelectedObjects.Add (obj);
Assume.That (vm.ArrangedEditors, Is.Not.Empty);
- Assert.That (vm.ArrangedEditors.FirstOrDefault (g => g.Key == "Sub"), Is.Not.Null);
+ Assert.That (vm.ArrangedEditors.FirstOrDefault (g => g.Category == "Sub"), Is.Not.Null);
}
[Test]
@@ -180,9 +180,9 @@ namespace Xamarin.PropertyEditing.Tests
vm.ArrangeMode = PropertyArrangeMode.Category;
vm.SelectedObjects.Add (editor.Target);
- Assert.That (vm.ArrangedEditors[0].Key, Is.EqualTo ("A"));
- Assert.That (vm.ArrangedEditors[1].Key, Is.EqualTo ("B"));
- Assert.That (vm.ArrangedEditors[2].Key, Is.EqualTo ("C"));
+ Assert.That (vm.ArrangedEditors[0].Category, Is.EqualTo ("A"));
+ Assert.That (vm.ArrangedEditors[1].Category, Is.EqualTo ("B"));
+ Assert.That (vm.ArrangedEditors[2].Category, Is.EqualTo ("C"));
}
[Test]
@@ -201,9 +201,9 @@ namespace Xamarin.PropertyEditing.Tests
vm.FilterText = "sub";
Assert.That (vm.ArrangedEditors.Count, Is.EqualTo (1));
- var group = vm.ArrangedEditors.FirstOrDefault (g => g.Key == "Sub");
+ var group = vm.ArrangedEditors.FirstOrDefault (g => g.Category == "Sub");
Assert.That (group, Is.Not.Null);
- Assert.That (group.Count, Is.EqualTo (1));
+ Assert.That (group.Editors.Count, Is.EqualTo (1));
}
[Test]
@@ -236,8 +236,8 @@ namespace Xamarin.PropertyEditing.Tests
vm.SelectedObjects.Add (target);
Assert.That (vm.ArrangedEditors.Count, Is.EqualTo (2));
- Assert.That (vm.ArrangedEditors[0].Key, Is.EqualTo ("ints"), "Grouped group not found or out of order");
- Assert.That (vm.ArrangedEditors[1].Key, Is.Null);
+ Assert.That (vm.ArrangedEditors[0].Category, Is.EqualTo ("ints"), "Grouped group not found or out of order");
+ Assert.That (vm.ArrangedEditors[1].Category, Is.Empty);
}
[Test]
@@ -272,7 +272,7 @@ namespace Xamarin.PropertyEditing.Tests
vm.FilterText = "name";
Assert.That (vm.ArrangedEditors.Count, Is.EqualTo (1));
- var group = vm.ArrangedEditors.FirstOrDefault (g => g.Key == "ints");
+ var group = vm.ArrangedEditors.FirstOrDefault (g => g.Category == "ints");
Assert.That (group, Is.Null);
}
@@ -332,7 +332,7 @@ namespace Xamarin.PropertyEditing.Tests
};
vm.SelectedObjects.Add (target);
- Assert.That (vm.ArrangedEditors.Any (g => g.Key == "ints"), Is.True, "Does not have grouped editors category");
+ Assert.That (vm.ArrangedEditors.Any (g => g.Category == "ints"), Is.True, "Does not have grouped editors category");
}
[Test]
@@ -350,7 +350,7 @@ namespace Xamarin.PropertyEditing.Tests
vm.SelectedObjects.Add (obj);
Assume.That (vm.ArrangedEditors, Is.Not.Empty);
- Assert.That (vm.GetIsExpanded (vm.ArrangedEditors[0].Key), Is.True);
+ Assert.That (vm.GetIsExpanded (vm.ArrangedEditors[0].Category), Is.True);
}
[Test]
@@ -383,7 +383,7 @@ namespace Xamarin.PropertyEditing.Tests
vm.SelectedObjects.Add (target);
Assume.That (vm.ArrangedEditors, Is.Not.Empty);
- Assume.That (vm.ArrangedEditors.Any (g => g.Key == "ints"), Is.True, "Does not have grouped editors category");
+ Assume.That (vm.ArrangedEditors.Any (g => g.Category == "ints"), Is.True, "Does not have grouped editors category");
Assert.That (vm.GetIsExpanded ("ints"), Is.True);
}
diff --git a/Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj b/Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj
index 61d14ad..85d1e3a 100644
--- a/Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj
+++ b/Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj
@@ -76,6 +76,7 @@
<Compile Include="MockValueConverter.cs" />
<Compile Include="NumericTests.cs" />
<Compile Include="NumericViewModelTests.cs" />
+ <Compile Include="OrderedDictionaryTests.cs" />
<Compile Include="ResourceSelectorViewModelTests.cs" />
<Compile Include="ResourceTests.cs" />
<Compile Include="SimpleCollectionViewTests.cs" />
diff --git a/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs b/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs
index 90ddb10..3fa7203 100644
--- a/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs
+++ b/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs
@@ -210,7 +210,7 @@ namespace Xamarin.PropertyEditing.Windows
Binding itemsSource;
if (newMode == PropertyArrangeMode.Name)
- itemsSource = new Binding ("ArrangedEditors[0]");
+ itemsSource = new Binding ("ArrangedEditors[0].Editors");
else
itemsSource = new Binding ("ArrangedEditors");
diff --git a/Xamarin.PropertyEditing.Windows/Themes/PropertyEditorPanelStyle.xaml b/Xamarin.PropertyEditing.Windows/Themes/PropertyEditorPanelStyle.xaml
index 29f7db0..b479b1d 100644
--- a/Xamarin.PropertyEditing.Windows/Themes/PropertyEditorPanelStyle.xaml
+++ b/Xamarin.PropertyEditing.Windows/Themes/PropertyEditorPanelStyle.xaml
@@ -72,15 +72,90 @@
</Setter>
</Style>
+ <Style TargetType="ToggleButton" x:Key="AdvancedPropertiesToggleButton">
+ <Setter Property="OverridesDefaultStyle" Value="True" />
+ <Setter Property="Height" Value="14" />
+ <Setter Property="Padding" Value="1.5,1,1.5,1" />
+ <Setter Property="VerticalAlignment" Value="Stretch" />
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="Foreground" Value="{DynamicResource ToggleItemForegroundBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <Border x:Name="ExpanderButtonBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{TemplateBinding Background}" DockPanel.Dock="Top">
+ <Path x:Name="Chevron" Data="M 0,3 L 5,8 L 10,3" Stroke="{TemplateBinding Foreground}" Width="10" Height="10" StrokeThickness="2"
+ HorizontalAlignment="Stretch" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5"/>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsChecked" Value="True">
+ <Setter Property="Data" TargetName="Chevron" Value="M 0,8 L 5,3 L 10,8"/>
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Stroke" Value="{DynamicResource ToggleItemMouseOverForegroundBrush}" TargetName="Chevron"/>
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Setter Property="Stroke" Value="{DynamicResource ToggleItemSelectedForegroundBrush}" TargetName="Chevron"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Style.Triggers>
+ <Trigger Property="IsMouseOver" Value="True">
+ <Setter Property="Background" Value="{DynamicResource AdvancedExpanderMouseOverBackgroundBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource AdvancedExpanderMouseOverBorderBrush}" />
+ <Setter Property="Foreground" Value="{DynamicResource AdvancedExpanderMouseOverForegroundBrush}" />
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+
+ <Style TargetType="Expander" x:Key="AdvancedPropertiesExpander">
+ <Setter Property="Foreground" Value="{DynamicResource PanelGroupSecondaryForegroundBrush}" />
+ <Setter Property="Background" Value="{DynamicResource PanelGroupSecondaryBackgroundBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="Expander">
+ <StackPanel Background="{TemplateBinding Background}">
+ <ToggleButton
+ x:Name="ExpanderButton" DockPanel.Dock="Top" Style="{DynamicResource AdvancedPropertiesToggleButton}" Content="{TemplateBinding Header}"
+ IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}">
+ </ToggleButton>
+ <ContentPresenter x:Name="ExpanderContent" Visibility="Collapsed" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
+ </StackPanel>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsExpanded" Value="True">
+ <Setter TargetName="ExpanderContent" Property="Visibility" Value="Visible"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
<DataTemplate x:Key="PropertyGroupTemplate">
- <local:CategoryExpander Header="{Binding Key,Mode=OneTime}" Style="{StaticResource TreeExpanderStyle}">
- <ItemsControl Style="{StaticResource PropertyListStyle}" ItemsSource="{Binding}">
- <ItemsControl.Template>
- <ControlTemplate>
- <ItemsPresenter />
- </ControlTemplate>
- </ItemsControl.Template>
- </ItemsControl>
+ <local:CategoryExpander Header="{Binding Category,Mode=OneTime}" Style="{StaticResource TreeExpanderStyle}">
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <ItemsControl Grid.Row="0" Style="{StaticResource PropertyListStyle}" ItemsSource="{Binding Editors,Mode=OneTime}">
+ <ItemsControl.Template>
+ <ControlTemplate>
+ <ItemsPresenter />
+ </ControlTemplate>
+ </ItemsControl.Template>
+ </ItemsControl>
+ <Expander Grid.Row="1" Visibility="{Binding HasUncommonElements,Converter={StaticResource BoolToVisibilityConverter}}" Style="{StaticResource AdvancedPropertiesExpander}">
+ <ItemsControl Grid.Row="0" Style="{StaticResource PropertyListStyle}" ItemsSource="{Binding UncommonEditors,Mode=OneTime}">
+ <ItemsControl.Template>
+ <ControlTemplate>
+ <ItemsPresenter />
+ </ControlTemplate>
+ </ItemsControl.Template>
+ </ItemsControl>
+ </Expander>
+ </Grid>
</local:CategoryExpander>
</DataTemplate>
diff --git a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
index 4ae48b6..9932fd2 100644
--- a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
+++ b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
@@ -702,7 +702,7 @@
</StackPanel>
</Border>
<Border Padding="1" Visibility="{Binding TargetPlatform.SupportsBrushOpacity, Mode=OneTime, Converter={StaticResource BoolToVisibilityConverter}}">
- <Expander Name="advancedPropertyPanel" Template="{DynamicResource AdvancedPropertiesExpander}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
+ <Expander Name="advancedPropertyPanel" Style="{DynamicResource AdvancedPropertiesExpander}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
Background="{DynamicResource PanelGroupSecondaryBackgroundBrush}" Foreground="{DynamicResource PanelForegroundBrush}">
<Expander.Content>
<Border Padding="19,6,6,6" Background="{DynamicResource PanelGroupSecondaryBackgroundBrush}">
@@ -1489,7 +1489,6 @@
</Style>
<Style TargetType="local:PropertyPresenter">
- <Setter Property="Background" Value="{DynamicResource PanelBackgroundBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:PropertyPresenter">
@@ -1541,39 +1540,6 @@
</Style.Triggers>
</Style>
- <ControlTemplate TargetType="Expander" x:Key="AdvancedPropertiesExpander">
- <StackPanel Background="{DynamicResource PanelGroupSecondaryBackgroundBrush}">
- <ToggleButton
- x:Name="ExpanderButton" DockPanel.Dock="Top" Template="{DynamicResource AdvancedPropertiesToggleButton}" Content="{TemplateBinding Header}" VerticalAlignment="Stretch"
- IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" OverridesDefaultStyle="True" Height="14" Padding="1.5,1,1.5,1">
- </ToggleButton>
- <ContentPresenter x:Name="ExpanderContent" Grid.Row="1" Visibility="Collapsed" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
- </StackPanel>
- <ControlTemplate.Triggers>
- <Trigger Property="IsExpanded" Value="True">
- <Setter TargetName="ExpanderContent" Property="Visibility" Value="Visible"/>
- </Trigger>
- </ControlTemplate.Triggers>
- </ControlTemplate>
-
- <ControlTemplate x:Key="AdvancedPropertiesToggleButton" TargetType="{x:Type ToggleButton}">
- <Border x:Name="ExpanderButtonBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{DynamicResource PanelGroupSecondaryBackgroundBrush}" DockPanel.Dock="Top">
- <Path x:Name="Chevron" Data="M 0,3 L 5,8 L 10,3" Stroke="{DynamicResource ToggleItemForegroundBrush}" Width="10" Height="10" StrokeThickness="2"
- HorizontalAlignment="Stretch" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5"/>
- </Border>
- <ControlTemplate.Triggers>
- <Trigger Property="IsChecked" Value="True">
- <Setter Property="Data" TargetName="Chevron" Value="M 0,8 L 5,3 L 10,8"/>
- </Trigger>
- <Trigger Property="IsMouseOver" Value="true">
- <Setter Property="Stroke" Value="{DynamicResource ToggleItemMouseOverForegroundBrush}" TargetName="Chevron"/>
- </Trigger>
- <Trigger Property="IsPressed" Value="true">
- <Setter Property="Stroke" Value="{DynamicResource ToggleItemSelectedForegroundBrush}" TargetName="Chevron"/>
- </Trigger>
- </ControlTemplate.Triggers>
- </ControlTemplate>
-
<Style TargetType="{x:Type TabItem}">
<Setter Property="Foreground" Value="{DynamicResource PanelForegroundBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
diff --git a/Xamarin.PropertyEditing/IPropertyInfo.cs b/Xamarin.PropertyEditing/IPropertyInfo.cs
index 2727ca0..26d1927 100644
--- a/Xamarin.PropertyEditing/IPropertyInfo.cs
+++ b/Xamarin.PropertyEditing/IPropertyInfo.cs
@@ -33,6 +33,14 @@ namespace Xamarin.PropertyEditing
bool CanWrite { get; }
/// <summary>
+ /// Gets whether the property is an uncommonly used property.
+ /// </summary>
+ /// <remarks>
+ /// This acts as a hint to hide the property behind disclosures when appropriate.
+ /// </remarks>
+ bool IsUncommon { get; }
+
+ /// <summary>
/// Gets the possible sources of values for this property.
/// </summary>
ValueSources ValueSources { get; }
diff --git a/Xamarin.PropertyEditing/OrderedDictionary.cs b/Xamarin.PropertyEditing/OrderedDictionary.cs
index 69df576..4b2ab91 100644
--- a/Xamarin.PropertyEditing/OrderedDictionary.cs
+++ b/Xamarin.PropertyEditing/OrderedDictionary.cs
@@ -30,11 +30,13 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using Xamarin.PropertyEditing;
namespace Cadenza.Collections
{
internal class OrderedDictionary<TKey, TValue>
- : IDictionary<TKey, TValue>, IList<KeyValuePair<TKey, TValue>>, IReadOnlyOrderedDictionary<TKey, TValue>
+ : IDictionary<TKey, TValue>, IList<KeyValuePair<TKey, TValue>>, IReadOnlyOrderedDictionary<TKey, TValue>, INotifyCollectionChanged
{
public OrderedDictionary ()
: this (0)
@@ -56,7 +58,7 @@ namespace Cadenza.Collections
this.dict = new Dictionary<TKey, TValue> (capacity, equalityComparer);
this.kvpCollection = this.dict;
this.keyOrder = new List<TKey> (capacity);
- this.roKeys = new ReadOnlyCollection<TKey> (this.keyOrder);
+ this.roKeys = new ReadOnlyKeysCollection (this.keyOrder);
this.roValues = new ReadOnlyValueCollection (this);
}
@@ -75,6 +77,8 @@ namespace Cadenza.Collections
Add (kvp.Key, kvp.Value);
}
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
{
get { return false; }
@@ -99,10 +103,24 @@ namespace Cadenza.Collections
get { return this.dict[key]; }
set
{
- if (!this.dict.ContainsKey (key))
+ int index = -1;
+ bool replace = false;
+ if (!this.dict.TryGetValue (key, out TValue oldValue))
this.keyOrder.Add (key);
+ else {
+ replace = true;
+ index = this.keyOrder.IndexOf (key);
+ }
this.dict[key] = value;
+
+ if (replace) {
+ this.roValues.OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Replace, oldValue, value, index));
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Replace,
+ new KeyValuePair<TKey, TValue> (key, oldValue), new KeyValuePair<TKey, TValue> (key, value)));
+ } else {
+ OnCollectionChanged (NotifyCollectionChangedAction.Add, this.keyOrder.Count - 1, key, value);
+ }
}
}
@@ -137,8 +155,22 @@ namespace Cadenza.Collections
get { return new KeyValuePair<TKey, TValue> (this.keyOrder[index], this[index]); }
set
{
- keyOrder[index] = value.Key;
+ TKey existingKey = this.keyOrder[index];
+ TValue existingValue = this.dict[existingKey];
+ if (!Equals (existingKey, value.Key)) {
+ if (this.dict.ContainsKey (value.Key))
+ throw new ArgumentException ("Existing keys can't be moved by setting them into another index", $"{nameof(value)}.{nameof(value.Key)}");
+
+ this.keyOrder[index] = value.Key;
+ this.dict.Remove (existingKey);
+ }
+
this.dict[value.Key] = value.Value;
+
+ this.roKeys.OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Replace, existingKey, value.Key, index));
+ this.roValues.OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Replace, existingValue, value.Value, index));
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Replace,
+ new KeyValuePair<TKey, TValue> (existingKey, existingValue), new KeyValuePair<TKey, TValue> (value.Key, value.Value)));
}
}
@@ -282,8 +314,11 @@ namespace Cadenza.Collections
/// <exception cref="ArgumentException"><paramref name="key"/> already exists in the dictionary.</exception>
public void Add (TKey key, TValue value)
{
+ int index = this.keyOrder.Count;
this.dict.Add (key, value);
this.keyOrder.Add (key);
+
+ OnCollectionChanged (NotifyCollectionChangedAction.Add, index, key, value);
}
void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> item)
@@ -301,8 +336,13 @@ namespace Cadenza.Collections
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0 or greater than <see cref="Count"/></exception>
public void Insert (int index, TKey key, TValue value)
{
- this.keyOrder.Insert (index, key);
+ if (index < 0 || index > this.keyOrder.Count)
+ throw new ArgumentOutOfRangeException (nameof(index));
+
this.dict.Add (key, value);
+ this.keyOrder.Insert (index, key);
+
+ OnCollectionChanged (NotifyCollectionChangedAction.Add, index, key, value);
}
void IList<KeyValuePair<TKey, TValue>>.Insert (int index, KeyValuePair<TKey, TValue> item)
@@ -318,22 +358,42 @@ namespace Cadenza.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <c>null</c>.</exception>
public bool Remove (TKey key)
{
- return (this.dict.Remove (key) && this.keyOrder.Remove (key));
+ if (key == null)
+ throw new ArgumentNullException (nameof(key));
+
+ int index = this.keyOrder.IndexOf (key);
+ if (index > -1) {
+ RemoveAt (index);
+ return true;
+ }
+
+ return false;
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> item)
{
- return (kvpCollection.Remove (item) && this.keyOrder.Remove (item.Key));
+ int index = this.keyOrder.IndexOf (item.Key);
+ if (index > -1) {
+ RemoveAt (index);
+ return true;
+ }
+
+ return false;
}
/// <summary>
/// Removes they key and associated value from the dictionary located at <paramref name="index"/>.
/// </summary>
/// <param name="index">The index at which to remove an item.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0 or greater than <see cref="Count"/></exception>
public void RemoveAt (int index)
{
TKey key = this.keyOrder[index];
- Remove (key);
+
+ this.keyOrder.RemoveAt (index);
+ this.dict.TryRemove (key, out var value);
+
+ OnCollectionChanged (NotifyCollectionChangedAction.Remove, index, key, value);
}
/// <summary>
@@ -343,6 +403,11 @@ namespace Cadenza.Collections
{
this.dict.Clear ();
this.keyOrder.Clear ();
+
+ var args = new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Reset);
+ this.roKeys.OnCollectionChanged (args);
+ this.roValues.OnCollectionChanged (args);
+ OnCollectionChanged (args);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int arrayIndex)
@@ -372,19 +437,49 @@ namespace Cadenza.Collections
}
private readonly ReadOnlyValueCollection roValues;
- private readonly ReadOnlyCollection<TKey> roKeys;
+ private readonly ReadOnlyKeysCollection roKeys;
private readonly ICollection<KeyValuePair<TKey, TValue>> kvpCollection;
private readonly Dictionary<TKey, TValue> dict;
private readonly List<TKey> keyOrder;
+ private void OnCollectionChanged (NotifyCollectionChangedAction action, int index, TKey key, TValue value)
+ {
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (action, new KeyValuePair<TKey, TValue> (key, value), index));
+ this.roKeys.OnCollectionChanged (new NotifyCollectionChangedEventArgs (action, key, index));
+ this.roValues.OnCollectionChanged (new NotifyCollectionChangedEventArgs (action, value, index));
+ }
+
+ private void OnCollectionChanged (NotifyCollectionChangedEventArgs e)
+ {
+ CollectionChanged?.Invoke (this, e);
+ }
+
+ private class ReadOnlyKeysCollection
+ : ReadOnlyCollection<TKey>, INotifyCollectionChanged
+ {
+ public ReadOnlyKeysCollection (IList<TKey> list)
+ : base (list)
+ {
+ }
+
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+ internal void OnCollectionChanged (NotifyCollectionChangedEventArgs e)
+ {
+ CollectionChanged?.Invoke (this, e);
+ }
+ }
+
private class ReadOnlyValueCollection
- : IList<TValue>
+ : IList<TValue>, IReadOnlyList<TValue>, INotifyCollectionChanged
{
public ReadOnlyValueCollection (OrderedDictionary<TKey, TValue> dict)
{
this.odict = dict;
}
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
public void Add (TValue item)
{
throw new NotSupportedException ();
@@ -462,6 +557,11 @@ namespace Cadenza.Collections
}
private readonly OrderedDictionary<TKey, TValue> odict;
+
+ internal void OnCollectionChanged (NotifyCollectionChangedEventArgs e)
+ {
+ CollectionChanged?.Invoke (this, e);
+ }
}
}
} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing/Reflection/ReflectionPropertyInfo.cs b/Xamarin.PropertyEditing/Reflection/ReflectionPropertyInfo.cs
index 9734dfe..005c649 100644
--- a/Xamarin.PropertyEditing/Reflection/ReflectionPropertyInfo.cs
+++ b/Xamarin.PropertyEditing/Reflection/ReflectionPropertyInfo.cs
@@ -47,6 +47,8 @@ namespace Xamarin.PropertyEditing.Reflection
public bool CanWrite => this.propertyInfo.CanWrite;
+ public bool IsUncommon => false;
+
public ValueSources ValueSources => ValueSources.Local;
public IReadOnlyList<PropertyVariationOption> Variations => EmtpyVariationOptions;
diff --git a/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs
index ce8ebd7..25ff232 100644
--- a/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs
@@ -1,11 +1,114 @@
-using System;
+using System;
using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading.Tasks;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Cadenza.Collections;
namespace Xamarin.PropertyEditing.ViewModels
{
+ internal class PanelGroupViewModel
+ : NotifyingObject
+ {
+ public PanelGroupViewModel (string category, IEnumerable<EditorViewModel> editors, bool separateUncommon = true)
+ {
+ if (editors == null)
+ throw new ArgumentNullException (nameof(editors));
+
+ Category = category;
+ AddCore (editors, separateUncommon);
+ }
+
+ public string Category
+ {
+ get;
+ }
+
+ public IReadOnlyList<EditorViewModel> Editors => this.editors;
+
+ public IReadOnlyList<EditorViewModel> UncommonEditors => this.uncommonEditors;
+
+ public bool HasChildElements => Editors.Count > 0 || HasUncommonElements;
+
+ public bool HasUncommonElements => UncommonEditors.Count > 0;
+
+ public bool UncommonShown
+ {
+ get;
+ set;
+ }
+
+ public void Add (IEnumerable<EditorViewModel> editors)
+ {
+ AddCore (editors, separate: true);
+ }
+
+ public void Add (EditorViewModel editor)
+ {
+ AddCore (editor, separate: true);
+ }
+
+ public bool Remove (EditorViewModel editor)
+ {
+ if (editor == null)
+ throw new ArgumentNullException (nameof(editor));
+
+ return GetList (editor, separate: true).Remove (editor);
+ }
+
+ public bool GetIsExpanded (PropertyArrangeMode mode)
+ {
+ if (this.isExpanded == null)
+ return false;
+
+ this.isExpanded.TryGetValue (mode, out bool expanded);
+ return expanded;
+ }
+
+ public void SetIsExpanded (PropertyArrangeMode mode, bool expanded)
+ {
+ if (this.isExpanded == null) {
+ if (!expanded)
+ return;
+
+ this.isExpanded = new Dictionary<PropertyArrangeMode, bool> ();
+ }
+
+ this.isExpanded[mode] = expanded;
+ }
+
+ private Dictionary<PropertyArrangeMode, bool> isExpanded;
+ private readonly ObservableCollectionEx<EditorViewModel> editors = new ObservableCollectionEx<EditorViewModel> ();
+ private readonly ObservableCollectionEx<EditorViewModel> uncommonEditors = new ObservableCollectionEx<EditorViewModel> ();
+
+ private void AddCore (IEnumerable<EditorViewModel> editors, bool separate)
+ {
+ if (editors == null)
+ throw new ArgumentNullException (nameof (editors));
+
+ foreach (EditorViewModel evm in editors)
+ AddCore (evm, separate);
+ }
+
+ private void AddCore (EditorViewModel editor, bool separate)
+ {
+ if (editor == null)
+ throw new ArgumentNullException (nameof (editor));
+
+ GetList (editor, separate).Add (editor);
+ OnPropertyChanged (nameof(HasChildElements));
+ OnPropertyChanged (nameof(HasUncommonElements));
+ }
+
+ private IList<EditorViewModel> GetList (EditorViewModel evm, bool separate)
+ {
+ if (separate && evm is PropertyViewModel pvm)
+ return pvm.Property.IsUncommon ? this.uncommonEditors : this.editors;
+ else
+ return this.editors;
+ }
+ }
+
internal class PanelViewModel
: PropertiesViewModel, IFilterable
{
@@ -20,8 +123,11 @@ namespace Xamarin.PropertyEditing.ViewModels
public event EventHandler ArrangedPropertiesChanged;
- public IReadOnlyList<IGroupingList<string, EditorViewModel>> ArrangedEditors => this.arranged;
+ public IReadOnlyList<PanelGroupViewModel> ArrangedEditors => (IReadOnlyList<PanelGroupViewModel>)this.arranged.Values;
+ /// <summary>
+ /// Gets or sets whether all categories should automatically expand.
+ /// </summary>
public bool AutoExpand
{
get { return this.autoExpand; }
@@ -78,11 +184,10 @@ namespace Xamarin.PropertyEditing.ViewModels
public bool GetIsExpanded (string group)
{
- HashSet<string> groups;
- if (!this.expandedGroups.TryGetValue (ArrangeMode, out groups))
+ if (group == null || !this.arranged.TryGetValue (group, out PanelGroupViewModel panelGroup))
return false;
- return groups.Contains (group);
+ return panelGroup.GetIsExpanded (ArrangeMode);
}
public void SetIsExpanded (string group, bool isExpanded)
@@ -108,6 +213,8 @@ namespace Xamarin.PropertyEditing.ViewModels
Dictionary<string, List<PropertyViewModel>> groupedTypeProperties = null;
+ bool isFlat = ArrangeMode == PropertyArrangeMode.Name;
+
this.arranged.Clear ();
foreach (var grouping in props.GroupBy (GetGroup).OrderBy (g => g.Key, CategoryComparer.Instance)) {
HashSet<EditorViewModel> remainingItems = null;
@@ -131,37 +238,37 @@ namespace Xamarin.PropertyEditing.ViewModels
}
}
- AutoExpandGroup (grouping.Key);
- if (remainingItems != null)
- this.arranged.Add (grouping.Key, remainingItems);
+ string key = grouping.Key ?? String.Empty;
+ if (remainingItems != null) // TODO: pretty sure this was out of order before, add test
+ this.arranged.Add (key, new PanelGroupViewModel (key, grouping.Where (evm => remainingItems.Contains (evm))));
else
- this.arranged.Add (grouping);
+ this.arranged.Add (key, new PanelGroupViewModel (key, grouping, separateUncommon: !isFlat));
+
+ AutoExpandGroup (key);
}
if (groupedTypeProperties != null) { // Insert type-grouped properties back in sorted.
int i = 0;
foreach (var kvp in groupedTypeProperties.OrderBy (kvp => kvp.Key, CategoryComparer.Instance)) {
- var group = new ObservableGrouping<string, EditorViewModel> (kvp.Key) {
- new PropertyGroupViewModel (TargetPlatform, kvp.Key, kvp.Value, ObjectEditors)
- };
-
- AutoExpandGroup (group.Key);
+ var group = new PanelGroupViewModel (kvp.Key, new[] { new PropertyGroupViewModel (TargetPlatform, kvp.Key, kvp.Value, ObjectEditors) });
bool added = false;
for (; i < this.arranged.Count; i++) {
- var g = (IGrouping<string, EditorViewModel>) this.arranged[i];
+ var g = this.arranged[i];
// TODO: Are we translating categories? If so this needs to lookup the resource and be culture specific
// nulls go on the bottom.
- if (g.Key == null || String.Compare (g.Key, kvp.Key, StringComparison.Ordinal) > 0) {
+ if (String.IsNullOrEmpty (g.Category) || String.Compare (g.Category, kvp.Key, StringComparison.Ordinal) > 0) {
added = true;
- this.arranged.Insert (i, group);
+ this.arranged.Insert (i, group.Category, group);
break;
}
}
if (!added)
- this.arranged.Add (group);
+ this.arranged.Add (group.Category, group);
+
+ AutoExpandGroup (group.Category);
}
}
@@ -172,10 +279,13 @@ namespace Xamarin.PropertyEditing.ViewModels
{
foreach (EditorViewModel vm in editors) {
string g = GetGroup (vm);
- var grouping = this.arranged[g] as ObservableGrouping<string, EditorViewModel>;
- if (grouping != null) {
- this.arranged.Remove (g, vm);
- }
+ PanelGroupViewModel group = this.arranged[g];
+ if (group == null)
+ continue;
+
+ group.Remove (vm);
+ if (!group.HasChildElements)
+ this.arranged.Remove (group.Category);
}
ArrangedPropertiesChanged?.Invoke (this, EventArgs.Empty);
@@ -188,10 +298,7 @@ namespace Xamarin.PropertyEditing.ViewModels
ArrangedPropertiesChanged?.Invoke (this, EventArgs.Empty);
}
- private readonly Dictionary<PropertyArrangeMode, HashSet<string>> expandedGroups = new Dictionary<PropertyArrangeMode, HashSet<string>> ();
- private readonly ObservableLookup<string, EditorViewModel> arranged = new ObservableLookup<string, EditorViewModel> {
- ReuseGroups = true
- };
+ private readonly OrderedDictionary<string, PanelGroupViewModel> arranged = new OrderedDictionary<string, PanelGroupViewModel> ();
private PropertyArrangeMode arrangeMode;
private string filterText;
@@ -199,38 +306,35 @@ namespace Xamarin.PropertyEditing.ViewModels
private void AutoExpandGroup (string group)
{
- if (AutoExpand || (group != null && TargetPlatform.AutoExpandGroups != null && TargetPlatform.AutoExpandGroups.Contains (group)))
- UpdateExpanded (new[] { group }, true);
+ if (group == null || !this.arranged.TryGetValue (group, out PanelGroupViewModel panelGroup))
+ return;
+ if (!AutoExpand && (TargetPlatform.AutoExpandGroups == null || !TargetPlatform.AutoExpandGroups.Contains (group)))
+ return;
+
+ UpdateExpanded (new[] { panelGroup }, true);
}
private void SetIsExpanded (PropertyArrangeMode mode, string group, bool isExpanded)
{
- if (!this.expandedGroups.TryGetValue (mode, out HashSet<string> groups)) {
- if (!isExpanded)
- return;
-
- this.expandedGroups[mode] = groups = new HashSet<string> ();
- }
+ if (!this.arranged.TryGetValue (group, out PanelGroupViewModel panelGroup) || mode == PropertyArrangeMode.Name)
+ return;
- if (isExpanded)
- groups.Add (group);
- else
- groups.Remove (group);
+ panelGroup.SetIsExpanded (mode, isExpanded);
}
private void UpdateExpanded (bool expanded)
{
- UpdateExpanded (this.arranged.Select<IGroupingList<string, EditorViewModel>, string> (g => g.Key), expanded);
+ UpdateExpanded (this.arranged.Values, expanded);
}
- private void UpdateExpanded (IEnumerable<string> groups, bool expanded)
+ private void UpdateExpanded (IEnumerable<PanelGroupViewModel> groups, bool expanded)
{
- foreach (string group in groups) {
+ foreach (PanelGroupViewModel group in groups) {
foreach (var mode in ArrangeModes) {
if (mode.ArrangeMode == PropertyArrangeMode.Name)
continue;
- SetIsExpanded (mode.ArrangeMode, group, expanded);
+ group.SetIsExpanded (mode.ArrangeMode, expanded);
}
}
}
@@ -248,8 +352,8 @@ namespace Xamarin.PropertyEditing.ViewModels
if (FilterText != null && (String.IsNullOrWhiteSpace (oldFilter) || FilterText.StartsWith (oldFilter, StringComparison.OrdinalIgnoreCase))) {
var toRemove = new List<EditorViewModel> ();
- foreach (var g in this.arranged) {
- foreach (var vm in g) {
+ foreach (PanelGroupViewModel g in this.arranged.Values) {
+ foreach (EditorViewModel vm in g.Editors.Concat (g.UncommonEditors)) {
if (!MatchesFilter (vm))
toRemove.Add (vm);
else if (vm is IFilterable) {
@@ -272,14 +376,14 @@ namespace Xamarin.PropertyEditing.ViewModels
private string GetGroup (EditorViewModel vm)
{
- return (ArrangeMode == PropertyArrangeMode.Name) ? "0" : vm.Category;
+ return (ArrangeMode == PropertyArrangeMode.Name) ? "0" : (vm.Category ?? String.Empty);
}
private bool MatchesFilter (EditorViewModel vm)
{
if (String.IsNullOrWhiteSpace (FilterText))
return true;
- if (ArrangeMode == PropertyArrangeMode.Category && vm.Category != null && vm.Category.Contains (FilterText, StringComparison.OrdinalIgnoreCase))
+ if (ArrangeMode == PropertyArrangeMode.Category && !String.IsNullOrEmpty (vm.Category) && vm.Category.Contains (FilterText, StringComparison.OrdinalIgnoreCase))
return true;
if (String.IsNullOrWhiteSpace (vm.Name))
return false;