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:
authorBertrand Le Roy <beleroy@microsoft.com>2017-11-21 03:45:16 +0300
committerBertrand Le Roy <beleroy@microsoft.com>2017-11-21 03:45:16 +0300
commit7c565d5af6303017437a521d11c90ac1583016c2 (patch)
tree5a8eec3495a7f09a6e65988b14014d8e90b02bcc
parentd271fb3b70d8cdc8ee1deb658af68833071289bd (diff)
parentc5590c312d9aef1dd6a8b3987f5849de8da41edd (diff)
Merge remote-tracking branch 'origin/master' into bleroy-BrushEditor
# Conflicts: # Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs # Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs # Xamarin.PropertyEditing.Windows/Themes/Resources.xaml # Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj # Xamarin.PropertyEditing/Properties/Resources.Designer.cs # Xamarin.PropertyEditing/Properties/Resources.resx # Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
-rw-r--r--.editorconfig95
-rw-r--r--Xamarin.PropertyEditing.Mac.Standalone/Xamarin.PropertyEditing.Mac.Standalone.csproj2
-rw-r--r--Xamarin.PropertyEditing.Mac/PropertyEditorPanel.cs2
-rw-r--r--Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs19
-rw-r--r--Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs18
-rw-r--r--Xamarin.PropertyEditing.Tests/IGetAndSet.cs8
-rw-r--r--Xamarin.PropertyEditing.Tests/IPropertyConverter.cs9
-rw-r--r--Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs67
-rw-r--r--Xamarin.PropertyEditing.Tests/MockControls/MockWpfControl.cs2
-rw-r--r--Xamarin.PropertyEditing.Tests/MockEditorProvider.cs16
-rw-r--r--Xamarin.PropertyEditing.Tests/MockObjectEditor.cs70
-rw-r--r--Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockEnumPropertyInfo.cs58
-rw-r--r--Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs97
-rw-r--r--Xamarin.PropertyEditing.Tests/MockPropertyProviderTests.cs36
-rw-r--r--Xamarin.PropertyEditing.Tests/ObservableLookupTests.cs71
-rw-r--r--Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs130
-rw-r--r--Xamarin.PropertyEditing.Tests/PropertyGroupViewModelTests.cs187
-rw-r--r--Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs127
-rw-r--r--Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj4
-rw-r--r--Xamarin.PropertyEditing.Windows.Standalone/MainWindow.xaml.cs21
-rw-r--r--Xamarin.PropertyEditing.Windows.Standalone/MockedSampleControlButton.cs26
-rw-r--r--Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs5
-rw-r--r--Xamarin.PropertyEditing.Windows/GroupEditorControl.cs123
-rw-r--r--Xamarin.PropertyEditing.Windows/HeaderedContextMenu.cs27
-rw-r--r--Xamarin.PropertyEditing.Windows/MenuButton.cs19
-rw-r--r--Xamarin.PropertyEditing.Windows/NumericUpDownControl.cs2
-rw-r--r--Xamarin.PropertyEditing.Windows/PropertyButton.cs135
-rw-r--r--Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs15
-rw-r--r--Xamarin.PropertyEditing.Windows/PropertyMenuItemContainerStyleSelector.cs36
-rw-r--r--Xamarin.PropertyEditing.Windows/Themes/Resources.xaml225
-rw-r--r--Xamarin.PropertyEditing.Windows/Themes/VS.Dark.xaml14
-rw-r--r--Xamarin.PropertyEditing.Windows/Themes/VS.Light.xaml8
-rw-r--r--Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj4
-rw-r--r--Xamarin.PropertyEditing/IGroupingList.cs5
-rw-r--r--Xamarin.PropertyEditing/ObservableLookup.cs32
-rw-r--r--Xamarin.PropertyEditing/OrderedDictionary.cs12
-rw-r--r--Xamarin.PropertyEditing/Properties/Resources.Designer.cs26
-rw-r--r--Xamarin.PropertyEditing/Properties/Resources.resx9
-rw-r--r--Xamarin.PropertyEditing/TargetPlatform.cs28
-rw-r--r--Xamarin.PropertyEditing/ValueSource.cs2
-rw-r--r--Xamarin.PropertyEditing/ViewModels/EditorViewModel.cs15
-rw-r--r--Xamarin.PropertyEditing/ViewModels/EventViewModel.cs4
-rw-r--r--Xamarin.PropertyEditing/ViewModels/IFilterable.cs9
-rw-r--r--Xamarin.PropertyEditing/ViewModels/ObjectViewModel.cs6
-rw-r--r--Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs114
-rw-r--r--Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs86
-rw-r--r--Xamarin.PropertyEditing/ViewModels/PropertyGroupViewModel.cs103
-rw-r--r--Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs34
-rw-r--r--Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj3
49 files changed, 1713 insertions, 453 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..6134c88
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,95 @@
+# EditorConfig is awesome:http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Baseline
+[*]
+charset = utf-8
+indent_style = tab
+trim_trailing_whitespace = true
+max_line_length = 120
+
+# MSBuild
+[*.{csproj,proj,projitems,shproj,fsproj,target,props}]
+indent_style = space
+indent_size = 2
+
+# XML config files
+[*.{config,nuspec,resx}]
+indent_style = space
+indent_size = 2
+
+# JSON files
+[*.json]
+indent_style = space
+indent_size = 2
+
+# XAML files
+[*.xaml]
+indent_style = tab
+indent_size = 4
+
+# Dotnet code style settings:
+[*.{cs,vb}]
+
+# Sort using and Import directives with System.* appearing first
+dotnet_sort_system_directives_first = true
+
+# Avoid "this." and "Me." if not necessary
+dotnet_style_qualification_for_field = true:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+
+# Use language keywords instead of framework type names for type references
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+# Suggest more modern language features when available
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+
+# CSharp code style settings:
+[*.cs]
+
+# spaces before parens
+csharp_space_between_method_declaration_name_and_open_parenthesis = true
+csharp_space_between_method_call_name_and_opening_parenthesis = true
+csharp_space_after_keywords_in_control_flow_statements = true
+
+# Newline settings
+csharp_new_line_before_open_brace = types,methods,properties,events,indexers
+csharp_new_line_before_else = false
+csharp_new_line_before_catch = false
+csharp_new_line_before_finally = false
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+
+# Switch indentation
+csharp_indent_switch_labels = false
+
+# Prefer "var" everywhere it's apparent
+csharp_style_var_for_built_in_types = true:suggestion
+csharp_style_var_when_type_is_apparent = true:suggestion
+csharp_style_var_elsewhere = false:suggestion
+
+# Prefer method-like constructs to have a block body
+csharp_style_expression_bodied_methods = false:none
+csharp_style_expression_bodied_constructors = false:none
+csharp_style_expression_bodied_operators = false:none
+
+# Prefer property-like constructs to have an expression-body
+csharp_style_expression_bodied_properties = true:none
+csharp_style_expression_bodied_indexers = true:none
+csharp_style_expression_bodied_accessors = true:none
+
+# Suggest more modern language features when available
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_throw_expression = false:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
diff --git a/Xamarin.PropertyEditing.Mac.Standalone/Xamarin.PropertyEditing.Mac.Standalone.csproj b/Xamarin.PropertyEditing.Mac.Standalone/Xamarin.PropertyEditing.Mac.Standalone.csproj
index 620721d..f835a47 100644
--- a/Xamarin.PropertyEditing.Mac.Standalone/Xamarin.PropertyEditing.Mac.Standalone.csproj
+++ b/Xamarin.PropertyEditing.Mac.Standalone/Xamarin.PropertyEditing.Mac.Standalone.csproj
@@ -26,7 +26,7 @@
<IncludeMonoRuntime>false</IncludeMonoRuntime>
<UseSGen>true</UseSGen>
<UseRefCounting>true</UseRefCounting>
- <Profiling>true</Profiling>
+ <Profiling>false</Profiling>
<HttpClientHandler></HttpClientHandler>
<LinkMode></LinkMode>
<XamMacArch></XamMacArch>
diff --git a/Xamarin.PropertyEditing.Mac/PropertyEditorPanel.cs b/Xamarin.PropertyEditing.Mac/PropertyEditorPanel.cs
index 7e6d331..c963108 100644
--- a/Xamarin.PropertyEditing.Mac/PropertyEditorPanel.cs
+++ b/Xamarin.PropertyEditing.Mac/PropertyEditorPanel.cs
@@ -67,7 +67,7 @@ namespace Xamarin.PropertyEditing.Mac
// Populate the Property Table
editorProvider = value;
- viewModel = new PanelViewModel (editorProvider);
+ viewModel = new PanelViewModel (editorProvider, TargetPlatform.Default);
dataSource = new PropertyTableDataSource (viewModel);
propertyTable.Delegate = new PropertyTableDelegate (dataSource);
propertyTable.DataSource = dataSource;
diff --git a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs
index f46220b..5153562 100644
--- a/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs
+++ b/Xamarin.PropertyEditing.Mac/PropertyTableDataSource.cs
@@ -22,28 +22,29 @@ namespace Xamarin.PropertyEditing.Mac
public override nint GetChildrenCount (NSOutlineView outlineView, NSObject item)
{
- if (this.vm.ArrangedProperties.Count == 0)
+ if (this.vm.ArrangedEditors.Count == 0)
return 0;
if (this.vm.ArrangeMode == PropertyArrangeMode.Name)
- return this.vm.ArrangedProperties[0].Count;
+ return this.vm.ArrangedEditors[0].Count;
if (item == null)
- return this.vm.ArrangedProperties.Count;
- else
- return ((IGroupingList<string, PropertyViewModel>)((NSObjectFacade)item).Target).Count;
+ return this.vm.ArrangedEditors.Count;
+ else {
+ return ((IGroupingList<string, EditorViewModel>)((NSObjectFacade)item).Target).Count;
+ }
}
public override NSObject GetChild (NSOutlineView outlineView, nint childIndex, NSObject item)
{
object element;
if (this.vm.ArrangeMode == PropertyArrangeMode.Name) {
- element = (this.vm.ArrangedProperties[0][(int)childIndex]);
+ element = (this.vm.ArrangedEditors[0][(int)childIndex]);
} else {
if (item == null)
- element = this.vm.ArrangedProperties[(int)childIndex];
+ element = this.vm.ArrangedEditors[(int)childIndex];
else {
- element = ((IGroupingList<string, PropertyViewModel>)((NSObjectFacade)item).Target)[(int)childIndex];
+ element = ((IGroupingList<string, EditorViewModel>)((NSObjectFacade)item).Target)[(int)childIndex];
}
}
@@ -55,7 +56,7 @@ namespace Xamarin.PropertyEditing.Mac
if (this.vm.ArrangeMode == PropertyArrangeMode.Name)
return false;
- return ((NSObjectFacade)item).Target is IGroupingList<string, PropertyViewModel>;
+ return ((NSObjectFacade)item).Target is IGroupingList<string, EditorViewModel>;
}
public NSObject GetFacade (object element)
diff --git a/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs b/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs
index 063a8c3..357209e 100644
--- a/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs
+++ b/Xamarin.PropertyEditing.Mac/PropertyTableDelegate.cs
@@ -24,7 +24,7 @@ namespace Xamarin.PropertyEditing.Mac
if (!String.IsNullOrWhiteSpace (this.dataSource.DataContext.FilterText)) {
outlineView.ExpandItem (null, true);
} else {
- foreach (IGrouping<string, PropertyViewModel> g in this.dataSource.DataContext.ArrangedProperties) {
+ foreach (IGrouping<string, EditorViewModel> g in this.dataSource.DataContext.ArrangedEditors) {
NSObject item;
if (!this.dataSource.TryGetFacade (g, out item))
continue;
@@ -43,7 +43,7 @@ namespace Xamarin.PropertyEditing.Mac
{
var facade = (NSObjectFacade)item;
var vm = facade.Target as PropertyViewModel;
- var group = facade.Target as IGroupingList<string, PropertyViewModel>;
+ var group = facade.Target as IGroupingList<string, EditorViewModel>;
string cellIdentifier = (group == null) ? vm.GetType ().Name : group.Key;
// Setup view based on the column
@@ -78,7 +78,7 @@ namespace Xamarin.PropertyEditing.Mac
throw new Exception ("Unknown column identifier: " + tableColumn.Identifier);
}
- PropertyEditorControl GetEditor (PropertyViewModel vm, NSOutlineView outlineView)
+ PropertyEditorControl GetEditor (EditorViewModel vm, NSOutlineView outlineView)
{
Type[] genericArgs = null;
Type controlType;
@@ -103,7 +103,7 @@ namespace Xamarin.PropertyEditing.Mac
public override bool ShouldSelectItem (NSOutlineView outlineView, NSObject item)
{
- return (!(item is NSObjectFacade) || !(((NSObjectFacade)item).Target is IGroupingList<string, PropertyViewModel>));
+ return (!(item is NSObjectFacade) || !(((NSObjectFacade)item).Target is IGroupingList<string, EditorViewModel>));
}
public override void ItemDidExpand (NSNotification notification)
@@ -112,7 +112,7 @@ namespace Xamarin.PropertyEditing.Mac
return;
NSObjectFacade facade = notification.UserInfo.Values[0] as NSObjectFacade;
- var group = facade.Target as IGroupingList<string, PropertyViewModel>;
+ var group = facade.Target as IGroupingList<string, EditorViewModel>;
if (group != null)
this.dataSource.DataContext.SetIsExpanded (group.Key, isExpanded: true);
}
@@ -123,7 +123,7 @@ namespace Xamarin.PropertyEditing.Mac
return;
NSObjectFacade facade = notification.UserInfo.Values[0] as NSObjectFacade;
- var group = facade.Target as IGroupingList<string, PropertyViewModel>;
+ var group = facade.Target as IGroupingList<string, EditorViewModel>;
if (group != null)
this.dataSource.DataContext.SetIsExpanded (group.Key, isExpanded: false);
}
@@ -131,12 +131,12 @@ namespace Xamarin.PropertyEditing.Mac
public override nfloat GetRowHeight (NSOutlineView outlineView, NSObject item)
{
var facade = (NSObjectFacade)item;
- var group = facade.Target as IGroupingList<string, PropertyViewModel>;
+ var group = facade.Target as IGroupingList<string, EditorViewModel>;
if (group != null) {
return 30;
}
- var vm = (PropertyViewModel)facade.Target;
+ var vm = (EditorViewModel)facade.Target;
var editor = (PropertyEditorControl)outlineView.MakeView (vm.GetType ().Name + "edits", this);
if (editor == null) {
editor = GetEditor (vm, outlineView);
@@ -148,7 +148,7 @@ namespace Xamarin.PropertyEditing.Mac
private bool isExpanding;
// set up the editor based on the type of view model
- private PropertyEditorControl SetUpEditor (Type controlType, PropertyViewModel property, NSOutlineView outline)
+ private PropertyEditorControl SetUpEditor (Type controlType, EditorViewModel property, NSOutlineView outline)
{
var view = (PropertyEditorControl)Activator.CreateInstance (controlType);
view.Identifier = property.GetType ().Name;
diff --git a/Xamarin.PropertyEditing.Tests/IGetAndSet.cs b/Xamarin.PropertyEditing.Tests/IGetAndSet.cs
deleted file mode 100644
index e334eb9..0000000
--- a/Xamarin.PropertyEditing.Tests/IGetAndSet.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Xamarin.PropertyEditing.Tests
-{
- interface IGetAndSet
- {
- TValue GetValue<TValue> (object target);
- void SetValue<TValue> (object target, TValue value);
- }
-}
diff --git a/Xamarin.PropertyEditing.Tests/IPropertyConverter.cs b/Xamarin.PropertyEditing.Tests/IPropertyConverter.cs
new file mode 100644
index 0000000..d83fcc7
--- /dev/null
+++ b/Xamarin.PropertyEditing.Tests/IPropertyConverter.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Xamarin.PropertyEditing.Tests
+{
+ interface IPropertyConverter
+ {
+ bool TryConvert<TFrom> (TFrom fromValue, Type toType, out object toValue);
+ }
+}
diff --git a/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs b/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs
index 27a587e..875a1f8 100644
--- a/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs
+++ b/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using Cadenza.Collections;
using Xamarin.PropertyEditing.Tests.MockPropertyInfo;
@@ -8,19 +7,11 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
{
public class MockControl
{
- private OrderedDictionary<string, IPropertyInfo> PropertyInfos { get; }
- = new OrderedDictionary<string, IPropertyInfo> { };
- private OrderedDictionary<string, IEventInfo> EventInfos { get; }
- = new OrderedDictionary<string, IEventInfo> { };
- internal IDictionary<IPropertyInfo, object> Values { get; }
- = new Dictionary<IPropertyInfo, object> { };
- internal IDictionary<IEventInfo, string> EventHandlers { get; }
- = new Dictionary<IEventInfo, string> { };
+ public IReadOnlyDictionary<string, IPropertyInfo> Properties => this.properties;
- public ICollection<IPropertyInfo> Properties => PropertyInfos.Values;
- public ICollection<IEventInfo> Events => EventInfos.Values;
+ public IReadOnlyDictionary<string, IEventInfo> Events => this.events;
- public IPropertyInfo AddProperty<T> (string name, string category = "",
+ public void AddProperty<T> (string name, string category = "",
bool canWrite = true, bool flag = false,
IEnumerable<Type> converterTypes = null)
{
@@ -30,33 +21,27 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
var enumPropertyInfoType = typeof (MockEnumPropertyInfo<,>)
.MakeGenericType (underlyingType, typeof (T));
propertyInfo = (IPropertyInfo)Activator.CreateInstance (enumPropertyInfoType, name, category, canWrite, flag, converterTypes);
- }
- else {
+ } else {
propertyInfo = new MockPropertyInfo<T> (name, category, canWrite, converterTypes);
}
- return AddProperty<T>(propertyInfo);
+
+ AddProperty<T> (propertyInfo);
}
- public IPropertyInfo AddProperty<T>(IPropertyInfo propertyInfo)
+ public void AddProperty<T> (IPropertyInfo propertyInfo)
{
- PropertyInfos.Add (propertyInfo.Name, propertyInfo);
- Values.Add (propertyInfo, new ValueInfo<T> {
- Value = default (T),
- Source = ValueSource.Local
- });
- return propertyInfo;
+ this.properties.Add (propertyInfo.Name, propertyInfo);
}
- public IPropertyInfo AddReadOnlyProperty<T> (string name, string category = "")
+ public void AddReadOnlyProperty<T> (string name, string category = null)
{
- return AddProperty<T> (name, category, false);
+ AddProperty<T> (name, category, false);
}
public void AddEvent (string name)
{
var eventInfo = new MockEventInfo (name);
- EventInfos.Add (name, eventInfo);
- EventHandlers.Add (eventInfo, "");
+ this.events.Add (name, eventInfo);
}
public void AddEvents (params string[] names)
@@ -66,33 +51,9 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
}
}
- public IPropertyInfo GetPropertyInfo (string name)
- => PropertyInfos[name];
-
- public T GetValue<T> (string name) => GetValue<T> (PropertyInfos[name]);
-
- public T GetValue<T> (IPropertyInfo info)
- {
- var infoObject = Values[info];
- var valueInfo = infoObject as ValueInfo<T>;
- if (valueInfo != null)
- return valueInfo.Value;
- return default(T);
- }
-
- public void SetValue<T> (string name, T value)
- {
- SetValue(PropertyInfos[name], value);
- }
-
- public void SetValue<T> (IPropertyInfo info, T value)
- {
- Values[info] = new ValueInfo<T> {
- Value = value,
- Source = ValueSource.Local
- };
- }
-
public class NotImplemented { }
+
+ private readonly OrderedDictionary<string, IEventInfo> events = new OrderedDictionary<string, IEventInfo> ();
+ private readonly OrderedDictionary<string, IPropertyInfo> properties = new OrderedDictionary<string, IPropertyInfo> ();
}
}
diff --git a/Xamarin.PropertyEditing.Tests/MockControls/MockWpfControl.cs b/Xamarin.PropertyEditing.Tests/MockControls/MockWpfControl.cs
index 504fecc..e9f9680 100644
--- a/Xamarin.PropertyEditing.Tests/MockControls/MockWpfControl.cs
+++ b/Xamarin.PropertyEditing.Tests/MockControls/MockWpfControl.cs
@@ -21,7 +21,7 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
AddProperty<NotImplemented> ("BindingGroup");
AddProperty<NotImplemented> ("BitmapEffect");
AddProperty<NotImplemented> ("BitmapEffectInput");
- AddProperty<NotImplemented> ("BorderBrush", Appearance);
+ AddProperty<CommonBrush> ("BorderBrush", Appearance);
AddProperty<CommonThickness> ("BorderThickness", Appearance);
AddProperty<NotImplemented> ("CacheMode");
AddProperty<NotImplemented> ("Clip");
diff --git a/Xamarin.PropertyEditing.Tests/MockEditorProvider.cs b/Xamarin.PropertyEditing.Tests/MockEditorProvider.cs
index 32bf1a0..e9b8de3 100644
--- a/Xamarin.PropertyEditing.Tests/MockEditorProvider.cs
+++ b/Xamarin.PropertyEditing.Tests/MockEditorProvider.cs
@@ -1,4 +1,4 @@
-using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.PropertyEditing.Reflection;
using Xamarin.PropertyEditing.Tests.MockControls;
@@ -10,10 +10,16 @@ namespace Xamarin.PropertyEditing.Tests
{
public Task<IObjectEditor> GetObjectEditorAsync (object item)
{
- var mockControl = item as MockControl;
- if (mockControl != null)
- return Task.FromResult<IObjectEditor> (new MockObjectEditor (mockControl));
- return Task.FromResult<IObjectEditor> (new ReflectionObjectEditor (item));
+ if (this.editorCache.TryGetValue (item, out IObjectEditor cachedEditor)) {
+ return Task.FromResult (cachedEditor);
+ }
+ IObjectEditor editor = (item is MockControl mockControl)
+ ? (IObjectEditor)(new MockObjectEditor (mockControl) { SupportsDefault = true })
+ : new ReflectionObjectEditor (item);
+ this.editorCache.Add (item, editor);
+ return Task.FromResult (editor);
}
+
+ private Dictionary<object, IObjectEditor> editorCache = new Dictionary<object, IObjectEditor> ();
}
} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing.Tests/MockObjectEditor.cs b/Xamarin.PropertyEditing.Tests/MockObjectEditor.cs
index 7e0030b..714f527 100644
--- a/Xamarin.PropertyEditing.Tests/MockObjectEditor.cs
+++ b/Xamarin.PropertyEditing.Tests/MockObjectEditor.cs
@@ -20,13 +20,17 @@ namespace Xamarin.PropertyEditing.Tests
public MockObjectEditor (MockControl control)
{
- Properties = control.Properties.ToArray();
- values = control.Values;
- Events = control.Events.ToArray ();
- events = control.EventHandlers;
+ Properties = control.Properties.Values.ToArray();
+ Events = control.Events.Values.ToArray();
Target = control;
}
+ public bool SupportsDefault
+ {
+ get;
+ set;
+ }
+
public object Target
{
get;
@@ -112,20 +116,21 @@ namespace Xamarin.PropertyEditing.Tests
value.Value = (T)ValueEvaluator (property, value.ValueDescriptor);
}
- var mockControl = Target as MockControl;
- if (mockControl != null) {
- var mockPropertyInfo = property as IGetAndSet;
- if (mockPropertyInfo != null) {
- mockPropertyInfo.SetValue (mockControl, value.Value);
- }
- else {
- values[property] = value;
+ object softValue = value;
+
+ if (typeof(T) != property.Type) {
+ IPropertyConverter converter = property as IPropertyConverter;
+
+ object v;
+ if (converter != null && converter.TryConvert (value.Value, property.Type, out v)) {
+ var softType = typeof(ValueInfo<>).MakeGenericType (property.Type);
+ softValue = Activator.CreateInstance (softType);
+ softType.GetProperty ("Value").SetValue (softValue, v);
+ softType.GetProperty ("Source").SetValue (softValue, value.Source);
}
}
- else {
- values[property] = value;
- }
-
+
+ this.values[property] = softValue;
PropertyChanged?.Invoke (this, new EditorPropertyChangedEventArgs (property));
}
@@ -134,32 +139,37 @@ namespace Xamarin.PropertyEditing.Tests
if (variation != null)
throw new NotSupportedException (); // TODO
- var mockControl = Target as MockControl;
- if (mockControl != null) {
- var mockPropertyInfo = property as IGetAndSet;
- if (mockPropertyInfo != null) {
- return new ValueInfo<T> {
- Value = mockPropertyInfo.GetValue<T> (mockControl),
- Source = ValueSource.Local
- };
- }
- }
-
object value;
- if (values.TryGetValue (property, out value)) {
+ if (this.values.TryGetValue (property, out value)) {
var info = value as ValueInfo<T>;
if (info != null)
return info;
- else if (value != null) {
+ else if (value == null || value is T) {
return new ValueInfo<T> {
Value = (T)value,
Source = ValueSource.Local
};
+ } else {
+ ValueSource source = ValueSource.Local;
+ Type valueType = value.GetType ();
+ if (valueType.IsConstructedGenericType && valueType.GetGenericTypeDefinition () == typeof(ValueInfo<>)) {
+ source = (ValueSource)valueType.GetProperty ("Source").GetValue (value);
+ value = valueType.GetProperty ("Value").GetValue (value);
+ }
+
+ object newValue;
+ IPropertyConverter converter = property as IPropertyConverter;
+ if (converter != null && converter.TryConvert (value, typeof(T), out newValue)) {
+ return new ValueInfo<T> {
+ Source = source,
+ Value = (T)newValue
+ };
+ }
}
}
return new ValueInfo<T> {
- Source = ValueSource.Local,
+ Source = (SupportsDefault) ? ValueSource.Default : ValueSource.Local,
Value = default(T)
};
}
diff --git a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockEnumPropertyInfo.cs b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockEnumPropertyInfo.cs
index 2ee05f5..053b915 100644
--- a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockEnumPropertyInfo.cs
+++ b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockEnumPropertyInfo.cs
@@ -44,19 +44,9 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
public IReadOnlyDictionary<string, TUnderlying> PredefinedValues { get; }
- private TUnderlying[] Values;
- private ulong[] LongValues;
-
- /// <summary>
- /// Gets the value or list of values of the described property on the target, as an `TUnderlying`
- /// or as a `IEnumerable` of `TUnderlying`.
- /// </summary>
- /// <typeparam name="TValue">The result type desired, should be `TUnderlying` or an `IEnumerable` of `TUnderlying`.</typeparam>
- /// <param name="target">The control from which to get the value.</param>
- /// <returns>The value or list of values.</returns>
- public override TValue GetValue<TValue> (MockControl target)
+ public override bool TryConvert<TFrom> (TFrom fromValue, Type toType, out object toValue)
{
- var enumerationType = typeof (TValue)
+ var enumerationType = toType
.GetInterfaces ()
.FirstOrDefault (i => i.IsGenericType && i.GetGenericTypeDefinition () == typeof (IEnumerable<>));
if (enumerationType != null) {
@@ -65,48 +55,38 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
var listOfEnumeratedType = typeof (List<>).MakeGenericType (enumeratedType);
// Build the list of values through a trip to uint64
unchecked {
- var realValue = Convert.ToUInt64 (target.GetValue<TEnum> (this));
+ var realValue = Convert.ToUInt64 (fromValue);
var values = (IList)Activator.CreateInstance (listOfEnumeratedType);
for (var i = 0; i < Values.Length; i++) {
if ((LongValues[i] & realValue) != 0)
values.Add (Convert.ChangeType(Values[i], enumeratedType));
}
- return (TValue)values;
+ toValue = values;
+ return true;
}
- }
- else {
- // Get a single value.
- var value = target.GetValue<TEnum> (this);
- return (TValue)Convert.ChangeType (value, typeof (TValue));
- }
- }
-
- /// <summary>
- /// Sets a value or a list of values for the represented property on the target control.
- /// </summary>
- /// <typeparam name="TValue">An `IEnumerable` of the values to set, or the value to set.</typeparam>
- /// <param name="target">The object on which the value must be set.</param>
- /// <param name="value">The value to set.</param>
- public override void SetValue<TValue> (MockControl target, TValue value)
- {
- var values = value as IEnumerable;
- if (values != null) {
+ } else if (fromValue is IEnumerable) {
if (!IsValueCombinable)
- throw new ArgumentException ("Can't set a combined value on a non-combinable type", nameof (value));
+ throw new ArgumentException ("Can't set a combined value on a non-combinable type", nameof(TFrom));
unchecked {
- var realValue = Convert.ToUInt64 (default (TUnderlying));
- foreach (var val in values) {
+ var realValue = Convert.ToUInt64 (default(TUnderlying));
+ foreach (var val in (IEnumerable) fromValue) {
realValue |= Convert.ToUInt64 (val);
}
- target.SetValue (this, (TEnum)Enum.ToObject (typeof (TEnum), realValue));
+ toValue = realValue;
+ return true;
}
+ } else if (toType.IsEnum) {
+ toValue = Enum.ToObject (toType, fromValue);
+ return true;
}
- else {
- target.SetValue (this, (TEnum)Enum.ToObject (typeof (TEnum), value));
- }
+
+ return base.TryConvert (fromValue, toType, out toValue);
}
+
+ private TUnderlying[] Values;
+ private ulong[] LongValues;
}
}
diff --git a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs
index a7a1799..dc44742 100644
--- a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs
+++ b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockPropertyInfo.cs
@@ -6,7 +6,7 @@ using Xamarin.PropertyEditing.Tests.MockControls;
namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
{
- public class MockPropertyInfo<T> : IPropertyInfo, IGetAndSet, IEquatable<MockPropertyInfo<T>>
+ public class MockPropertyInfo<T> : IPropertyInfo, IPropertyConverter, IEquatable<MockPropertyInfo<T>>
{
public MockPropertyInfo (string name, string category = "", bool canWrite = true, IEnumerable<Type> converterTypes = null)
{
@@ -14,7 +14,7 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
Category = category;
CanWrite = canWrite;
if (converterTypes != null) {
- TypeConverters = converterTypes
+ this.typeConverters = converterTypes
.Where (type => type != null && typeof (TypeConverter).IsAssignableFrom (type))
.Select (type => (TypeConverter)Activator.CreateInstance (type))
.ToArray();
@@ -25,83 +25,36 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
public virtual Type Type => typeof (T);
public string Category { get; }
public bool CanWrite { get; }
- public virtual ValueSources ValueSources => ValueSources.Local;
+ public virtual ValueSources ValueSources => ValueSources.Local | ValueSources.Default;
static readonly PropertyVariation[] EmptyVariations = new PropertyVariation[0];
public virtual IReadOnlyList<PropertyVariation> Variations => EmptyVariations;
static readonly IAvailabilityConstraint[] EmptyConstraints = new IAvailabilityConstraint[0];
public virtual IReadOnlyList<IAvailabilityConstraint> AvailabilityConstraints => EmptyConstraints;
- private IReadOnlyList<TypeConverter> TypeConverters;
-
- public TValue GetValue<TValue> (object target)
- {
- return GetValue<TValue> ((MockControl)target);
- }
-
- public void SetValue<TValue> (object target, TValue value)
- {
- SetValue ((MockControl)target, value);
- }
-
- public virtual TValue GetValue<TValue> (MockControl target)
- {
- object value = target.GetValue<T> (this);
- if (value is TValue)
- return (TValue)value;
- TValue converted;
- if (TryConvertToValue (value, out converted)) {
- return converted;
- }
- if (value == null)
- return default (TValue);
- return (TValue)(typeof (TValue) == typeof (string)
- ? value.ToString ()
- : Convert.ChangeType (value, typeof (TValue)));
- }
-
- public virtual void SetValue<TValue> (MockControl target, TValue value)
- {
- object realValue = value;
- object converted;
- if (TryConvertFromValue (value, out converted)) {
- realValue = converted;
- }
- else if (realValue != null && !typeof (T).IsInstanceOfType (value)) {
- realValue = Convert.ChangeType (value, typeof (T));
- }
-
- target.SetValue (this, (T)realValue);
- }
-
- private bool TryConvertToValue<TValue> (object value, out TValue converted)
+ public virtual bool TryConvert<TFrom> (TFrom fromValue, Type toType, out object toValue)
{
- converted = default (TValue);
-
- if (TypeConverters == null) return false;
- foreach (var converter in TypeConverters) {
- if (converter.CanConvertTo (typeof (TValue))) {
- converted = (TValue)converter.ConvertTo (value, typeof (TValue));
- return true;
+ toValue = null;
+ if (this.typeConverters != null) {
+ foreach (var converter in this.typeConverters) {
+ if (converter.CanConvertTo (toType)) {
+ toValue = converter.ConvertTo (fromValue, toType);
+ return true;
+ }
}
}
- return false;
- }
-
- private bool TryConvertFromValue<TValue> (TValue value, out object converted)
- {
- converted = null;
-
- if (TypeConverters == null)
- return false;
-
- foreach (var converter in TypeConverters) {
- if (converter.CanConvertFrom (typeof (TValue))) {
- converted = converter.ConvertFrom (value);
- return true;
- }
+ if (toType == typeof(string)) {
+ toValue = fromValue?.ToString ();
+ return true;
}
+ try {
+ toValue = Convert.ChangeType (fromValue, toType);
+ return true;
+ } catch {
+
+ }
+
return false;
}
@@ -133,11 +86,15 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo
{
var hashCode = 1861411795;
unchecked {
- hashCode = hashCode * -1521134295 + Name.GetHashCode ();
- hashCode = hashCode * -1521134295 + Category.GetHashCode ();
+ if (Name != null)
+ hashCode = hashCode * -1521134295 + Name.GetHashCode ();
+ if (Category != null)
+ hashCode = hashCode * -1521134295 + Category.GetHashCode ();
hashCode = hashCode * -1521134295 + CanWrite.GetHashCode ();
}
return hashCode;
}
+
+ private readonly IReadOnlyList<TypeConverter> typeConverters;
}
}
diff --git a/Xamarin.PropertyEditing.Tests/MockPropertyProviderTests.cs b/Xamarin.PropertyEditing.Tests/MockPropertyProviderTests.cs
index bb033e1..eee6d2b 100644
--- a/Xamarin.PropertyEditing.Tests/MockPropertyProviderTests.cs
+++ b/Xamarin.PropertyEditing.Tests/MockPropertyProviderTests.cs
@@ -9,6 +9,7 @@ using Xamarin.PropertyEditing.Tests.MockControls;
namespace Xamarin.PropertyEditing.Tests
{
+ /*
[TestFixture]
public class MockPropertyProviderTests
{
@@ -25,39 +26,7 @@ namespace Xamarin.PropertyEditing.Tests
Assert.That (propertyInfo.Name, Is.EqualTo (TestClass.PropertyName));
Assert.That (propertyInfo.Type, Is.EqualTo (typeof (string)));
}
-
- [Test]
- public async Task MockSetValue ()
- {
- var obj = new TestClass ();
-
- var provider = new MockEditorProvider ();
- IObjectEditor editor = await provider.GetObjectEditorAsync (obj);
-
- const string value = "value";
-
- var propertyInfo = editor.Properties.Single ();
- await editor.SetValueAsync (propertyInfo, new ValueInfo<string> {
- Value = value
- });
-
- Assert.That (obj.GetValue<string>(propertyInfo), Is.EqualTo (value));
- }
-
- [Test]
- public async Task MockGetValue ()
- {
- const string value = "value";
- var obj = new TestClass (value);
-
- var provider = new MockEditorProvider ();
- IObjectEditor editor = await provider.GetObjectEditorAsync (obj);
-
- ValueInfo<string> info = await editor.GetValueAsync<string> (editor.Properties.Single ());
- Assert.That (info.Value, Is.EqualTo (value));
- Assert.That (info.Source, Is.EqualTo (ValueSource.Local));
- }
-
+
[Test]
public async Task MockSetValueConvert ()
{
@@ -243,4 +212,5 @@ namespace Xamarin.PropertyEditing.Tests
}
}
}
+ */
}
diff --git a/Xamarin.PropertyEditing.Tests/ObservableLookupTests.cs b/Xamarin.PropertyEditing.Tests/ObservableLookupTests.cs
new file mode 100644
index 0000000..f88b9b5
--- /dev/null
+++ b/Xamarin.PropertyEditing.Tests/ObservableLookupTests.cs
@@ -0,0 +1,71 @@
+using System.Collections.Specialized;
+using System.Linq;
+using NUnit.Framework;
+
+namespace Xamarin.PropertyEditing.Tests
+{
+ [TestFixture]
+ internal class ObservableLookupTests
+ {
+ [TestCase ("key")]
+ [TestCase (null)]
+ public void RemoveGroup (string key)
+ {
+ const string value = "value";
+ var lookup = new ObservableLookup<string, string> ();
+ lookup.Add (key, value);
+ Assume.That (lookup.Contains (key), Is.True);
+ Assume.That (lookup[key], Contains.Item (value));
+
+ bool groupRemoved = false;
+ lookup.CollectionChanged += (sender, args) => {
+ if (args.Action == NotifyCollectionChangedAction.Remove) {
+ var g = args.OldItems[0] as IGrouping<string, string>;
+ if (g != null && g.Key == key)
+ groupRemoved = true;
+ }
+ };
+
+ var grouping = lookup[key];
+ lookup.Remove (key);
+
+ Assert.That (groupRemoved, Is.True);
+ Assert.That (lookup, Does.Not.Contain (grouping));
+ }
+
+ [TestCase ("key")]
+ [TestCase (null)] // The reality is that null as a key receives special treatment
+ public void RemoveLastItemInGroup (string key)
+ {
+ const string value = "value";
+ var lookup = new ObservableLookup<string, string> ();
+ lookup.Add (key, value);
+ Assume.That (lookup.Contains (key), Is.True);
+ Assume.That (lookup[key], Contains.Item (value));
+
+ bool itemRemoved = false, groupRemoved = false;
+ lookup.CollectionChanged += (sender, args) => {
+ if (args.Action == NotifyCollectionChangedAction.Remove) {
+ var g = args.OldItems[0] as IGrouping<string, string>;
+ if (g != null && g.Key == key)
+ groupRemoved = true;
+ }
+ };
+
+ var grouping = lookup[key];
+ ((INotifyCollectionChanged) grouping).CollectionChanged += (sender, args) => {
+ if (args.Action == NotifyCollectionChangedAction.Remove) {
+ if (args.OldItems[0] == value)
+ itemRemoved = true;
+ }
+ };
+
+ lookup.Remove (key, value);
+
+ Assert.That (itemRemoved, Is.True);
+ Assert.That (groupRemoved, Is.True);
+ Assert.That (grouping, Does.Not.Contains (value));
+ Assert.That (lookup, Does.Not.Contain (grouping));
+ }
+ }
+}
diff --git a/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs b/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs
index 4d537b3..69299f6 100644
--- a/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs
+++ b/Xamarin.PropertyEditing.Tests/PanelViewModelTests.cs
@@ -1,4 +1,5 @@
-using System;
+using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
@@ -30,11 +31,11 @@ namespace Xamarin.PropertyEditing.Tests
var editor = await provider.GetObjectEditorAsync (obj);
Assume.That (editor.Properties.Count, Is.EqualTo (1));
- var vm = new PanelViewModel (provider);
+ var vm = new PanelViewModel (provider, TargetPlatform.Default);
vm.SelectedObjects.Add (obj);
Assert.That (vm.Properties, Is.Not.Empty);
- Assert.That (vm.Properties[0].Property, Is.EqualTo (editor.Properties.Single ()));
+ Assert.That (((PropertyViewModel)vm.Properties[0]).Property, Is.EqualTo (editor.Properties.Single ()));
}
[Test]
@@ -58,16 +59,16 @@ namespace Xamarin.PropertyEditing.Tests
providerMock.Setup (ep => ep.GetObjectEditorAsync (obj1)).ReturnsAsync (editor1Mock.Object);
providerMock.Setup (ep => ep.GetObjectEditorAsync (obj2)).ReturnsAsync (editor2Mock.Object);
- var vm = new PanelViewModel (providerMock.Object);
+ var vm = new PanelViewModel (providerMock.Object, TargetPlatform.Default);
vm.SelectedObjects.Add (obj1);
Assume.That (vm.Properties.Count, Is.EqualTo (1));
- Assume.That (vm.Properties[0].Property, Is.EqualTo (sharedPropertyMock.Object));
+ Assume.That (((PropertyViewModel)vm.Properties[0]).Property, Is.EqualTo (sharedPropertyMock.Object));
// Reflection property info equate actually fails on the same property across class/subclass
vm.SelectedObjects.Add (obj2);
Assert.That (vm.Properties.Count, Is.EqualTo (1));
- Assert.That (vm.Properties.Single ().Property, Is.EqualTo (sharedPropertyMock.Object));
+ Assert.That (((PropertyViewModel)vm.Properties.Single()).Property, Is.EqualTo (sharedPropertyMock.Object));
}
[Test]
@@ -91,17 +92,17 @@ namespace Xamarin.PropertyEditing.Tests
providerMock.Setup (ep => ep.GetObjectEditorAsync (obj1)).ReturnsAsync (editor1Mock.Object);
providerMock.Setup (ep => ep.GetObjectEditorAsync (obj2)).ReturnsAsync (editor2Mock.Object);
- var vm = new PanelViewModel (providerMock.Object);
+ var vm = new PanelViewModel (providerMock.Object, TargetPlatform.Default);
vm.SelectedObjects.Add (obj2);
Assume.That (vm.Properties.Count, Is.EqualTo (2));
- Assume.That (vm.Properties.Select (v => v.Property), Contains.Item (sharedPropertyMock.Object));
- Assume.That (vm.Properties.Select (v => v.Property), Contains.Item (subPropertyMock.Object));
+ Assume.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (sharedPropertyMock.Object));
+ Assume.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (subPropertyMock.Object));
// Reflection property info equate actually fails on the same property across class/subclass
vm.SelectedObjects.Add (obj1);
Assert.That (vm.Properties.Count, Is.EqualTo (1));
- Assert.That (vm.Properties.Select (v => v.Property), Contains.Item (sharedPropertyMock.Object));
+ Assert.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (sharedPropertyMock.Object));
}
[Test]
@@ -113,7 +114,7 @@ namespace Xamarin.PropertyEditing.Tests
var obj1 = new TestClass ();
var obj2 = new TestClass ();
- var vm = new PanelViewModel (provider);
+ var vm = new PanelViewModel (provider, TargetPlatform.Default);
vm.SelectedObjects.Add (obj1);
var property = vm.Properties[0];
@@ -136,7 +137,7 @@ namespace Xamarin.PropertyEditing.Tests
var obj1 = new TestClass ();
var obj2 = new TestClass ();
- var vm = new PanelViewModel (provider);
+ var vm = new PanelViewModel (provider, TargetPlatform.Default);
vm.SelectedObjects.Add (obj1);
vm.SelectedObjects.Add (obj2);
@@ -168,16 +169,16 @@ namespace Xamarin.PropertyEditing.Tests
var provider = new Mock<IEditorProvider> ();
provider.Setup (ep => ep.GetObjectEditorAsync (obj)).ReturnsAsync (editorMock.Object);
- var vm = new PanelViewModel (provider.Object);
+ var vm = new PanelViewModel (provider.Object, TargetPlatform.Default);
vm.SelectedObjects.Add (obj);
Assume.That (vm.Properties.Count, Is.EqualTo (2));
- Assume.That (vm.Properties.Select (v => v.Property), Contains.Item (mockProperty1.Object));
- Assume.That (vm.Properties.Select (v => v.Property), Contains.Item (mockProperty2.Object));
+ Assume.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (mockProperty1.Object));
+ Assume.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (mockProperty2.Object));
properties.Remove (mockProperty2.Object);
Assert.That (vm.Properties.Count, Is.EqualTo (1));
- Assert.That (vm.Properties.Select (v => v.Property), Contains.Item (mockProperty1.Object));
+ Assert.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (mockProperty1.Object));
}
[Test]
@@ -198,17 +199,17 @@ namespace Xamarin.PropertyEditing.Tests
var provider = new Mock<IEditorProvider> ();
provider.Setup (ep => ep.GetObjectEditorAsync (obj)).ReturnsAsync (editorMock.Object);
- var vm = new PanelViewModel (provider.Object);
+ var vm = new PanelViewModel (provider.Object, TargetPlatform.Default);
vm.SelectedObjects.Add (obj);
Assume.That (vm.Properties.Count, Is.EqualTo (1));
- Assume.That (vm.Properties.Select (v => v.Property), Contains.Item (mockProperty1.Object));
+ Assume.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (mockProperty1.Object));
properties.Add (mockProperty2.Object);
Assert.That (vm.Properties.Count, Is.EqualTo (2));
- Assert.That (vm.Properties.Select (v => v.Property), Contains.Item (mockProperty1.Object));
- Assert.That (vm.Properties.Select (v => v.Property), Contains.Item (mockProperty2.Object));
+ Assert.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (mockProperty1.Object));
+ Assert.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (mockProperty2.Object));
}
[Test]
@@ -229,7 +230,7 @@ namespace Xamarin.PropertyEditing.Tests
var provider = new Mock<IEditorProvider> ();
provider.Setup (ep => ep.GetObjectEditorAsync (obj)).ReturnsAsync (editorMock.Object);
- var vm = new PanelViewModel (provider.Object);
+ var vm = new PanelViewModel (provider.Object, TargetPlatform.Default);
// We need access to the custom reset method here to ensure compliance
// It's a bit hacky but this is unlikely to change. If it does, this test
@@ -238,13 +239,13 @@ namespace Xamarin.PropertyEditing.Tests
((ObservableCollectionEx<object>)vm.SelectedObjects).Reset (new[] { obj });
Assume.That (vm.Properties.Count, Is.EqualTo (1));
- Assume.That (vm.Properties.Select (v => v.Property), Contains.Item (mockProperty1.Object));
+ Assume.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (mockProperty1.Object));
properties.Add (mockProperty2.Object);
Assert.That (vm.Properties.Count, Is.EqualTo (2));
- Assert.That (vm.Properties.Select (v => v.Property), Contains.Item (mockProperty1.Object));
- Assert.That (vm.Properties.Select (v => v.Property), Contains.Item (mockProperty2.Object));
+ Assert.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (mockProperty1.Object));
+ Assert.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (mockProperty2.Object));
}
[Test]
@@ -271,11 +272,11 @@ namespace Xamarin.PropertyEditing.Tests
providerMock.Setup (ep => ep.GetObjectEditorAsync (baseObj)).ReturnsAsync (baseEditorMock.Object);
providerMock.Setup (ep => ep.GetObjectEditorAsync (derivedObj)).ReturnsAsync (derivedEditorMock.Object);
- var vm = new PanelViewModel (providerMock.Object);
+ var vm = new PanelViewModel (providerMock.Object, TargetPlatform.Default);
vm.SelectedObjects.AddItems (new[] { baseObj, derivedObj });
Assume.That (vm.Properties.Count, Is.EqualTo (1));
- Assume.That (vm.Properties.Select (v => v.Property), Contains.Item (baseProperty.Object));
+ Assume.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (baseProperty.Object));
derivedProperties.Remove (baseProperty.Object);
Assert.That (vm.Properties, Is.Empty);
@@ -305,11 +306,11 @@ namespace Xamarin.PropertyEditing.Tests
providerMock.Setup (ep => ep.GetObjectEditorAsync (baseObj)).ReturnsAsync (baseEditorMock.Object);
providerMock.Setup (ep => ep.GetObjectEditorAsync (derivedObj)).ReturnsAsync (derivedEditorMock.Object);
- var vm = new PanelViewModel (providerMock.Object);
+ var vm = new PanelViewModel (providerMock.Object, TargetPlatform.Default);
vm.SelectedObjects.AddItems (new[] { baseObj, derivedObj });
Assume.That (vm.Properties.Count, Is.EqualTo (1));
- Assume.That (vm.Properties.Select (v => v.Property), Contains.Item (baseProperty.Object));
+ Assume.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (baseProperty.Object));
vm.SelectedObjects.Remove (derivedObj);
Assume.That (vm.Properties, Is.Not.Empty);
@@ -343,11 +344,11 @@ namespace Xamarin.PropertyEditing.Tests
providerMock.Setup (ep => ep.GetObjectEditorAsync (baseObj)).ReturnsAsync (baseEditorMock.Object);
providerMock.Setup (ep => ep.GetObjectEditorAsync (derivedObj)).ReturnsAsync (derivedEditorMock.Object);
- var vm = new PanelViewModel (providerMock.Object);
+ var vm = new PanelViewModel (providerMock.Object, TargetPlatform.Default);
vm.SelectedObjects.AddItems (new[] { baseObj, derivedObj });
Assume.That (vm.Properties.Count, Is.EqualTo (1));
- Assume.That (vm.Properties.Select (v => v.Property), Contains.Item (baseProperty.Object));
+ Assume.That (vm.Properties.Cast<PropertyViewModel>().Select (v => v.Property), Contains.Item (baseProperty.Object));
Assume.That (vm.SelectedObjects, Is.TypeOf<ObservableCollectionEx<object>> ());
((ObservableCollectionEx<object>)vm.SelectedObjects).Reset (new[] { baseObj });
@@ -385,7 +386,7 @@ namespace Xamarin.PropertyEditing.Tests
});
provider.Setup (ep => ep.GetObjectEditorAsync (obj2)).ReturnsAsync (editor2.Object);
- var vm = new PanelViewModel (provider.Object);
+ var vm = new PanelViewModel (provider.Object, TargetPlatform.Default);
vm.SelectedObjects.Add (obj1);
Assume.That (returnObject, Is.Not.Null);
@@ -409,15 +410,15 @@ namespace Xamarin.PropertyEditing.Tests
var editor = await provider.GetObjectEditorAsync (obj);
Assume.That (editor.Properties.Count, Is.EqualTo (2));
- var vm = new PanelViewModel (provider);
+ var vm = new PanelViewModel (provider, TargetPlatform.Default);
Assume.That (vm.ArrangeMode, Is.EqualTo (PropertyArrangeMode.Name));
vm.SelectedObjects.Add (obj);
- Assume.That (vm.ArrangedProperties, Is.Not.Empty);
- Assume.That (vm.ArrangedProperties[0].Count, Is.EqualTo (2));
+ Assume.That (vm.ArrangedEditors, Is.Not.Empty);
+ Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2));
vm.FilterText = "sub";
- Assert.That (vm.ArrangedProperties[0].Count, Is.EqualTo (1));
+ Assert.That (vm.ArrangedEditors[0].Count, Is.EqualTo (1));
}
[Test]
@@ -429,18 +430,18 @@ namespace Xamarin.PropertyEditing.Tests
var editor = await provider.GetObjectEditorAsync (obj);
Assume.That (editor.Properties.Count, Is.EqualTo (2));
- var vm = new PanelViewModel (provider);
+ var vm = new PanelViewModel (provider, TargetPlatform.Default);
Assume.That (vm.ArrangeMode, Is.EqualTo (PropertyArrangeMode.Name));
vm.SelectedObjects.Add (obj);
- Assume.That (vm.ArrangedProperties, Is.Not.Empty);
- Assume.That (vm.ArrangedProperties[0].Count, Is.EqualTo (2));
+ Assume.That (vm.ArrangedEditors, Is.Not.Empty);
+ Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2));
vm.FilterText = "sub";
- Assume.That (vm.ArrangedProperties[0].Count, Is.EqualTo (1));
+ Assume.That (vm.ArrangedEditors[0].Count, Is.EqualTo (1));
vm.FilterText = String.Empty;
- Assert.That (vm.ArrangedProperties[0].Count, Is.EqualTo (2));
+ Assert.That (vm.ArrangedEditors[0].Count, Is.EqualTo (2));
}
[Test]
@@ -451,11 +452,44 @@ namespace Xamarin.PropertyEditing.Tests
var editor = await provider.GetObjectEditorAsync (obj);
Assume.That (editor.Properties.Count, Is.EqualTo (2));
- var vm = new PanelViewModel (provider) { ArrangeMode = PropertyArrangeMode.Category };
+ var vm = new PanelViewModel (provider, TargetPlatform.Default) { ArrangeMode = PropertyArrangeMode.Category };
vm.SelectedObjects.Add (obj);
- Assume.That (vm.ArrangedProperties, Is.Not.Empty);
- Assert.That (vm.ArrangedProperties.FirstOrDefault (g => g.Key == "Sub"), Is.Not.Null);
+ Assume.That (vm.ArrangedEditors, Is.Not.Empty);
+ Assert.That (vm.ArrangedEditors.FirstOrDefault (g => g.Key == "Sub"), Is.Not.Null);
+ }
+
+ [Test]
+ public void GroupedPropertiesArrange ()
+ {
+ var intProvider = new IntegerPropertyViewModelTests ();
+ var stringProvider = new StringViewModelTests ();
+ var brushProvider = new SolidBrushPropertyViewModelTests();
+
+ var intProperty = intProvider.GetPropertyMock ("int", "A");
+ var stringProperty1 = stringProvider.GetPropertyMock ("string1");
+ var stringProperty2 = stringProvider.GetPropertyMock ("string2");
+ var brushProperty = brushProvider.GetPropertyMock ("brush", "C");
+
+ var editor = new MockObjectEditor (intProperty.Object, stringProperty1.Object, stringProperty2.Object, brushProperty.Object);
+
+ var provider = new Mock<IEditorProvider> ();
+ provider.Setup (p => p.GetObjectEditorAsync (editor.Target)).ReturnsAsync (editor);
+
+ var platform = new TargetPlatform {
+ GroupedTypes = new Dictionary<Type, string> {
+ { typeof(string), "B" }
+ }
+ };
+
+ var vm = new PanelViewModel (provider.Object, platform);
+ Assume.That (vm.ArrangeMode, Is.EqualTo (PropertyArrangeMode.Name));
+
+ 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"));
}
[Test]
@@ -466,22 +500,22 @@ namespace Xamarin.PropertyEditing.Tests
var editor = await provider.GetObjectEditorAsync (obj);
Assume.That (editor.Properties.Count, Is.EqualTo (2));
- var vm = new PanelViewModel (provider) { ArrangeMode = PropertyArrangeMode.Category };
+ var vm = new PanelViewModel (provider, TargetPlatform.Default) { ArrangeMode = PropertyArrangeMode.Category };
vm.SelectedObjects.Add (obj);
- Assume.That (vm.ArrangedProperties, Is.Not.Empty);
+ Assume.That (vm.ArrangedEditors, Is.Not.Empty);
vm.FilterText = "sub";
- Assert.That (vm.ArrangedProperties.Count, Is.EqualTo (1));
+ Assert.That (vm.ArrangedEditors.Count, Is.EqualTo (1));
- var group = vm.ArrangedProperties.FirstOrDefault (g => g.Key == "Sub");
+ var group = vm.ArrangedEditors.FirstOrDefault (g => g.Key == "Sub");
Assert.That (group, Is.Not.Null);
Assert.That (group.Count, Is.EqualTo (1));
}
internal override PropertiesViewModel CreateVm (IEditorProvider provider)
{
- return new PanelViewModel (provider);
+ return new PanelViewModel (provider, TargetPlatform.Default);
}
private TestContext context;
diff --git a/Xamarin.PropertyEditing.Tests/PropertyGroupViewModelTests.cs b/Xamarin.PropertyEditing.Tests/PropertyGroupViewModelTests.cs
new file mode 100644
index 0000000..468fe79
--- /dev/null
+++ b/Xamarin.PropertyEditing.Tests/PropertyGroupViewModelTests.cs
@@ -0,0 +1,187 @@
+using System.Collections.Specialized;
+using Moq;
+using NUnit.Framework;
+using Xamarin.PropertyEditing.ViewModels;
+
+namespace Xamarin.PropertyEditing.Tests
+{
+ [TestFixture]
+ internal class PropertyGroupViewModelTests
+ {
+ [Test]
+ public void PropertyGroup ()
+ {
+ IObjectEditor editor = null;
+
+ var prop = new Mock<IPropertyInfo> ();
+ prop.SetupGet (p => p.Type).Returns (typeof(int));
+
+ var prop2 = new Mock<IPropertyInfo> ();
+ prop2.SetupGet (p => p.Type).Returns (typeof(int));
+
+ editor = new MockObjectEditor (prop.Object, prop2.Object);
+ var pvm = new PropertyViewModel<int> (prop.Object, new[] { editor });
+ var pvm2 = new PropertyViewModel<int> (prop2.Object, new[] { editor });
+
+ var vm = new PropertyGroupViewModel ("category", new[] { pvm, pvm2 }, new [] { editor});
+ Assert.That (vm.Properties, Contains.Item (pvm));
+ Assert.That (vm.Properties, Contains.Item (pvm2));
+ }
+
+ [Test]
+ public void UnavailablePropertyNotInList ()
+ {
+ IObjectEditor editor;
+
+ var constraint = new Mock<IAvailabilityConstraint>();
+ var prop = new Mock<IPropertyInfo> ();
+ prop.SetupGet (p => p.Type).Returns (typeof(int));
+ prop.SetupGet (p => p.AvailabilityConstraints).Returns (new[] { constraint.Object });
+
+ var constraint2 = new Mock<IAvailabilityConstraint> ();
+ var prop2 = new Mock<IPropertyInfo> ();
+ prop2.SetupGet (p => p.Type).Returns (typeof(int));
+ prop2.SetupGet (p => p.AvailabilityConstraints).Returns (new[] { constraint2.Object });
+
+ editor = new MockObjectEditor (prop.Object, prop2.Object);
+ constraint.Setup (c => c.GetIsAvailableAsync (editor)).ReturnsAsync (true);
+ constraint2.Setup (c => c.GetIsAvailableAsync (editor)).ReturnsAsync (false);
+
+ var pvm = new PropertyViewModel<int> (prop.Object, new[] { editor });
+ var pvm2 = new PropertyViewModel<int> (prop2.Object, new[] { editor });
+
+ var vm = new PropertyGroupViewModel ("category", new[] { pvm, pvm2 }, new [] { editor});
+ Assert.That (vm.Properties, Contains.Item (pvm));
+ Assert.That (vm.Properties, Does.Not.Contain (pvm2));
+ }
+
+ [Test]
+ public void AvailabilityUpdates ()
+ {
+ IObjectEditor editor = null;
+
+ var constraint = new Mock<IAvailabilityConstraint>();
+ var prop = new Mock<IPropertyInfo> ();
+ prop.SetupGet (p => p.Type).Returns (typeof(int));
+ prop.SetupGet (p => p.AvailabilityConstraints).Returns (new[] { constraint.Object });
+
+ bool isAvailable = false;
+
+ var constraint2 = new Mock<IAvailabilityConstraint> ();
+ constraint2.SetupGet (a => a.ConstrainingProperties).Returns (new[] { prop.Object });
+ var prop2 = new Mock<IPropertyInfo> ();
+ prop2.SetupGet (p => p.Type).Returns (typeof(int));
+ prop2.SetupGet (p => p.AvailabilityConstraints).Returns (new[] { constraint2.Object });
+
+ editor = new MockObjectEditor (prop.Object, prop2.Object);
+ constraint.Setup (c => c.GetIsAvailableAsync (editor)).ReturnsAsync (true);
+ constraint2.Setup (c => c.GetIsAvailableAsync (editor)).ReturnsAsync (() => isAvailable);
+
+ var pvm = new PropertyViewModel<int> (prop.Object, new[] { editor });
+ var pvm2 = new PropertyViewModel<int> (prop2.Object, new[] { editor });
+
+ var vm = new PropertyGroupViewModel ("category", new[] { pvm, pvm2 }, new [] { editor});
+ Assume.That (vm.Properties, Contains.Item (pvm));
+ Assume.That (vm.Properties, Does.Not.Contain (pvm2));
+
+ INotifyCollectionChanged notify = vm.Properties as INotifyCollectionChanged;
+ Assume.That (notify, Is.Not.Null);
+
+ bool changed = false;
+ notify.CollectionChanged += (sender, args) => {
+ if (args.Action == NotifyCollectionChangedAction.Add && args.NewItems[0] == pvm2)
+ changed = true;
+ };
+
+ isAvailable = true;
+
+ // Bit of integration here, constrainting property changes will trigger availability requery
+ pvm.Value = 5;
+
+ Assert.That (changed, Is.True);
+ Assert.That (vm.Properties, Contains.Item (pvm));
+ Assert.That (vm.Properties, Contains.Item (pvm2));
+ }
+
+ [Test]
+ public void Filtered ()
+ {
+ IObjectEditor editor = null;
+
+ var prop = new Mock<IPropertyInfo> ();
+ prop.SetupGet (p => p.Type).Returns (typeof(int));
+ prop.SetupGet (p => p.Name).Returns ("one");
+
+ var prop2 = new Mock<IPropertyInfo> ();
+ prop2.SetupGet (p => p.Type).Returns (typeof(int));
+ prop2.SetupGet (p => p.Name).Returns ("two");
+
+ editor = new MockObjectEditor (prop.Object, prop2.Object);
+ var pvm = new PropertyViewModel<int> (prop.Object, new[] { editor });
+ var pvm2 = new PropertyViewModel<int> (prop2.Object, new[] { editor });
+
+ var vm = new PropertyGroupViewModel ("category", new[] { pvm, pvm2 }, new [] { editor});
+ Assume.That (vm.Properties, Contains.Item (pvm));
+ Assume.That (vm.Properties, Contains.Item (pvm2));
+
+ INotifyCollectionChanged notify = vm.Properties as INotifyCollectionChanged;
+ Assume.That (notify, Is.Not.Null);
+
+ bool changed = false;
+ notify.CollectionChanged += (sender, args) => {
+ if (args.Action == NotifyCollectionChangedAction.Remove && args.OldItems[0] == pvm)
+ changed = true;
+ };
+
+ vm.FilterText = "t";
+
+ Assert.That (changed, Is.True, "Collection changed event didn't trigger correctly");
+ Assert.That (vm.Properties, Contains.Item (pvm2));
+ Assert.That (vm.Properties, Does.Not.Contain (pvm));
+ Assert.That (vm.HasChildElements, Is.True);
+ }
+
+ [Test]
+ public void FilteredOutOfChildren ()
+ {
+ IObjectEditor editor = null;
+
+ var prop = new Mock<IPropertyInfo> ();
+ prop.SetupGet (p => p.Type).Returns (typeof(int));
+ prop.SetupGet (p => p.Name).Returns ("one");
+
+ var prop2 = new Mock<IPropertyInfo> ();
+ prop2.SetupGet (p => p.Type).Returns (typeof(int));
+ prop2.SetupGet (p => p.Name).Returns ("two");
+
+ editor = new MockObjectEditor (prop.Object, prop2.Object);
+ var pvm = new PropertyViewModel<int> (prop.Object, new[] { editor });
+ var pvm2 = new PropertyViewModel<int> (prop2.Object, new[] { editor });
+
+ var vm = new PropertyGroupViewModel ("category", new[] { pvm, pvm2 }, new [] { editor});
+ Assume.That (vm.Properties, Contains.Item (pvm));
+ Assume.That (vm.Properties, Contains.Item (pvm2));
+
+ bool changed = false;
+ vm.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == nameof(PropertyGroupViewModel.HasChildElements))
+ changed = true;
+ };
+
+ vm.FilterText = "1";
+
+ Assert.That (changed, Is.True, "HasChildElements didn't change");
+ Assert.That (vm.HasChildElements, Is.False);
+ Assert.That (vm.Properties, Does.Not.Contain (pvm2));
+ Assert.That (vm.Properties, Does.Not.Contain (pvm));
+
+ changed = false;
+ vm.FilterText = null;
+
+ Assert.That (changed, Is.True, "HasChildElements didn't change");
+ Assert.That (vm.HasChildElements, Is.True);
+ Assert.That (vm.Properties, Contains.Item (pvm2));
+ Assert.That (vm.Properties, Contains.Item (pvm));
+ }
+ }
+}
diff --git a/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs b/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs
index cd82739..de5f787 100644
--- a/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs
+++ b/Xamarin.PropertyEditing.Tests/PropertyViewModelTests.cs
@@ -491,10 +491,43 @@ namespace Xamarin.PropertyEditing.Tests
}
[Test]
- public async Task PropertyChangeRequeriesAvailability ()
+ public async Task ConstrainingPropertyChangeRequeriesAvailability ()
{
+ var otherProp = GetPropertyMock ();
+
var prop = GetPropertyMock ();
+ var constraint = new Mock<IAvailabilityConstraint> ();
+ constraint.SetupGet (c => c.ConstrainingProperties).Returns (new[] { otherProp.Object });
+ prop.SetupGet (p => p.AvailabilityConstraints).Returns (new List<IAvailabilityConstraint> { constraint.Object });
+
+ bool isAvailable = true;
+ IObjectEditor editor = new MockObjectEditor (prop.Object, otherProp.Object);
+ constraint.Setup (c => c.GetIsAvailableAsync (editor)).ReturnsAsync (() => isAvailable);
+
+ var vm = GetViewModel (prop.Object, new[] { editor });
+ Assume.That (vm.IsAvailable, Is.True);
+
+ bool changed = false;
+ vm.PropertyChanged += (o, e) => {
+ if (e.PropertyName == nameof(PropertyViewModel.IsAvailable))
+ changed = true;
+ };
+
+ isAvailable = false;
+ await editor.SetValueAsync (otherProp.Object, new ValueInfo<TValue> {
+ Value = GetRandomTestValue(),
+ Source = ValueSource.Local
+ });
+
+ Assert.That (vm.IsAvailable, Is.False);
+ Assert.That (changed, Is.True);
+ }
+
+ [Test]
+ public async Task PropertyChangeRequeriesAvailability ()
+ {
+ var prop = GetPropertyMock ();
var constraint = new Mock<IAvailabilityConstraint> ();
constraint.SetupGet (c => c.ConstrainingProperties).Returns (new[] { prop.Object });
@@ -523,6 +556,82 @@ namespace Xamarin.PropertyEditing.Tests
Assert.That (changed, Is.True);
}
+ [Test]
+ public void ClearLocalValue ()
+ {
+ var value = GetNonDefaultRandomTestValue ();
+
+ var mockProperty = GetPropertyMock ();
+ mockProperty.SetupGet (pi => pi.ValueSources).Returns (ValueSources.Default | ValueSources.Local);
+
+ var mockEditor = new MockObjectEditor {
+ SupportsDefault = true,
+ Properties = new [] {
+ mockProperty.Object
+ }
+ };
+
+ var vm = GetViewModel (mockProperty.Object, new[] { mockEditor });
+ Assume.That (vm.Value, Is.EqualTo (default(TValue)));
+
+ Assert.That (vm.ClearValueCommand.CanExecute (null), Is.False);
+
+ bool changed = false;
+ vm.ClearValueCommand.CanExecuteChanged += (sender, args) => {
+ changed = true;
+ };
+
+ vm.Value = value;
+ Assume.That (vm.ValueSource, Is.EqualTo (ValueSource.Local));
+
+ Assert.That (changed, Is.True);
+ Assert.That (vm.ClearValueCommand.CanExecute (null), Is.True);
+
+ vm.ClearValueCommand.Execute (null);
+
+ Assert.That (vm.Value, Is.EqualTo (default(TValue)));
+ }
+
+ [Test]
+ [Description ("If the target platform doesn't support distinguishing local/default, we can't clear the value")]
+ public void ClearValueDefaultNotSupported ()
+ {
+ var value = GetNonDefaultRandomTestValue ();
+
+ var mockProperty = GetPropertyMock ();
+
+ var editorMock = new Mock<IObjectEditor> ();
+ editorMock.Setup (oe => oe.GetValueAsync<TValue> (mockProperty.Object, null)).ReturnsAsync (new ValueInfo<TValue> {
+ Value = value,
+ Source = ValueSource.Local
+ });
+ mockProperty.SetupGet (pi => pi.ValueSources).Returns (ValueSources.Local);
+
+ var vm = GetViewModel (mockProperty.Object, new[] { editorMock.Object });
+
+ Assert.That (vm.ClearValueCommand.CanExecute (null), Is.False);
+ }
+
+ [Test]
+ [Description ("We only allow clearing local values")]
+ public void ClearValueNotLocalValue ()
+ {
+ var value = GetNonDefaultRandomTestValue ();
+
+ var mockProperty = GetPropertyMock ();
+ mockProperty.SetupGet (pi => pi.ValueSources).Returns (ValueSources.Default | ValueSources.Local | ValueSources.Resource);
+
+ var editorMock = new Mock<IObjectEditor> ();
+ editorMock.Setup (oe => oe.GetValueAsync<TValue> (mockProperty.Object, null)).ReturnsAsync (new ValueInfo<TValue> {
+ Value = value,
+ Source = ValueSource.Resource
+ });
+
+ var vm = GetViewModel (mockProperty.Object, new[] { editorMock.Object });
+
+ Assert.That (vm.ClearValueCommand.CanExecute (null), Is.False);
+ }
+
protected TValue GetNonDefaultRandomTestValue ()
{
TValue value = default (TValue);
@@ -541,10 +650,12 @@ namespace Xamarin.PropertyEditing.Tests
{
}
- protected Mock<IPropertyInfo> GetPropertyMock ()
+ protected internal Mock<IPropertyInfo> GetPropertyMock (string name = null, string category = null)
{
var mock = new Mock<IPropertyInfo> ();
mock.SetupGet (pi => pi.Type).Returns (typeof(TValue));
+ mock.SetupGet (pi => pi.Name).Returns (name);
+ mock.SetupGet (pi => pi.Category).Returns (category);
AugmentPropertyMock (mock);
@@ -553,12 +664,12 @@ namespace Xamarin.PropertyEditing.Tests
protected Random Random => this.rand;
- protected TValue GetRandomTestValue ()
+ protected internal TValue GetRandomTestValue ()
{
return GetRandomTestValue (this.rand);
}
- protected TValue GetRandomTestValue (TValue notValue)
+ protected internal TValue GetRandomTestValue (TValue notValue)
{
TValue value = GetRandomTestValue ();
while (Equals (value, notValue)) {
@@ -568,7 +679,7 @@ namespace Xamarin.PropertyEditing.Tests
return value;
}
- protected MockObjectEditor GetBasicEditor (IPropertyInfo property = null)
+ protected internal MockObjectEditor GetBasicEditor (IPropertyInfo property = null)
{
if (property == null) {
var propertyMock = GetPropertyMock ();
@@ -584,21 +695,21 @@ namespace Xamarin.PropertyEditing.Tests
return editor;
}
- protected MockObjectEditor GetBasicEditor (TValue value, IPropertyInfo property = null)
+ protected internal MockObjectEditor GetBasicEditor (TValue value, IPropertyInfo property = null)
{
var editor = GetBasicEditor (property);
editor.values[editor.Properties.First ()] = value;
return editor;
}
- protected TViewModel GetBasicTestModel ()
+ protected internal TViewModel GetBasicTestModel ()
{
var editor = GetBasicEditor ();
return GetViewModel (editor.Properties.First(), new[] { editor });
}
- protected TViewModel GetBasicTestModel (TValue value)
+ protected internal TViewModel GetBasicTestModel (TValue value)
{
var editor = GetBasicEditor (value);
diff --git a/Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj b/Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj
index d86ffac..5e94400 100644
--- a/Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj
+++ b/Xamarin.PropertyEditing.Tests/Xamarin.PropertyEditing.Tests.csproj
@@ -62,7 +62,7 @@
<Compile Include="BytePropertyViewModelTests.cs" />
<Compile Include="CommonColorTests.cs" />
<Compile Include="Helpers.cs" />
- <Compile Include="IGetAndSet.cs" />
+ <Compile Include="IPropertyConverter.cs" />
<Compile Include="MockControls\MockControl.cs" />
<Compile Include="MockControls\MockSampleControl.cs" />
<Compile Include="MockControls\MockWpfButton.cs" />
@@ -74,7 +74,9 @@
<Compile Include="MockPropertyInfo\MockEnumPropertyInfo.cs" />
<Compile Include="MockPropertyProviderTests.cs" />
<Compile Include="MultiAvailabilityConstraintTests.cs" />
+ <Compile Include="ObservableLookupTests.cs" />
<Compile Include="PointViewModelTests.cs" />
+ <Compile Include="PropertyGroupViewModelTests.cs" />
<Compile Include="RadialGradientBrushPropertyViewModelTests.cs" />
<Compile Include="LinearGradientBrushPropertyViewModelTests.cs" />
<Compile Include="ImageBrushPropertyViewModelTests.cs" />
diff --git a/Xamarin.PropertyEditing.Windows.Standalone/MainWindow.xaml.cs b/Xamarin.PropertyEditing.Windows.Standalone/MainWindow.xaml.cs
index 292d8c2..9508b85 100644
--- a/Xamarin.PropertyEditing.Windows.Standalone/MainWindow.xaml.cs
+++ b/Xamarin.PropertyEditing.Windows.Standalone/MainWindow.xaml.cs
@@ -1,5 +1,6 @@
-using System.Windows;
+using System.Windows;
using System.Windows.Controls;
+using Xamarin.PropertyEditing.Drawing;
using Xamarin.PropertyEditing.Tests;
namespace Xamarin.PropertyEditing.Windows.Standalone
@@ -15,11 +16,21 @@ namespace Xamarin.PropertyEditing.Windows.Standalone
this.panel.EditorProvider = new MockEditorProvider ();
}
- private void Button_Click (object sender, RoutedEventArgs e)
+ private async void Button_Click (object sender, RoutedEventArgs e)
{
- var mockedButton = sender as IMockedControl;
- var inspectedObject = (mockedButton == null || mockedButton.MockedControl == null)
- ? sender : mockedButton.MockedControl;
+ var mockedControl = sender as IMockedControl;
+ object inspectedObject;
+ if (mockedControl == null || mockedControl.MockedControl == null) {
+ inspectedObject = sender;
+ } else {
+ inspectedObject = mockedControl.MockedControl;
+ if (mockedControl is MockedSampleControlButton mockedButton) {
+ IObjectEditor editor = await this.panel.EditorProvider.GetObjectEditorAsync (inspectedObject);
+ await mockedButton.SetBrush (editor, new CommonSolidBrush (20, 120, 220, 240, "sRGB"));
+ await mockedButton.SetReadOnlyBrush (editor, new CommonSolidBrush (240, 220, 15, 190));
+ }
+ }
+
if (this.panel.SelectedItems.Contains (inspectedObject))
this.panel.SelectedItems.Remove (inspectedObject);
else
diff --git a/Xamarin.PropertyEditing.Windows.Standalone/MockedSampleControlButton.cs b/Xamarin.PropertyEditing.Windows.Standalone/MockedSampleControlButton.cs
index e2e30e8..9e26a3b 100644
--- a/Xamarin.PropertyEditing.Windows.Standalone/MockedSampleControlButton.cs
+++ b/Xamarin.PropertyEditing.Windows.Standalone/MockedSampleControlButton.cs
@@ -1,3 +1,4 @@
+using System.Threading.Tasks;
using Xamarin.PropertyEditing.Drawing;
using Xamarin.PropertyEditing.Tests.MockControls;
@@ -8,22 +9,31 @@ namespace Xamarin.PropertyEditing.Windows.Standalone
public MockedSampleControlButton () : base (new MockSampleControl ())
{
// TODO: Move the declaration of this property to MockSampleControl once SolidBrush is supported on both platforms.
- var brushPropertyInfo = new BrushPropertyInfo (
+ this.brushPropertyInfo = new BrushPropertyInfo (
name: "SolidBrush",
category: "Windows Only",
canWrite: true,
colorSpaces: new[] { "RGB", "sRGB" });
- MockedControl.AddProperty<CommonBrush> (brushPropertyInfo);
- MockedControl.SetValue<CommonBrush>(brushPropertyInfo,
- new CommonSolidBrush(20, 120, 220, 240, "sRGB"));
+ MockedControl.AddProperty<CommonBrush> (this.brushPropertyInfo);
- var readOnlyBrushPropertyInfo = new BrushPropertyInfo(
+ this.readOnlyBrushPropertyInfo = new BrushPropertyInfo (
name: "ReadOnlySolidBrush",
category: "Windows Only",
canWrite: false);
- MockedControl.AddProperty<CommonBrush> (readOnlyBrushPropertyInfo);
- MockedControl.SetValue<CommonBrush> (readOnlyBrushPropertyInfo,
- new CommonSolidBrush (240, 220, 15, 190));
+ MockedControl.AddProperty<CommonBrush> (this.readOnlyBrushPropertyInfo);
}
+
+ public async Task SetBrush (IObjectEditor editor, CommonBrush brush)
+ {
+ await editor.SetValueAsync (this.brushPropertyInfo, new ValueInfo<CommonBrush> { Value = brush });
+ }
+
+ public async Task SetReadOnlyBrush (IObjectEditor editor, CommonBrush brush)
+ {
+ await editor.SetValueAsync (this.readOnlyBrushPropertyInfo, new ValueInfo<CommonBrush> { Value = brush });
+ }
+
+ private IPropertyInfo brushPropertyInfo;
+ private IPropertyInfo readOnlyBrushPropertyInfo;
}
}
diff --git a/Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs b/Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs
index e9b8209..2b81047 100644
--- a/Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs
+++ b/Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs
@@ -41,9 +41,9 @@ namespace Xamarin.PropertyEditing.Windows
public override DataTemplate SelectTemplate (object item, DependencyObject container)
{
- var vm = item as PropertyViewModel;
+ var vm = item as EditorViewModel;
if (vm != null) {
- if (!vm.CanDelve)
+ if (!(vm is PropertyViewModel) || !((PropertyViewModel)vm).CanDelve)
return Options.EditorTemplate;
else
return Options.ParentTemplate;
@@ -107,6 +107,7 @@ namespace Xamarin.PropertyEditing.Windows
{ typeof(PropertyViewModel<CommonThickness>), typeof(ThicknessEditorControl) },
{ typeof(PredefinedValuesViewModel<>), typeof(EnumEditorControl) },
{ typeof(BrushPropertyViewModel), typeof(BrushEditorControl) },
+ { typeof(PropertyGroupViewModel), typeof(GroupEditorControl) }
};
}
}
diff --git a/Xamarin.PropertyEditing.Windows/GroupEditorControl.cs b/Xamarin.PropertyEditing.Windows/GroupEditorControl.cs
new file mode 100644
index 0000000..48b140c
--- /dev/null
+++ b/Xamarin.PropertyEditing.Windows/GroupEditorControl.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Specialized;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace Xamarin.PropertyEditing.Windows
+{
+ [TemplatePart (Name = "List", Type = typeof(ItemsControl))]
+ [TemplatePart (Name = "EditorPresenter", Type = typeof(Selector))]
+ internal class GroupEditorControl
+ : Selector
+ {
+ public GroupEditorControl ()
+ {
+ FocusableProperty.OverrideMetadata (typeof(GroupEditorControl), new FrameworkPropertyMetadata (false));
+
+ ItemContainerGenerator.StatusChanged += OnItemContainerGeneratorStatusChanged;
+ }
+
+ public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register (
+ "ContentTemplate", typeof(DataTemplate), typeof(GroupEditorControl), new PropertyMetadata (default(DataTemplate)));
+
+ public DataTemplate ContentTemplate
+ {
+ get { return (DataTemplate) GetValue (ContentTemplateProperty); }
+ set { SetValue (ContentTemplateProperty, value); }
+ }
+
+ public static readonly DependencyProperty ContentTemplateSelectorProperty = DependencyProperty.Register (
+ "ContentTemplateSelector", typeof(DataTemplateSelector), typeof(GroupEditorControl), new PropertyMetadata (default(DataTemplateSelector)));
+
+ public DataTemplateSelector ContentTemplateSelector
+ {
+ get { return (DataTemplateSelector) GetValue (ContentTemplateSelectorProperty); }
+ set { SetValue (ContentTemplateSelectorProperty, value); }
+ }
+
+ public static readonly DependencyProperty SelectorStyleProperty = DependencyProperty.Register (
+ "SelectorStyle", typeof(Style), typeof(GroupEditorControl), new PropertyMetadata (default(Style)));
+
+ public Style SelectorStyle
+ {
+ get { return (Style) GetValue (SelectorStyleProperty); }
+ set { SetValue (SelectorStyleProperty, value); }
+ }
+
+ protected override void OnSelectionChanged (SelectionChangedEventArgs e)
+ {
+ if (e.AddedItems.Count == 0)
+ return;
+
+ DependencyObject container = ItemContainerGenerator.ContainerFromItem (e.AddedItems[0]);
+ if (container == null || VisualTreeHelper.GetChildrenCount (container) == 0)
+ return;
+
+ var presenter = container as ContentPresenter;
+ if (presenter == null)
+ throw new InvalidOperationException ("Unexpected visual tree");
+
+ var toggle = VisualTreeHelper.GetChild (presenter, 0) as ToggleButton;
+ if (toggle == null)
+ throw new InvalidOperationException ("Children must be ToggleButton's");
+
+ toggle.IsChecked = true;
+ }
+
+ protected override void OnItemsChanged (NotifyCollectionChangedEventArgs e)
+ {
+ base.OnItemsChanged (e);
+
+ if (SelectedItem == null && Items.Count > 0)
+ SelectedIndex = 0;
+ }
+
+ protected override void OnKeyDown (KeyEventArgs e)
+ {
+ base.OnKeyDown (e);
+
+ if (e.Key == Key.Down && SelectedIndex < Items.Count - 1)
+ SetCurrentValue (SelectedIndexProperty, SelectedIndex + 1);
+ else if (e.Key == Key.Up && SelectedIndex >= 1)
+ SetCurrentValue (SelectedIndexProperty, SelectedIndex - 1);
+ }
+
+ private void OnItemContainerGeneratorStatusChanged (object sender, EventArgs args)
+ {
+ if (ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
+ return;
+
+ if (SelectedItem == null)
+ SetCurrentValue (SelectedItemProperty, Items[0]);
+
+ for (int i = 0; i < Items.Count; i++) {
+ var container = ItemContainerGenerator.ContainerFromIndex (i) as ContentPresenter;
+ if (container == null)
+ throw new InvalidOperationException ("Unexpected visual tree");
+
+ container.ApplyTemplate ();
+
+ var child = VisualTreeHelper.GetChild (container, 0);
+ var toggle = child as ToggleButton;
+ if (toggle == null)
+ throw new InvalidOperationException ("Children must be of ToggleButton");
+
+ if (Equals (SelectedItem, container.DataContext))
+ toggle.IsChecked = true;
+
+ toggle.Checked -= OnChoiceSelected;
+ toggle.Checked += OnChoiceSelected;
+ }
+ }
+
+ private void OnChoiceSelected (object sender, RoutedEventArgs e)
+ {
+ object selected = ((FrameworkElement) sender).DataContext;
+
+ SetCurrentValue (SelectedItemProperty, selected);
+ }
+ }
+}
diff --git a/Xamarin.PropertyEditing.Windows/HeaderedContextMenu.cs b/Xamarin.PropertyEditing.Windows/HeaderedContextMenu.cs
new file mode 100644
index 0000000..21558b6
--- /dev/null
+++ b/Xamarin.PropertyEditing.Windows/HeaderedContextMenu.cs
@@ -0,0 +1,27 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Xamarin.PropertyEditing.Windows
+{
+ internal class HeaderedContextMenu
+ : ContextMenu
+ {
+ public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register (
+ "Header", typeof (object), typeof (HeaderedContextMenu), new PropertyMetadata (default (object)));
+
+ public object Header
+ {
+ get { return (object)GetValue (HeaderProperty); }
+ set { SetValue (HeaderProperty, value); }
+ }
+
+ public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register (
+ "HeaderTemplate", typeof (DataTemplate), typeof (HeaderedContextMenu), new PropertyMetadata (default (DataTemplate)));
+
+ public DataTemplate HeaderTemplate
+ {
+ get { return (DataTemplate)GetValue (HeaderTemplateProperty); }
+ set { SetValue (HeaderTemplateProperty, value); }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing.Windows/MenuButton.cs b/Xamarin.PropertyEditing.Windows/MenuButton.cs
index db7b196..01a9aae 100644
--- a/Xamarin.PropertyEditing.Windows/MenuButton.cs
+++ b/Xamarin.PropertyEditing.Windows/MenuButton.cs
@@ -26,17 +26,30 @@ namespace Xamarin.PropertyEditing.Windows
set { SetValue (HeaderTemplateProperty, value); }
}
+ protected override void OnClick ()
+ {
+ base.OnClick ();
+
+ if (ContextMenu != null)
+ OpenMenu();
+ }
+
protected override void OnMouseDown (MouseButtonEventArgs e)
{
if (ContextMenu == null)
return;
- ContextMenu.PlacementTarget = this;
- ContextMenu.Placement = PlacementMode.Bottom;
- ContextMenu.IsOpen = true;
+ OpenMenu ();
e.Handled = true;
base.OnMouseDown (e);
}
+
+ private void OpenMenu ()
+ {
+ ContextMenu.PlacementTarget = this;
+ ContextMenu.Placement = PlacementMode.Bottom;
+ ContextMenu.IsOpen = true;
+ }
}
} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing.Windows/NumericUpDownControl.cs b/Xamarin.PropertyEditing.Windows/NumericUpDownControl.cs
index adbda6c..28f7259 100644
--- a/Xamarin.PropertyEditing.Windows/NumericUpDownControl.cs
+++ b/Xamarin.PropertyEditing.Windows/NumericUpDownControl.cs
@@ -156,8 +156,8 @@ namespace Xamarin.PropertyEditing.Windows
base.OnApplyTemplate ();
this.textBox = (TextBox) GetTemplateChild ("TextBox");
- this.textBox.TextChanged += OnTextChanged;
this.textBox.Text = Value.ToString ();
+ this.textBox.TextChanged += OnTextChanged;
Button up = (Button) GetTemplateChild ("Up");
up.Click += (sender, args) => SetCurrentValue (ValueProperty, GetIncrementedValue (Value));
diff --git a/Xamarin.PropertyEditing.Windows/PropertyButton.cs b/Xamarin.PropertyEditing.Windows/PropertyButton.cs
new file mode 100644
index 0000000..5e893c1
--- /dev/null
+++ b/Xamarin.PropertyEditing.Windows/PropertyButton.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Shapes;
+
+namespace Xamarin.PropertyEditing.Windows
+{
+ [TemplatePart (Name = "Border", Type = typeof(UIElement))]
+ [TemplatePart (Name = "Indicator", Type = typeof(Rectangle))]
+ internal class PropertyButton
+ : Control
+ {
+ static PropertyButton ()
+ {
+ FocusableProperty.OverrideMetadata (typeof (PropertyButton), new FrameworkPropertyMetadata (false));
+ DefaultStyleKeyProperty.OverrideMetadata (typeof (PropertyButton), new FrameworkPropertyMetadata (typeof (PropertyButton)));
+ }
+
+ public static readonly DependencyProperty CanSetCustomExpressionProperty = DependencyProperty.Register (
+ "CanSetCustomExpression", typeof (bool), typeof (PropertyButton), new PropertyMetadata (default (bool)));
+
+ public bool CanSetCustomExpression
+ {
+ get { return (bool)GetValue (CanSetCustomExpressionProperty); }
+ set { SetValue (CanSetCustomExpressionProperty, value); }
+ }
+
+ public static readonly DependencyProperty SystemResourcesSourceProperty = DependencyProperty.Register (
+ "SystemResourcesSource", typeof (IEnumerable), typeof (PropertyButton), new PropertyMetadata (default (IEnumerable)));
+
+ public IEnumerable SystemResourcesSource
+ {
+ get { return (IEnumerable)GetValue (SystemResourcesSourceProperty); }
+ set { SetValue (SystemResourcesSourceProperty, value); }
+ }
+
+ public static readonly DependencyProperty SystemResourceNamePathProperty = DependencyProperty.Register (
+ "SystemResourceNamePath", typeof (string), typeof (PropertyButton), new PropertyMetadata ("Name"));
+
+ public string SystemResourceNamePath
+ {
+ get { return (string)GetValue (SystemResourceNamePathProperty); }
+ set { SetValue (SystemResourceNamePathProperty, value); }
+ }
+
+ public static readonly DependencyProperty ValueSourceProperty = DependencyProperty.Register (
+ "ValueSource", typeof (ValueSource), typeof (PropertyButton), new PropertyMetadata (ValueSource.Default, (o, args) => ((PropertyButton)o).OnValueSourceChanged ((ValueSource)args.NewValue)));
+
+ public ValueSource ValueSource
+ {
+ get { return (ValueSource)GetValue (ValueSourceProperty); }
+ set { SetValue (ValueSourceProperty, value); }
+ }
+
+ public static readonly DependencyProperty MenuTemplateProperty = DependencyProperty.Register (
+ "MenuTemplate", typeof (DataTemplate), typeof (PropertyButton), new PropertyMetadata (default (DataTemplate)));
+
+ public DataTemplate MenuTemplate
+ {
+ get { return (DataTemplate)GetValue (MenuTemplateProperty); }
+ set { SetValue (MenuTemplateProperty, value); }
+ }
+
+ public override void OnApplyTemplate ()
+ {
+ base.OnApplyTemplate ();
+
+ this.indicator = (Rectangle)GetTemplateChild ("Indicator");
+ if (this.indicator == null)
+ throw new InvalidOperationException ("PropertyButton template Missing part Indicator");
+
+ var border = GetTemplateChild ("Border") as UIElement;
+ if (border == null)
+ throw new InvalidOperationException ("PropertyButton template Missing part Border");
+
+ border.MouseDown += OnBorderMouseDown;
+
+ OnValueSourceChanged (ValueSource);
+ }
+
+ private Rectangle indicator;
+ private ContextMenu menu;
+
+ private MenuItem systemResources;
+
+ private void OnBorderMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ if (e.ChangedButton != MouseButton.Left)
+ return;
+
+ if (this.menu == null) {
+ this.menu = MenuTemplate?.LoadContent () as ContextMenu;
+ if (this.menu == null)
+ return;
+
+ this.menu.PlacementTarget = this.indicator;
+ this.menu.DataContext = DataContext;
+ }
+
+ this.menu.IsOpen = true;
+ e.Handled = true;
+ }
+
+ private void OnValueSourceChanged (ValueSource source)
+ {
+ if (this.indicator == null)
+ return;
+
+ string brush = null;
+
+ switch (source) {
+ case ValueSource.Local:
+ brush = "PropertyLocalValueBrush";
+ break;
+ case ValueSource.Binding:
+ brush = "PropertyBoundValueBrush";
+ break;
+ case ValueSource.Inherited:
+ case ValueSource.DefaultStyle:
+ case ValueSource.Style:
+ case ValueSource.Resource:
+ brush = "PropertyResourceBrush";
+ break;
+
+ case ValueSource.Default:
+ this.indicator.ClearValue (Shape.FillProperty);
+ return;
+ }
+
+ this.indicator.SetResourceReference (Shape.FillProperty, brush);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs b/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs
index 3598e3d..d3b43ad 100644
--- a/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs
+++ b/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs
@@ -43,6 +43,15 @@ namespace Xamarin.PropertyEditing.Windows
set { SetValue (EditorProviderProperty, value); }
}
+ public static readonly DependencyProperty TargetPlatformProperty = DependencyProperty.Register (
+ "TargetPlatform", typeof(TargetPlatform), typeof(PropertyEditorPanel), new PropertyMetadata (TargetPlatform.Default));
+
+ public TargetPlatform TargetPlatform
+ {
+ get { return (TargetPlatform) GetValue (TargetPlatformProperty); }
+ set { SetValue (TargetPlatformProperty, value); }
+ }
+
private static readonly DependencyPropertyKey SelectedItemsPropertyKey = DependencyProperty.RegisterReadOnly (
nameof(SelectedItems), typeof(IList), typeof(PropertyEditorPanel), new PropertyMetadata (default(IList)));
@@ -149,7 +158,7 @@ namespace Xamarin.PropertyEditing.Windows
if (this.vm != null)
this.vm.PropertyChanged -= OnVmPropertyChanged;
- this.root.DataContext = this.vm = (EditorProvider != null) ? new PanelViewModel (EditorProvider) : null;
+ this.root.DataContext = this.vm = (EditorProvider != null) ? new PanelViewModel (EditorProvider, TargetPlatform) : null;
if (this.vm != null)
this.vm.PropertyChanged += OnVmPropertyChanged;
@@ -168,9 +177,9 @@ namespace Xamarin.PropertyEditing.Windows
Binding itemsSource;
if (newMode == PropertyArrangeMode.Name)
- itemsSource = new Binding ("ArrangedProperties[0]");
+ itemsSource = new Binding ("ArrangedEditors[0]");
else
- itemsSource = new Binding ("ArrangedProperties");
+ itemsSource = new Binding ("ArrangedEditors");
this.items.SetBinding (ItemsControl.ItemsSourceProperty, itemsSource);
}
diff --git a/Xamarin.PropertyEditing.Windows/PropertyMenuItemContainerStyleSelector.cs b/Xamarin.PropertyEditing.Windows/PropertyMenuItemContainerStyleSelector.cs
new file mode 100644
index 0000000..6d8377c
--- /dev/null
+++ b/Xamarin.PropertyEditing.Windows/PropertyMenuItemContainerStyleSelector.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Xamarin.PropertyEditing.Windows
+{
+ internal class PropertyMenuItemContainerStyleSelector
+ : StyleSelector
+ {
+ public Style MenuItemStyle
+ {
+ get;
+ set;
+ }
+
+ public Style SeparatorStyle
+ {
+ get;
+ set;
+ }
+
+ public override Style SelectStyle (object item, DependencyObject container)
+ {
+ if (container is MenuItem)
+ return MenuItemStyle;
+ if (container is Separator)
+ return SeparatorStyle ?? base.SelectStyle (item, container);
+
+ return base.SelectStyle (item, container);
+ }
+ }
+}
diff --git a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
index 6e0b87c..49f912a 100644
--- a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
+++ b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml
@@ -1,10 +1,11 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="clr-namespace:Xamarin.PropertyEditing.Windows"
- xmlns:options="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
- xmlns:vm="clr-namespace:Xamarin.PropertyEditing.ViewModels;assembly=Xamarin.PropertyEditing"
- xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
- xmlns:prop="clr-namespace:Xamarin.PropertyEditing.Properties;assembly=Xamarin.PropertyEditing">
+ xmlns:local="clr-namespace:Xamarin.PropertyEditing.Windows"
+ xmlns:options="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
+ xmlns:vm="clr-namespace:Xamarin.PropertyEditing.ViewModels;assembly=Xamarin.PropertyEditing"
+ xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
+ xmlns:prop="clr-namespace:Xamarin.PropertyEditing.Properties;assembly=Xamarin.PropertyEditing"
+ xmlns:system="clr-namespace:System;assembly=mscorlib">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MenuButtonStyle.xaml" />
@@ -34,6 +35,85 @@
</Setter>
</Style>
+ <Style x:Key="GroupSelectionItem" TargetType="RadioButton">
+ <Setter Property="OverridesDefaultStyle" Value="True" />
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="BorderThickness" Value="1" />
+ <Setter Property="Padding" Value="20,2,4,2" />
+ <Setter Property="MinHeight" Value="20" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="RadioButton">
+ <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
+ <ContentPresenter />
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="True">
+ <Setter Property="Background" Value="{DynamicResource ListItemMouseOverBackgroundBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource ListItemMouseOverBorderBrush}" />
+ <Setter Property="Foreground" Value="{DynamicResource ListItemMouseOverForegroundBrush}"/>
+ </Trigger>
+ <Trigger Property="IsChecked" Value="True">
+ <Setter Property="Background" Value="{DynamicResource ListItemSelectedBackgroundBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource ListItemSelectedBorderBrush}" />
+ <Setter Property="Foreground" Value="{DynamicResource ListItemSelectedForegroundBrush}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type local:GroupEditorControl}">
+ <Setter Property="Margin" Value="-19,0,0,0" />
+ <Setter Property="SelectorStyle">
+ <Setter.Value>
+ <Style TargetType="Border">
+ <Setter Property="BorderBrush" Value="{DynamicResource ListBackgroundBrush}" />
+ <Setter Property="Background" Value="{DynamicResource ListBackgroundBrush}" />
+ </Style>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="ItemsSource" Value="{Binding Properties}" />
+ <Setter Property="ItemTemplate">
+ <Setter.Value>
+ <DataTemplate DataType="vm:PropertyViewModel">
+ <RadioButton GroupName="GroupSelection" Style="{StaticResource GroupSelectionItem}" Content="{Binding}">
+ <RadioButton.ContentTemplate>
+ <DataTemplate>
+ <local:PropertyPresenter Label="{Binding Name,Mode=OneTime}" Content="{Binding Mode=OneTime}" ContentTemplateSelector="{StaticResource PropertyEditorSelector}" />
+ </DataTemplate>
+ </RadioButton.ContentTemplate>
+ </RadioButton>
+ </DataTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="ContentTemplateSelector" Value="{StaticResource PropertyEditorSelector}" />
+ <Setter Property="ItemContainerStyle">
+ <Setter.Value>
+ <Style TargetType="ContentPresenter">
+ <Setter Property="Width" Value="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType=ItemsPresenter}}" />
+ </Style>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="local:GroupEditorControl">
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+
+ <Border Style="{TemplateBinding SelectorStyle}" Grid.Row="0">
+ <ItemsPresenter Grid.IsSharedSizeScope="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
+ </Border>
+ <ContentPresenter Grid.Row="1" Name="EditorPresenter" Content="{TemplateBinding SelectedItem}" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
<Style x:Key="ChoiceControlItem" BasedOn="{StaticResource {x:Type RadioButton}}" TargetType="RadioButton">
<Setter Property="Background" Value="{DynamicResource ToggleItemBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleItemBorderBrush}" />
@@ -335,6 +415,57 @@
</Setter>
</Style>
+ <system:Double x:Key="MenuOffset">3</system:Double>
+
+ <Rectangle x:Shared="False" x:Key="LiteralMarker" Stroke="{DynamicResource PropertyButtonBorderBrush}" Fill="{DynamicResource LiteralMarkerBrush}" Height="12" Width="12" StrokeThickness="1" />
+
+ <Style TargetType="local:PropertyButton">
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="BorderThickness" Value="0" />
+ <Setter Property="Height" Value="17" />
+ <Setter Property="Width" Value="11" />
+ <Setter Property="SnapsToDevicePixels" Value="True" />
+ <Setter Property="MenuTemplate">
+ <Setter.Value>
+ <DataTemplate>
+ <local:HeaderedContextMenu Header="{Binding Property.Name,Mode=OneTime}" StaysOpen="True" Placement="Left" HorizontalOffset="{StaticResource MenuOffset}" VerticalOffset="{StaticResource MenuOffset}">
+ <!--<MenuItem x:Name="CustomExpressionItem" Header="{x:Static prop:Resources.CustomExpressionEllipsis}" Icon="{StaticResource LiteralMarker}" />
+ <Separator />!-->
+ <MenuItem Header="{x:Static prop:Resources.Reset}" Icon="{StaticResource LiteralMarker}" Command="{Binding ClearValueCommand,Mode=OneTime}" />
+ <!--<MenuItem Header="{x:Static prop:Resources.ConvertToLocalValue}" Icon="{StaticResource LiteralMarker}" Command="{Binding ConvertToLocalValueCommand,Mode=OneTime}" />
+ <Separator />
+ <MenuItem Header="{x:Static p:Resources.LocalResource}" />
+ <MenuItem Header="{x:Static p:Resources.SystemResource}" />
+ <MenuItem Header="{x:Static p:Resources.EditResourceEllipse}" />
+ <MenuItem Header="{x:Static p:Resources.ConvertToNewResourceEllipse}" />
+ <Separator />
+ <MenuItem Header="{x:Static p:Resources.CreateDataBindingEllipse}" />
+ <MenuItem Header="{x:Static p:Resources.TemplateBinding}" />
+ <Separator />
+ <MenuItem Header="{x:Static p:Resources.RecordCurrentValue}" />
+ <MenuItem Header="{x:Static p:Resources.GoToSource}" />!-->
+ </local:HeaderedContextMenu>
+ </DataTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="local:PropertyButton">
+ <Border Name="Border" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}">
+ <Rectangle Name="Indicator" Fill="{DynamicResource PanelBackgroundBrush}" Stroke="{DynamicResource PropertyButtonBorderBrush}" Height="7" Width="7" />
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Style.Triggers>
+ <Trigger Property="IsMouseOver" Value="True">
+ <Trigger.Setters>
+ <Setter Property="Background" Value="{DynamicResource ToggleItemMouseOverBackgroundBrush}" />
+ </Trigger.Setters>
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+
<Style TargetType="local:BrushEditorControl">
<Setter Property="Template">
<Setter.Value>
@@ -938,15 +1069,23 @@
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
- <ColumnDefinition MinWidth="100" MaxWidth="180" Width="0.4*" />
+ <ColumnDefinition Name="labelColumn" MinWidth="100" MaxWidth="180" Width="0.4*" />
<ColumnDefinition MinWidth="134" Width="0.6*" />
- <ColumnDefinition Width="12" />
+ <ColumnDefinition Name="propertyButtonColumn" Width="12" />
</Grid.ColumnDefinitions>
<TextBlock Name="Label" Grid.Column="0" Text="{TemplateBinding Label}" ToolTip="{TemplateBinding Label}" Height="16" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" />
<ContentPresenter Grid.Column="1" Margin="4,2,0,2" IsEnabled="{Binding Property.CanWrite}" />
+ <local:PropertyButton Grid.Column="2" ValueSource="{Binding ValueSource}" />
</Grid>
</Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="Label" Value="{x:Null}">
+ <Setter TargetName="labelColumn" Property="MinWidth" Value="0" />
+ <Setter TargetName="labelColumn" Property="Width" Value="0" />
+ <Setter TargetName="propertyButtonColumn" Property="Width" Value="0" />
+ </Trigger>
+ </ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
@@ -999,7 +1138,7 @@
<Setter Property="Foreground" Value="{DynamicResource LabelForegroundBrush}"/>
</Style>
- <Style TargetType="MenuItem">
+ <Style x:Key="HeaderedMenuItem" TargetType="MenuItem">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ItemsControl}}}"/>
@@ -1007,13 +1146,13 @@
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Transparent" />
- <Setter Property="Padding" Value="0,2,0,2" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
+ <Setter Property="Padding" Value="21,0,0,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MenuItem">
<Border x:Name="templateRoot" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True">
- <Grid>
+ <Grid Margin="{TemplateBinding Padding}">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="17" SharedSizeGroup="MenuItemIconColumnGroup" Width="Auto"/>
<ColumnDefinition Width="*"/>
@@ -1022,7 +1161,7 @@
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon" Grid.Column="0" ContentSource="Icon" Content="{TemplateBinding Icon}" Margin="4,0,6,0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" />
<Path x:Name="GlyphPanel" Data="M0,2L0,4.8 2.5,7.4 7.1,2.8 7.1,0 2.5,4.6z" Grid.Column="0" Margin="4,0,6,0" Visibility="Hidden" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" VerticalAlignment="Center" />
- <ContentPresenter x:Name="menuHeaderContainer" Grid.Column="1" ContentSource="Header" Content="{TemplateBinding Header}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" ContentTemplate="{TemplateBinding HeaderTemplate}" ContentStringFormat="{TemplateBinding HeaderStringFormat}" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
+ <ContentPresenter x:Name="menuHeaderContainer" Grid.Column="1" ContentSource="Header" Content="{TemplateBinding Header}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" ContentTemplate="{TemplateBinding HeaderTemplate}" Margin="0,2,0,2" ContentStringFormat="{TemplateBinding HeaderStringFormat}" HorizontalAlignment="Left" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
<TextBlock x:Name="menuGestureText" Grid.Column="2" Margin="5,2,0,2" DockPanel.Dock="Right" Text="{TemplateBinding InputGestureText}" VerticalAlignment="Center"/>
</Grid>
</Border>
@@ -1051,6 +1190,7 @@
</Style>
<Style TargetType="{x:Type ContextMenu}">
+ <Setter Property="Foreground" Value="{DynamicResource ListItemForegroundBrush}" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="FontFamily" Value="{DynamicResource {x:Static SystemFonts.MessageFontFamilyKey}}" />
<Setter Property="FontSize" Value="{DynamicResource {x:Static SystemFonts.MessageFontSizeKey}}" />
@@ -1091,6 +1231,55 @@
</Setter>
</Style>
+ <Style TargetType="{x:Type local:HeaderedContextMenu}" BasedOn="{StaticResource {x:Type ContextMenu}}">
+ <Setter Property="Padding" Value="2,0,2,14" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type local:HeaderedContextMenu}">
+ <theme:SystemDropShadowChrome x:Name="shadow" Color="Transparent">
+ <Border x:Name="Border" Padding="0,2,0,4" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
+ <StackPanel>
+ <Label Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" Margin="13,3,12,2" Padding="0" Foreground="{TemplateBinding Foreground}" />
+ <ItemsPresenter Margin="{TemplateBinding Padding}" HorizontalAlignment="Stretch" KeyboardNavigation.DirectionalNavigation="Cycle" />
+ </StackPanel>
+ </Border>
+ </theme:SystemDropShadowChrome>
+
+ <ControlTemplate.Triggers>
+ <MultiDataTrigger>
+ <MultiDataTrigger.Conditions>
+ <Condition Binding="{Binding HasDropShadow,RelativeSource={RelativeSource Self}}" Value="True" />
+ <Condition Binding="{Binding AreGradientsAllowed,Source={x:Static local:HostEnvironment.Current}}" Value="True" />
+ </MultiDataTrigger.Conditions>
+
+ <Setter TargetName="shadow" Property="Margin" Value="0,0,5,5" />
+ <Setter TargetName="shadow" Property="Color" Value="{DynamicResource DropShadowBackgroundColor}" />
+ </MultiDataTrigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="ItemContainerStyleSelector">
+ <Setter.Value>
+ <local:PropertyMenuItemContainerStyleSelector MenuItemStyle="{StaticResource HeaderedMenuItem}">
+ <local:PropertyMenuItemContainerStyleSelector.SeparatorStyle>
+ <Style TargetType="Separator" BasedOn="{StaticResource {x:Type Separator}}">
+ <Setter Property="Margin" Value="4,2,0,2" />
+ <Setter Property="Background" Value="{DynamicResource MenuSeparatorBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="Separator">
+ <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" />
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ </local:PropertyMenuItemContainerStyleSelector.SeparatorStyle>
+ </local:PropertyMenuItemContainerStyleSelector>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
<Style x:Key="ArrangeMenuItem" TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Header" Value="{Binding ArrangeMode,Mode=OneTime}" />
<Setter Property="IsCheckable" Value="True" />
@@ -1109,6 +1298,20 @@
<Setter Property="Foreground" Value="{DynamicResource InputForegroundBrush}" />
</Style>
+ <Style x:Key="RepeatButtonTransparent" TargetType="{x:Type RepeatButton}">
+ <Setter Property="OverridesDefaultStyle" Value="True" />
+ <Setter Property="IsTabStop" Value="False" />
+ <Setter Property="Focusable" Value="False" />
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="RepeatButton">
+ <Rectangle Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" Fill="{TemplateBinding Background}" />
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
diff --git a/Xamarin.PropertyEditing.Windows/Themes/VS.Dark.xaml b/Xamarin.PropertyEditing.Windows/Themes/VS.Dark.xaml
index e138c08..6622f4b 100644
--- a/Xamarin.PropertyEditing.Windows/Themes/VS.Dark.xaml
+++ b/Xamarin.PropertyEditing.Windows/Themes/VS.Dark.xaml
@@ -35,8 +35,22 @@
<SolidColorBrush x:Key="ListItemHighlightBorderBrush">#72555555</SolidColorBrush>
<SolidColorBrush x:Key="ListItemHighlightBackgroundBrush">#3E3E40</SolidColorBrush>
<SolidColorBrush x:Key="ListItemDisabledForegroundBrush">#656565</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemSelectedBackgroundBrush">#FF007ACC</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemSelectedBorderBrush">#FF3399FF</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemSelectedForegroundBrush">#FFFFFFFF</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemMouseOverBackgroundBrush">#FF3E3E40</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemMouseOverBorderBrush">#72555555</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemMouseOverForegroundBrush">#FFF1F1F1</SolidColorBrush>
<SolidColorBrush x:Key="MenuSeparatorBrush">#333337</SolidColorBrush>
+ <SolidColorBrush x:Key="LiteralMarkerBrush">#55000000</SolidColorBrush>
+ <SolidColorBrush x:Key="PropertyMenuDotIconBorderBrush">#FF999999</SolidColorBrush>
+ <SolidColorBrush x:Key="PropertyButtonBackgroundBrush">#F2F2F6</SolidColorBrush>
+ <SolidColorBrush x:Key="PropertyButtonBorderBrush">#717171</SolidColorBrush>
+ <SolidColorBrush x:Key="PropertyLocalValueBrush">#F1F1F1</SolidColorBrush>
+ <SolidColorBrush x:Key="PropertyBoundValueBrush">#FFCF00</SolidColorBrush>
+ <SolidColorBrush x:Key="PropertyResourceBrush">#00FF00</SolidColorBrush>
+
<SolidColorBrush x:Key="SearchControlBackgroundBrush">#333337</SolidColorBrush>
<SolidColorBrush x:Key="SearchControlBorderBrush">#3F3F46</SolidColorBrush>
<SolidColorBrush x:Key="SearchControlForegroundBrush">#D0D2D3</SolidColorBrush>
diff --git a/Xamarin.PropertyEditing.Windows/Themes/VS.Light.xaml b/Xamarin.PropertyEditing.Windows/Themes/VS.Light.xaml
index b7665de..35b1e8e 100644
--- a/Xamarin.PropertyEditing.Windows/Themes/VS.Light.xaml
+++ b/Xamarin.PropertyEditing.Windows/Themes/VS.Light.xaml
@@ -40,8 +40,16 @@
<SolidColorBrush x:Key="ListItemHighlightBackgroundBrush">#C9DEF5</SolidColorBrush>
<SolidColorBrush x:Key="ListItemHighlightBorderBrush">#C9DEF5</SolidColorBrush>
<SolidColorBrush x:Key="ListItemDisabledForegroundBrush">#FFA2A4A5</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemSelectedBackgroundBrush">#FF007ACC</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemSelectedBorderBrush">#FFCCCED8</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemSelectedForegroundBrush">#FFFFFFFF</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemMouseOverBackgroundBrush">#FFC9DEF5</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemMouseOverBorderBrush">#FFC9DEF5</SolidColorBrush>
+ <SolidColorBrush x:Key="ListItemMouseOverForegroundBrush">#FF1E1E1E</SolidColorBrush>
<SolidColorBrush x:Key="MenuSeparatorBrush">#E0E3E6</SolidColorBrush>
+ <SolidColorBrush x:Key="LiteralMarkerBrush">#55000000</SolidColorBrush>
+ <SolidColorBrush x:Key="PropertyMenuDotIconBorderBrush">#FF717171</SolidColorBrush>
<SolidColorBrush x:Key="PropertyButtonBackgroundBrush">#F2F2F6</SolidColorBrush>
<SolidColorBrush x:Key="PropertyButtonBorderBrush">#717171</SolidColorBrush>
<SolidColorBrush x:Key="PropertyLocalValueBrush">#1E1E1E</SolidColorBrush>
diff --git a/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj b/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj
index 8fac80f..818159a 100644
--- a/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj
+++ b/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj
@@ -71,6 +71,8 @@
<Compile Include="EditorPropertySelector.cs" />
<Compile Include="EnumEditorControl.cs" />
<Compile Include="HexColorConverter.cs" />
+ <Compile Include="HeaderedContextMenu.cs" />
+ <Compile Include="GroupEditorControl.cs" />
<Compile Include="HostEnvironment.cs" />
<Compile Include="MenuButton.cs" />
<Compile Include="NumericEditorControl.cs" />
@@ -81,8 +83,10 @@
<Compile Include="PointEditorControl.cs" />
<Compile Include="PointHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="PropertyButton.cs" />
<Compile Include="PropertyEditorControl.cs" />
<Compile Include="PropertyEditorPanel.cs" />
+ <Compile Include="PropertyMenuItemContainerStyleSelector.cs" />
<Compile Include="PropertyPresenter.cs" />
<Compile Include="ShadeEditorControl.cs" />
<Compile Include="SizeEditorControl.cs" />
diff --git a/Xamarin.PropertyEditing/IGroupingList.cs b/Xamarin.PropertyEditing/IGroupingList.cs
index 45d655b..ee7cdb4 100644
--- a/Xamarin.PropertyEditing/IGroupingList.cs
+++ b/Xamarin.PropertyEditing/IGroupingList.cs
@@ -28,10 +28,5 @@ namespace Xamarin.PropertyEditing
{
get;
}
-
- public void QuietClear()
- {
- Items.Clear ();
- }
}
}
diff --git a/Xamarin.PropertyEditing/ObservableLookup.cs b/Xamarin.PropertyEditing/ObservableLookup.cs
index 55a9778..5221989 100644
--- a/Xamarin.PropertyEditing/ObservableLookup.cs
+++ b/Xamarin.PropertyEditing/ObservableLookup.cs
@@ -87,7 +87,7 @@ namespace Xamarin.PropertyEditing
if (key == null) {
grouping = nullGrouping;
if (grouping.Count == 0)
- OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, grouping, this.groupings.Count));
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, (object)grouping, this.groupings.Count));
} else if (!this.groupings.TryGetValue (key, out grouping)) {
if (!ReuseGroups || !this.oldGroups.TryRemove (key, out grouping))
grouping = new ObservableGrouping<TKey, TElement> (key);
@@ -110,7 +110,7 @@ namespace Xamarin.PropertyEditing
grouping = nullGrouping;
grouping.AddRange (elements);
if (wasEmpty)
- OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, grouping, this.groupings.Count));
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, (object)grouping, this.groupings.Count));
} else if (!this.groupings.TryGetValue (key, out grouping)) {
if (!ReuseGroups || !this.oldGroups.TryRemove (key, out grouping))
grouping = new ObservableGrouping<TKey, TElement> (key);
@@ -128,7 +128,7 @@ namespace Xamarin.PropertyEditing
bool wasEmpty = this.nullGrouping.Count == 0;
this.nullGrouping.AddRange (grouping);
if (wasEmpty)
- OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, grouping, this.groupings.Count));
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, (object)grouping, this.groupings.Count));
return;
}
@@ -174,7 +174,8 @@ namespace Xamarin.PropertyEditing
if (group.Remove (element)) {
if (group.Count == 0) {
- Remove (key);
+ if (!Remove (key) && key == null)
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Remove, (object)this.nullGrouping, this.groupings.Count));
}
return true;
@@ -193,9 +194,9 @@ namespace Xamarin.PropertyEditing
{
if (key == null) {
bool removed = (this.nullGrouping.Count > 0);
- this.nullGrouping.QuietClear();
+ this.nullGrouping.Clear();
if (removed)
- OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Remove, this.nullGrouping, this.groupings.Count));
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Remove, (object)this.nullGrouping, this.groupings.Count));
return removed;
}
@@ -205,11 +206,11 @@ namespace Xamarin.PropertyEditing
var g = this.groupings[index];
this.groupings.Remove (key);
if (ReuseGroups) {
- g.QuietClear ();
+ g.Clear ();
this.oldGroups.Add (key, g);
}
- OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Remove, g, index));
+ OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Remove, (object)g, index));
return true;
}
@@ -218,12 +219,12 @@ namespace Xamarin.PropertyEditing
public void Clear ()
{
- this.nullGrouping?.QuietClear();
+ this.nullGrouping?.Clear();
if (ReuseGroups) {
foreach (var g in this.groupings.Values) {
this.oldGroups.Add (g.Key, g);
- g.QuietClear ();
+ g.Clear ();
}
}
@@ -260,7 +261,16 @@ namespace Xamarin.PropertyEditing
get { return this.groupings.Count + ((this.nullGrouping != null && this.nullGrouping.Count > 0) ? 1 : 0); }
}
- IGroupingList<TKey, TElement> IReadOnlyList<IGroupingList<TKey, TElement>>.this[int index] => (IGroupingList<TKey, TElement>)this[index];
+ IGroupingList<TKey, TElement> IReadOnlyList<IGroupingList<TKey, TElement>>.this [int index]
+ {
+ get
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException (nameof(index));
+
+ return (IGroupingList<TKey, TElement>)this[index];
+ }
+ }
/// <summary>
/// Gets the elements for <paramref name="key"/>.
diff --git a/Xamarin.PropertyEditing/OrderedDictionary.cs b/Xamarin.PropertyEditing/OrderedDictionary.cs
index 2c607a1..d5dec71 100644
--- a/Xamarin.PropertyEditing/OrderedDictionary.cs
+++ b/Xamarin.PropertyEditing/OrderedDictionary.cs
@@ -34,7 +34,7 @@ using System.Collections.ObjectModel;
namespace Cadenza.Collections
{
internal class OrderedDictionary<TKey, TValue>
- : IDictionary<TKey, TValue>, IList<KeyValuePair<TKey, TValue>>
+ : IDictionary<TKey, TValue>, IList<KeyValuePair<TKey, TValue>>, IReadOnlyDictionary<TKey, TValue>
{
public OrderedDictionary ()
: this (0)
@@ -106,6 +106,16 @@ namespace Cadenza.Collections
}
}
+ IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
+ {
+ get { return Keys; }
+ }
+
+ IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
+ {
+ get { return Values; }
+ }
+
/// <summary>
/// Gets the value at the specified index.
/// </summary>
diff --git a/Xamarin.PropertyEditing/Properties/Resources.Designer.cs b/Xamarin.PropertyEditing/Properties/Resources.Designer.cs
index 095c821..8d2faaa 100644
--- a/Xamarin.PropertyEditing/Properties/Resources.Designer.cs
+++ b/Xamarin.PropertyEditing/Properties/Resources.Designer.cs
@@ -187,6 +187,24 @@ namespace Xamarin.PropertyEditing.Properties {
}
/// <summary>
+ /// Looks up a localized string similar to Convert to Local Value.
+ /// </summary>
+ public static string ConvertToLocalValue {
+ get {
+ return ResourceManager.GetString("ConvertToLocalValue", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Custom Expression….
+ /// </summary>
+ public static string CustomExpressionEllipsis {
+ get {
+ return ResourceManager.GetString("CustomExpressionEllipsis", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Event handlers for the selected element.
/// </summary>
public static string EventHandlersSelectedElement {
@@ -482,6 +500,14 @@ namespace Xamarin.PropertyEditing.Properties {
return ResourceManager.GetString("SolidBrush", resourceCulture);
}
}
+ /// <summary>
+ /// Looks up a localized string similar to Reset.
+ /// </summary>
+ public static string Reset {
+ get {
+ return ResourceManager.GetString("Reset", resourceCulture);
+ }
+ }
/// <summary>
/// Looks up a localized string similar to Type.
diff --git a/Xamarin.PropertyEditing/Properties/Resources.resx b/Xamarin.PropertyEditing/Properties/Resources.resx
index 1ecae03..7142701 100644
--- a/Xamarin.PropertyEditing/Properties/Resources.resx
+++ b/Xamarin.PropertyEditing/Properties/Resources.resx
@@ -173,6 +173,12 @@
<value>Cyan / Magenta / Yellow / Black</value>
<comment>CMYK help text</comment>
</data>
+ <data name="ConvertToLocalValue" xml:space="preserve">
+ <value>Convert to Local Value</value>
+ </data>
+ <data name="CustomExpressionEllipsis" xml:space="preserve">
+ <value>Custom Expression…</value>
+ </data>
<data name="EventHandlersSelectedElement" xml:space="preserve">
<value>Event handlers for the selected element</value>
</data>
@@ -281,6 +287,9 @@
<value>S</value>
<comment>Saturation initial used as saturation textbox label</comment>
</data>
+ <data name="Reset" xml:space="preserve">
+ <value>Reset</value>
+ </data>
<data name="Type" xml:space="preserve">
<value>Type</value>
</data>
diff --git a/Xamarin.PropertyEditing/TargetPlatform.cs b/Xamarin.PropertyEditing/TargetPlatform.cs
new file mode 100644
index 0000000..496acc4
--- /dev/null
+++ b/Xamarin.PropertyEditing/TargetPlatform.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+
+using Xamarin.PropertyEditing.Drawing;
+
+namespace Xamarin.PropertyEditing
+{
+ /// <summary>
+ /// Represents a target platform by defining top level feature support and presentation preferences
+ /// </summary>
+ public sealed class TargetPlatform
+ {
+ /// <summary>
+ /// Gets a dictionary defining the property types will be grouped into a single editor and their groups resource name.
+ /// </summary>
+ public IReadOnlyDictionary<Type, string> GroupedTypes
+ {
+ get;
+ set;
+ }
+
+ public static readonly TargetPlatform Default = new TargetPlatform {
+ GroupedTypes = new Dictionary<Type, string> {
+ { typeof(CommonBrush), "Brush" }
+ }
+ };
+ }
+}
diff --git a/Xamarin.PropertyEditing/ValueSource.cs b/Xamarin.PropertyEditing/ValueSource.cs
index 43b430b..eef7350 100644
--- a/Xamarin.PropertyEditing/ValueSource.cs
+++ b/Xamarin.PropertyEditing/ValueSource.cs
@@ -12,6 +12,8 @@ namespace Xamarin.PropertyEditing
Binding = 2,
Resource = 3,
Style = 4,
+ Inherited = 5,
+ DefaultStyle = 6,
}
[Flags]
diff --git a/Xamarin.PropertyEditing/ViewModels/EditorViewModel.cs b/Xamarin.PropertyEditing/ViewModels/EditorViewModel.cs
index 84e5896..89bbc80 100644
--- a/Xamarin.PropertyEditing/ViewModels/EditorViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/EditorViewModel.cs
@@ -1,11 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.Collections.ObjectModel;
using System.Collections.Specialized;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace Xamarin.PropertyEditing.ViewModels
{
@@ -25,6 +21,16 @@ namespace Xamarin.PropertyEditing.ViewModels
//TODO: Property is being set after the editors added trickle down since its in the subclass
}
+ public abstract string Category
+ {
+ get;
+ }
+
+ public abstract string Name
+ {
+ get;
+ }
+
/// <summary>
/// Gets if the property's value can not be determined because multiple editors disagree.
/// </summary>
@@ -44,7 +50,6 @@ namespace Xamarin.PropertyEditing.ViewModels
public ICollection<IObjectEditor> Editors
{
get;
- private set;
}
protected static AsyncWorkQueue AsyncWork
diff --git a/Xamarin.PropertyEditing/ViewModels/EventViewModel.cs b/Xamarin.PropertyEditing/ViewModels/EventViewModel.cs
index c725619..179a7c4 100644
--- a/Xamarin.PropertyEditing/ViewModels/EventViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/EventViewModel.cs
@@ -25,6 +25,10 @@ namespace Xamarin.PropertyEditing.ViewModels
get;
}
+ public override string Name => Event.Name;
+
+ public override string Category => null;
+
public string MethodName
{
get { return this.methodName; }
diff --git a/Xamarin.PropertyEditing/ViewModels/IFilterable.cs b/Xamarin.PropertyEditing/ViewModels/IFilterable.cs
new file mode 100644
index 0000000..db6fe5b
--- /dev/null
+++ b/Xamarin.PropertyEditing/ViewModels/IFilterable.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.PropertyEditing.ViewModels
+{
+ internal interface IFilterable
+ {
+ string FilterText { get; set; }
+
+ bool HasChildElements { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing/ViewModels/ObjectViewModel.cs b/Xamarin.PropertyEditing/ViewModels/ObjectViewModel.cs
index 2c6c08d..fbd905a 100644
--- a/Xamarin.PropertyEditing/ViewModels/ObjectViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/ObjectViewModel.cs
@@ -1,12 +1,12 @@
-namespace Xamarin.PropertyEditing.ViewModels
+namespace Xamarin.PropertyEditing.ViewModels
{
internal class ObjectViewModel
: PropertiesViewModel
{
private object value;
- public ObjectViewModel (IEditorProvider provider)
- : base (provider)
+ public ObjectViewModel (IEditorProvider provider, TargetPlatform targetPlatform)
+ : base (provider, targetPlatform)
{
}
diff --git a/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs
index bffbcc0..fb5d4d8 100644
--- a/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs
@@ -5,10 +5,10 @@ using System.Linq;
namespace Xamarin.PropertyEditing.ViewModels
{
internal class PanelViewModel
- : PropertiesViewModel
+ : PropertiesViewModel, IFilterable
{
- public PanelViewModel (IEditorProvider provider)
- : base (provider)
+ public PanelViewModel (IEditorProvider provider, TargetPlatform targetPlatform)
+ : base (provider, targetPlatform)
{
ArrangeModes = new List<ArrangeModeViewModel> {
new ArrangeModeViewModel (PropertyArrangeMode.Name, this),
@@ -18,7 +18,9 @@ namespace Xamarin.PropertyEditing.ViewModels
public event EventHandler ArrangedPropertiesChanged;
- public IReadOnlyList<IGroupingList<string, PropertyViewModel>> ArrangedProperties => this.arranged;
+ public IReadOnlyList<IGroupingList<string, EditorViewModel>> ArrangedEditors => this.arranged;
+
+ public bool HasChildElements => (this.arranged.Count > 0);
public string FilterText
{
@@ -79,27 +81,69 @@ namespace Xamarin.PropertyEditing.ViewModels
groups.Remove (group);
}
- protected override void OnAddProperties (IEnumerable<PropertyViewModel> properties)
+ protected override void OnAddEditors (IEnumerable<EditorViewModel> editors)
{
- IEnumerable<PropertyViewModel> props = Properties;
+ IEnumerable<EditorViewModel> props = Properties;
if (!String.IsNullOrWhiteSpace (FilterText))
props = props.Where (MatchesFilter);
- props = props.OrderBy (vm => vm.Property.Name);
+ props = props.OrderBy (vm => vm.Name);
+
+ Dictionary<string, List<PropertyViewModel>> groupedTypeProperties = null;
this.arranged.Clear ();
foreach (var grouping in props.GroupBy (GetGroup).OrderBy (g => g.Key)) {
- this.arranged.Add (grouping);
+ HashSet<EditorViewModel> remainingItems = null;
+
+ if (ArrangeMode == PropertyArrangeMode.Category) {
+ foreach (EditorViewModel editorVm in grouping) {
+ var vm = editorVm as PropertyViewModel;
+ if (vm != null && TargetPlatform.GroupedTypes.TryGetValue (vm.Property.Type, out string category)) {
+ if (remainingItems == null)
+ remainingItems = new HashSet<EditorViewModel> (grouping);
+
+ remainingItems.Remove (vm);
+
+ if (groupedTypeProperties == null)
+ groupedTypeProperties = new Dictionary<string, List<PropertyViewModel>> ();
+ if (!groupedTypeProperties.TryGetValue (category, out List<PropertyViewModel> group))
+ groupedTypeProperties[category] = group = new List<PropertyViewModel> ();
+
+ group.Add (vm);
+ }
+ }
+ }
+
+ if (remainingItems != null)
+ this.arranged.Add (grouping.Key, remainingItems);
+ else
+ this.arranged.Add (grouping);
+ }
+
+ if (groupedTypeProperties != null) { // Insert type-grouped properties back in sorted.
+ int i = 0;
+ foreach (var kvp in groupedTypeProperties.OrderBy (kvp => kvp.Key)) {
+ for (; i < this.arranged.Count; i++) {
+ var g = (IGrouping<string, EditorViewModel>) this.arranged[i];
+ // TODO: Are we translating categories? If so this needs to lookup the resource and be culture specific
+ if (String.Compare (g.Key, kvp.Key, StringComparison.Ordinal) > 0) {
+ this.arranged.Insert (i++, new ObservableGrouping<string, EditorViewModel> (kvp.Key) {
+ new PropertyGroupViewModel (kvp.Key, kvp.Value, ObjectEditors)
+ });
+ break;
+ }
+ }
+ }
}
ArrangedPropertiesChanged?.Invoke (this, EventArgs.Empty);
}
- protected override void OnRemoveProperties (IEnumerable<PropertyViewModel> properties)
+ protected override void OnRemoveEditors (IEnumerable<EditorViewModel> editors)
{
- foreach (PropertyViewModel vm in properties) {
+ foreach (EditorViewModel vm in editors) {
string g = GetGroup (vm);
- var grouping = this.arranged[g] as ObservableGrouping<string, PropertyViewModel>;
+ var grouping = this.arranged[g] as ObservableGrouping<string, EditorViewModel>;
if (grouping != null) {
this.arranged.Remove (g, vm);
}
@@ -116,7 +160,7 @@ namespace Xamarin.PropertyEditing.ViewModels
}
private readonly Dictionary<PropertyArrangeMode, HashSet<string>> expandedGroups = new Dictionary<PropertyArrangeMode, HashSet<string>> ();
- private readonly ObservableLookup<string, PropertyViewModel> arranged = new ObservableLookup<string, PropertyViewModel> {
+ private readonly ObservableLookup<string, EditorViewModel> arranged = new ObservableLookup<string, EditorViewModel> {
ReuseGroups = true
};
@@ -127,56 +171,52 @@ namespace Xamarin.PropertyEditing.ViewModels
{
this.arranged.Clear ();
- OnAddProperties (Properties);
- }
-
- private enum FilterState
- {
- Unknown,
- Shorter,
- Longer
+ OnAddEditors (Properties);
}
private void Filter (string oldFilter)
{
- FilterState state = FilterState.Unknown;
- if (String.IsNullOrWhiteSpace (oldFilter) || FilterText.StartsWith (oldFilter, StringComparison.OrdinalIgnoreCase))
- state = FilterState.Longer;
- else if (oldFilter.StartsWith (FilterText, StringComparison.OrdinalIgnoreCase))
- state = FilterState.Shorter;
-
- if (state != FilterState.Shorter) {
- var toRemove = new List<PropertyViewModel> ();
+ bool hadChildren = HasChildElements;
+
+ 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) {
if (!MatchesFilter (vm))
toRemove.Add (vm);
+ else if (vm is IFilterable) {
+ var filterable = (IFilterable) vm;
+ filterable.FilterText = FilterText;
+ if (!filterable.HasChildElements)
+ toRemove.Add (vm);
+ }
}
}
- OnRemoveProperties (toRemove);
+ OnRemoveEditors (toRemove);
+ } else {
+ OnAddEditors (Properties);
}
- if (state != FilterState.Longer) {
- OnAddProperties (Properties);
- }
+ if (hadChildren != HasChildElements)
+ OnPropertyChanged (nameof(HasChildElements));
}
- private string GetGroup (PropertyViewModel vm)
+ private string GetGroup (EditorViewModel vm)
{
- return (ArrangeMode == PropertyArrangeMode.Name) ? "0" : vm.Property.Category;
+ return (ArrangeMode == PropertyArrangeMode.Name) ? "0" : vm.Category;
}
- private bool MatchesFilter (PropertyViewModel vm)
+ private bool MatchesFilter (EditorViewModel vm)
{
if (String.IsNullOrWhiteSpace (FilterText))
return true;
- if (ArrangeMode == PropertyArrangeMode.Category && vm.Property.Category != null && vm.Property.Category.Contains (FilterText, StringComparison.OrdinalIgnoreCase)) {
+ if (ArrangeMode == PropertyArrangeMode.Category && vm.Category != null && vm.Category.Contains (FilterText, StringComparison.OrdinalIgnoreCase)) {
return true;
}
- return vm.Property.Name.Contains (FilterText, StringComparison.OrdinalIgnoreCase);
+ return vm.Name.Contains (FilterText, StringComparison.OrdinalIgnoreCase);
}
}
} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs
index 7b647ec..e2fbbbb 100644
--- a/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs
@@ -13,12 +13,15 @@ namespace Xamarin.PropertyEditing.ViewModels
internal abstract class PropertiesViewModel
: NotifyingObject, INotifyDataErrorInfo
{
- public PropertiesViewModel (IEditorProvider provider)
+ public PropertiesViewModel (IEditorProvider provider, TargetPlatform targetPlatform)
{
if (provider == null)
throw new ArgumentNullException (nameof (provider));
+ if (targetPlatform == null)
+ throw new ArgumentNullException (nameof(targetPlatform));
EditorProvider = provider;
+ TargetPlatform = targetPlatform;
this.selectedObjects.CollectionChanged += OnSelectedObjectsChanged;
}
@@ -26,7 +29,7 @@ namespace Xamarin.PropertyEditing.ViewModels
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
/// <remarks>Consumers should check for <see cref="INotifyCollectionChanged"/> and hook appropriately.</remarks>
- public IReadOnlyList<PropertyViewModel> Properties => this.properties;
+ public IReadOnlyList<EditorViewModel> Properties => this.editors;
public IReadOnlyList<EventViewModel> Events => this.events;
@@ -85,6 +88,11 @@ namespace Xamarin.PropertyEditing.ViewModels
}
}
+ public TargetPlatform TargetPlatform
+ {
+ get;
+ }
+
public bool HasErrors => this.errors.IsValueCreated && this.errors.Value.Count > 0;
protected IEditorProvider EditorProvider
@@ -92,6 +100,8 @@ namespace Xamarin.PropertyEditing.ViewModels
get;
}
+ protected IReadOnlyList<IObjectEditor> ObjectEditors => this.objEditors;
+
public IEnumerable GetErrors (string propertyName)
{
if (!this.errors.IsValueCreated)
@@ -146,28 +156,28 @@ namespace Xamarin.PropertyEditing.ViewModels
case NotifyCollectionChangedAction.Remove:
removedEditors = new IObjectEditor[e.OldItems.Count];
for (int i = 0; i < e.OldItems.Count; i++) {
- IObjectEditor editor = this.editors.First (oe => oe.Target == e.OldItems[i]);
+ IObjectEditor editor = this.objEditors.First (oe => oe.Target == e.OldItems[i]);
INotifyCollectionChanged notifier = editor.Properties as INotifyCollectionChanged;
if (notifier != null)
notifier.CollectionChanged -= OnObjectEditorPropertiesChanged;
removedEditors[i] = editor;
- this.editors.Remove (editor);
+ this.objEditors.Remove (editor);
}
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Reset: {
- removedEditors = new IObjectEditor[this.editors.Count];
+ removedEditors = new IObjectEditor[this.objEditors.Count];
for (int i = 0; i < removedEditors.Length; i++) {
- removedEditors[i] = this.editors[i];
+ removedEditors[i] = this.objEditors[i];
INotifyCollectionChanged notifier = removedEditors[i].Properties as INotifyCollectionChanged;
if (notifier != null)
notifier.CollectionChanged -= OnObjectEditorPropertiesChanged;
}
- this.editors.Clear ();
+ this.objEditors.Clear ();
newEditors = await AddEditorsAsync (this.selectedObjects);
break;
@@ -178,11 +188,11 @@ namespace Xamarin.PropertyEditing.ViewModels
tcs.SetResult (true);
}
- protected virtual void OnAddProperties (IEnumerable<PropertyViewModel> properties)
+ protected virtual void OnAddEditors (IEnumerable<EditorViewModel> editors)
{
}
- protected virtual void OnRemoveProperties (IEnumerable<PropertyViewModel> properties)
+ protected virtual void OnRemoveEditors (IEnumerable<EditorViewModel> editors)
{
}
@@ -194,8 +204,8 @@ namespace Xamarin.PropertyEditing.ViewModels
private bool nameReadOnly;
private bool eventsEnabled;
private string typeName, objectName;
- private readonly List<IObjectEditor> editors = new List<IObjectEditor> ();
- private readonly ObservableCollectionEx<PropertyViewModel> properties = new ObservableCollectionEx<PropertyViewModel> ();
+ private readonly List<IObjectEditor> objEditors = new List<IObjectEditor> ();
+ private readonly ObservableCollectionEx<EditorViewModel> editors = new ObservableCollectionEx<EditorViewModel> ();
private readonly ObservableCollectionEx<object> selectedObjects = new ObservableCollectionEx<object> ();
private readonly ObservableCollectionEx<EventViewModel> events = new ObservableCollectionEx<EventViewModel> ();
private readonly Lazy<Dictionary<string, string>> errors = new Lazy<Dictionary<string, string>>();
@@ -205,16 +215,16 @@ namespace Xamarin.PropertyEditing.ViewModels
ErrorsChanged?.Invoke (this, e);
}
- private void AddProperties (IEnumerable<PropertyViewModel> properties)
+ private void AddProperties (IEnumerable<EditorViewModel> newEditors)
{
- this.properties.AddRange (properties);
- OnAddProperties (properties);
+ this.editors.AddRange (newEditors);
+ OnAddEditors (newEditors);
}
- private void RemoveProperties (IEnumerable<PropertyViewModel> properties)
+ private void RemoveProperties (IEnumerable<EditorViewModel> oldEditors)
{
- this.properties.RemoveRange (properties);
- OnRemoveProperties (properties);
+ this.editors.RemoveRange (oldEditors);
+ OnRemoveEditors (oldEditors);
}
private async void SetObjectName (string value)
@@ -255,31 +265,31 @@ namespace Xamarin.PropertyEditing.ViewModels
TypeName = null;
SetNameable (null);
SetCurrentObjectName (null, isReadonly: true);
- this.properties.Clear ();
+ this.editors.Clear ();
this.events.Clear ();
OnClearProperties ();
}
private void UpdateMembers (IObjectEditor[] removedEditors = null, IObjectEditor[] newEditors = null)
{
- if (this.editors.Count == 0) {
+ if (this.objEditors.Count == 0) {
ClearMembers ();
return;
}
Task<string> nameQuery = null;
- INameableObject firstNameable = this.editors[0] as INameableObject;
- if (this.editors.Count == 1) {
+ INameableObject firstNameable = this.objEditors[0] as INameableObject;
+ if (this.objEditors.Count == 1) {
nameQuery = firstNameable?.GetNameAsync ();
}
- IObjectEventEditor events = this.editors[0] as IObjectEventEditor;
+ IObjectEventEditor events = this.objEditors[0] as IObjectEventEditor;
var newEventSet = new HashSet<IEventInfo> (events?.Events ?? Enumerable.Empty<IEventInfo> ());
- string newTypeName = this.editors[0].TypeName;
- var newPropertySet = new HashSet<IPropertyInfo> (this.editors[0].Properties);
- for (int i = 1; i < this.editors.Count; i++) {
- IObjectEditor editor = this.editors[i];
+ string newTypeName = this.objEditors[0].TypeName;
+ var newPropertySet = new HashSet<IPropertyInfo> (this.objEditors[0].Properties);
+ for (int i = 1; i < this.objEditors.Count; i++) {
+ IObjectEditor editor = this.objEditors[i];
newPropertySet.IntersectWith (editor.Properties);
if (editor is IObjectEventEditor) {
@@ -291,7 +301,7 @@ namespace Xamarin.PropertyEditing.ViewModels
firstNameable = editor as INameableObject;
if (newTypeName != editor.TypeName)
- newTypeName = String.Format (PropertyEditing.Properties.Resources.MultipleTypesSelected, this.editors.Count);
+ newTypeName = String.Format (PropertyEditing.Properties.Resources.MultipleTypesSelected, this.objEditors.Count);
}
TypeName = newTypeName;
@@ -301,20 +311,20 @@ namespace Xamarin.PropertyEditing.ViewModels
EventsEnabled = events != null;
UpdateEvents (newEventSet, removedEditors, newEditors);
- string name = (this.editors.Count > 1) ? String.Format (PropertyEditing.Properties.Resources.MultipleObjectsSelected, this.editors.Count) : PropertyEditing.Properties.Resources.NoName;
- if (this.editors.Count == 1) {
+ string name = (this.objEditors.Count > 1) ? String.Format (PropertyEditing.Properties.Resources.MultipleObjectsSelected, this.objEditors.Count) : PropertyEditing.Properties.Resources.NoName;
+ if (this.objEditors.Count == 1) {
string tname = nameQuery?.Result;
if (tname != null)
name = tname;
}
SetNameable (firstNameable);
- SetCurrentObjectName (name, this.editors.Count > 1);
+ SetCurrentObjectName (name, this.objEditors.Count > 1);
}
private void UpdateEvents (HashSet<IEventInfo> newSet, IObjectEditor[] removedEditors = null, IObjectEditor[] newEditors = null)
{
- if (this.editors.Count > 1) {
+ if (this.objEditors.Count > 1) {
this.events.Clear ();
return;
}
@@ -341,14 +351,14 @@ namespace Xamarin.PropertyEditing.ViewModels
if (toRemove.Count > 0)
this.events.RemoveRange (toRemove);
if (newSet.Count > 0) {
- this.events.Reset (this.events.Concat (newSet.Select (i => new EventViewModel (i, this.editors))).OrderBy (e => e.Event.Name).ToArray());
+ this.events.Reset (this.events.Concat (newSet.Select (i => new EventViewModel (i, this.objEditors))).OrderBy (e => e.Event.Name).ToArray());
}
}
private void UpdateProperties (HashSet<IPropertyInfo> newSet, IObjectEditor[] removedEditors = null, IObjectEditor[] newEditors = null)
{
List<PropertyViewModel> toRemove = new List<PropertyViewModel> ();
- foreach (PropertyViewModel vm in this.properties.ToArray ()) {
+ foreach (PropertyViewModel vm in this.editors.ToArray ()) {
if (!newSet.Remove (vm.Property)) {
toRemove.Add (vm);
vm.Editors.Clear ();
@@ -386,7 +396,7 @@ namespace Xamarin.PropertyEditing.ViewModels
notifier.CollectionChanged += OnObjectEditorPropertiesChanged;
}
- this.editors.AddRange (newEditors);
+ this.objEditors.AddRange (newEditors);
return newEditors;
}
@@ -402,17 +412,17 @@ namespace Xamarin.PropertyEditing.ViewModels
Type hasPredefinedValues = interfaces.FirstOrDefault (t => t.IsGenericType && t.GetGenericTypeDefinition () == typeof(IHavePredefinedValues<>));
if (hasPredefinedValues != null) {
Type type = typeof(PredefinedValuesViewModel<>).MakeGenericType (hasPredefinedValues.GenericTypeArguments[0]);
- return (PropertyViewModel) Activator.CreateInstance (type, property, this.editors);
+ return (PropertyViewModel) Activator.CreateInstance (type, property, this.objEditors);
} else if (property.Type.IsEnum) {
Type type = typeof(EnumPropertyViewModel<>).MakeGenericType (property.Type);
- return (PropertyViewModel) Activator.CreateInstance (type, property, this.editors);
+ return (PropertyViewModel) Activator.CreateInstance (type, property, this.objEditors);
}
Func<IPropertyInfo, IEnumerable<IObjectEditor>, PropertyViewModel> vmFactory;
if (ViewModelMap.TryGetValue (property.Type, out vmFactory))
- return vmFactory (property, this.editors);
+ return vmFactory (property, this.objEditors);
- return new StringPropertyViewModel (property, this.editors);
+ return new StringPropertyViewModel (property, this.objEditors);
}
private Task busyTask;
diff --git a/Xamarin.PropertyEditing/ViewModels/PropertyGroupViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertyGroupViewModel.cs
new file mode 100644
index 0000000..8e65574
--- /dev/null
+++ b/Xamarin.PropertyEditing/ViewModels/PropertyGroupViewModel.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Xamarin.PropertyEditing.ViewModels
+{
+ internal class PropertyGroupViewModel
+ : EditorViewModel, IFilterable
+ {
+ public PropertyGroupViewModel (string category, IEnumerable<PropertyViewModel> properties, IEnumerable<IObjectEditor> objEditors)
+ : base (objEditors)
+ {
+ if (category == null)
+ throw new ArgumentNullException (nameof(category));
+ if (properties == null)
+ throw new ArgumentNullException (nameof (properties));
+
+ Category = category;
+
+ this.properties = properties.ToArray ();
+ foreach (var vm in this.properties) {
+ if (vm.IsAvailable)
+ this.propertiesView.Add (vm);
+
+ vm.PropertyChanged += OnChildPropertyChanged;
+ }
+ }
+
+ public IReadOnlyList<PropertyViewModel> Properties => this.propertiesView;
+
+ public override string Name => null;
+
+ public override string Category
+ {
+ get;
+ }
+
+ public bool HasChildElements => (this.propertiesView.Count > 0);
+
+ public string FilterText
+ {
+ get { return this.filterText; }
+ set
+ {
+ if (this.filterText == value)
+ return;
+
+ string oldFilter = this.filterText;
+ this.filterText = value;
+ Filter (oldFilter);
+ OnPropertyChanged();
+ }
+ }
+
+ private readonly PropertyViewModel[] properties;
+ private readonly ObservableCollectionEx<PropertyViewModel> propertiesView = new ObservableCollectionEx<PropertyViewModel> ();
+ private string filterText;
+
+ private void Filter (string oldFilter)
+ {
+ bool hadChildren = HasChildElements;
+
+ if (FilterText != null && (String.IsNullOrWhiteSpace (oldFilter) || FilterText.StartsWith (oldFilter, StringComparison.OrdinalIgnoreCase))) {
+ var current = new List<PropertyViewModel> (this.propertiesView);
+ for (int i = 0; i < current.Count; i++) {
+ var vm = current[i];
+ if (!MatchesFilter (vm))
+ this.propertiesView.Remove (vm);
+ }
+ } else {
+ this.propertiesView.Reset (this.properties.Where (MatchesFilter).OrderBy (p => p.Property.Name));
+ }
+
+ if (hadChildren != HasChildElements)
+ OnPropertyChanged (nameof(HasChildElements));
+ }
+
+ private bool MatchesFilter (PropertyViewModel vm)
+ {
+ if (!vm.IsAvailable)
+ return false;
+ if (String.IsNullOrWhiteSpace (FilterText))
+ return true;
+
+ return (vm.Property.Name.Contains (FilterText, StringComparison.OrdinalIgnoreCase));
+ }
+
+ private void OnChildPropertyChanged (object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName != nameof(PropertyViewModel.IsAvailable))
+ return;
+
+ var vm = (PropertyViewModel) sender;
+ if (MatchesFilter (vm))
+ this.propertiesView.Add (vm);
+ else
+ this.propertiesView.Remove (vm);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
index 3bee8d6..249a1ea 100644
--- a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
+++ b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs
@@ -19,6 +19,7 @@ namespace Xamarin.PropertyEditing.ViewModels
: base (property, editors)
{
SetValueResourceCommand = new RelayCommand<Resource> (OnSetValueToResource, CanSetValueToResource);
+ ClearValueCommand = new RelayCommand (OnClearValue, CanClearValue);
UpdateCurrentValue ();
}
@@ -58,6 +59,11 @@ namespace Xamarin.PropertyEditing.ViewModels
get;
}
+ public ICommand ClearValueCommand
+ {
+ get;
+ }
+
protected virtual TValue ValidateValue (TValue validationValue)
{
return validationValue;
@@ -110,6 +116,10 @@ namespace Xamarin.PropertyEditing.ViewModels
this.value = newValue;
OnValueChanged ();
OnPropertyChanged (nameof (Value));
+ OnPropertyChanged (nameof (ValueSource));
+
+ ((RelayCommand) ClearValueCommand)?.ChangeCanExecute ();
+
return true;
}
@@ -196,6 +206,19 @@ namespace Xamarin.PropertyEditing.ViewModels
((RelayCommand<Resource>)SetValueResourceCommand).ChangeCanExecute ();
}
+
+ private void OnClearValue ()
+ {
+ SetValue (new ValueInfo<TValue> {
+ Source = ValueSource.Default,
+ Value = default(TValue)
+ });
+ }
+
+ private bool CanClearValue ()
+ {
+ return (Property.ValueSources.HasFlag (ValueSources.Local) && Property.ValueSources.HasFlag (ValueSources.Default) && ValueSource == ValueSource.Local);
+ }
}
internal abstract class PropertyViewModel
@@ -218,9 +241,12 @@ namespace Xamarin.PropertyEditing.ViewModels
get;
}
+ public override string Name => Property.Name;
+ public override string Category => Property.Category;
+
public bool IsAvailable
{
- get { return this.isAvailable == null ? false : this.isAvailable.Result; }
+ get { return this.isAvailable?.Result ?? true; }
private set
{
if (this.isAvailable.Result == value)
@@ -279,9 +305,6 @@ namespace Xamarin.PropertyEditing.ViewModels
protected virtual async void OnEditorPropertyChanged (object sender, EditorPropertyChangedEventArgs e)
{
- if (e.Property != null && !Equals (e.Property, Property))
- return;
-
IDisposable work = null;
if (this.constraintProperties != null && this.constraintProperties.Contains (e.Property)) {
work = await AsyncWork.RequestAsyncWork (this);
@@ -289,6 +312,9 @@ namespace Xamarin.PropertyEditing.ViewModels
}
try {
+ if (e.Property != null && !Equals (e.Property, Property))
+ return;
+
// TODO: Smarter querying, can query the single editor and check against MultipleValues
UpdateCurrentValue ();
} finally {
diff --git a/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj b/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj
index ebacf32..e7a95a8 100644
--- a/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj
+++ b/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj
@@ -90,6 +90,7 @@
<Compile Include="Reflection\ReflectionEventInfo.cs" />
<Compile Include="Reflection\ReflectionObjectEditor.cs" />
<Compile Include="Reflection\ReflectionPropertyInfo.cs" />
+ <Compile Include="TargetPlatform.cs" />
<Compile Include="ValueInfo.cs" />
<Compile Include="ValueSource.cs" />
<Compile Include="ViewModels\ArrangeModeViewModel.cs" />
@@ -99,12 +100,14 @@
<Compile Include="ViewModels\EnumPropertyViewModel.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="ViewModels\EventViewModel.cs" />
+ <Compile Include="ViewModels\IFilterable.cs" />
<Compile Include="ViewModels\NumericPropertyViewModel.cs" />
<Compile Include="ViewModels\ObjectViewModel.cs" />
<Compile Include="ViewModels\PanelViewModel.cs" />
<Compile Include="ViewModels\PointPropertyViewModel.cs" />
<Compile Include="ViewModels\PredefinedValuesViewModel.cs" />
<Compile Include="ViewModels\PropertiesViewModel.cs" />
+ <Compile Include="ViewModels\PropertyGroupViewModel.cs" />
<Compile Include="ViewModels\PropertyViewModel.cs" />
<Compile Include="ViewModels\RelayCommand.cs" />
<Compile Include="ViewModels\SizePropertyViewModel.cs" />