[Core/Win] Bindings
This commit is contained in:
Родитель
eb754860e6
Коммит
d549051f4f
|
@ -1,12 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Internal;
|
||||
using Xamarin.PropertyEditing.ViewModels;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Tests
|
||||
|
|
|
@ -0,0 +1,709 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.PropertyEditing.Drawing;
|
||||
using Xamarin.PropertyEditing.Reflection;
|
||||
using Xamarin.PropertyEditing.Tests.MockControls;
|
||||
using Xamarin.PropertyEditing.ViewModels;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
internal class CreateBindingViewModelTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup ()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
|
||||
this.syncContext = new AsyncSynchronizationContext ();
|
||||
SynchronizationContext.SetSynchronizationContext (this.syncContext);
|
||||
}
|
||||
|
||||
private Exception unhandled;
|
||||
|
||||
private void CurrentDomainOnUnhandledException (object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
this.unhandled = e.ExceptionObject as Exception;
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown ()
|
||||
{
|
||||
this.syncContext.WaitForPendingOperationsToComplete ();
|
||||
SynchronizationContext.SetSynchronizationContext (null);
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException -= CurrentDomainOnUnhandledException;
|
||||
|
||||
if (this.unhandled != null) {
|
||||
var ex = this.unhandled;
|
||||
this.unhandled = null;
|
||||
Assert.Fail ("Unhandled exception: {0}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyDisplayType ()
|
||||
{
|
||||
var target = new object();
|
||||
|
||||
const string propName = "propertyName";
|
||||
var property = GetBasicProperty (propName);
|
||||
|
||||
var editor = new Mock<IObjectEditor> ();
|
||||
editor.SetupGet (e => e.Properties).Returns (new[] { property.Object });
|
||||
editor.SetupGet (e => e.Target).Returns (target);
|
||||
editor.SetupGet (e => e.TargetType).Returns (typeof(object).ToTypeInfo ());
|
||||
|
||||
var editorProvider = new MockEditorProvider (editor.Object);
|
||||
|
||||
var bpmock = new Mock<IBindingProvider> ();
|
||||
|
||||
var vm = new CreateBindingViewModel (new TargetPlatform (editorProvider, bpmock.Object), editor.Object, property.Object);
|
||||
Assert.That (vm.PropertyDisplay, Contains.Substring ("Object"));
|
||||
Assert.That (vm.PropertyDisplay, Contains.Substring (propName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyDisplayNameable ()
|
||||
{
|
||||
var target = new object ();
|
||||
const string propName = "propertyName";
|
||||
var property = GetBasicProperty (propName);
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
|
||||
const string objName = "objName";
|
||||
var nameable = editor.As<INameableObject> ();
|
||||
nameable.Setup (n => n.GetNameAsync ()).ReturnsAsync (objName);
|
||||
|
||||
var editorProvider = new MockEditorProvider (editor.Object);
|
||||
|
||||
var bpmock = new Mock<IBindingProvider> ();
|
||||
|
||||
var vm = new CreateBindingViewModel (new TargetPlatform (editorProvider, bpmock.Object), editor.Object, property.Object);
|
||||
Assert.That (vm.PropertyDisplay, Does.Not.Contains ("Object"));
|
||||
Assert.That (vm.PropertyDisplay, Contains.Substring (objName));
|
||||
Assert.That (vm.PropertyDisplay, Contains.Substring (propName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BindingSources ()
|
||||
{
|
||||
var target = new object ();
|
||||
var property = GetBasicProperty ();
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
|
||||
var editorProvider = new MockEditorProvider (editor.Object);
|
||||
|
||||
var sources = new[] {
|
||||
new BindingSource ("Short Description", BindingSourceType.Object, "Short Description"),
|
||||
new BindingSource ("Long Description", BindingSourceType.Object, "Long Description"),
|
||||
};
|
||||
|
||||
var bpmock = new Mock<IBindingProvider> ();
|
||||
bpmock.Setup (bp => bp.GetBindingSourcesAsync (target, property.Object)).ReturnsAsync (sources);
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[0], target)).ReturnsAsync (new[] { new object (), new object () });
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[1], target)).ReturnsAsync (new[] { new object () });
|
||||
bpmock.Setup (bp => bp.GetValueConverterResourcesAsync (It.IsAny<object> ())).ReturnsAsync (new Resource[0]);
|
||||
|
||||
var vm = new CreateBindingViewModel (new TargetPlatform (editorProvider, bpmock.Object), editor.Object, property.Object);
|
||||
|
||||
Assert.That (vm.BindingSources, Is.Not.Null);
|
||||
|
||||
var requested = await vm.BindingSources.Task;
|
||||
CollectionAssert.AreEqual (sources, requested);
|
||||
|
||||
Assert.That (vm.SelectedBindingSource, Is.EqualTo (sources[0]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task BindingSourceObjectRoots ()
|
||||
{
|
||||
var target = new object ();
|
||||
var property = GetBasicProperty ();
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
|
||||
var editorProvider = new MockEditorProvider (editor.Object);
|
||||
|
||||
var sources = new[] {
|
||||
new BindingSource ("Short Description", BindingSourceType.Object, "Short Description"),
|
||||
new BindingSource ("Long Description", BindingSourceType.Object, "Long Description"),
|
||||
};
|
||||
|
||||
var shortRoots = new[] { new object (), new object () };
|
||||
|
||||
var bpmock = new Mock<IBindingProvider> ();
|
||||
bpmock.Setup (bp => bp.GetBindingSourcesAsync (target, property.Object)).ReturnsAsync (sources);
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[0], target)).ReturnsAsync (shortRoots);
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[1], target)).ReturnsAsync (new[] { new object () });
|
||||
bpmock.Setup (bp => bp.GetValueConverterResourcesAsync (It.IsAny<object> ())).ReturnsAsync (new Resource[0]);
|
||||
|
||||
var vm = new CreateBindingViewModel (new TargetPlatform (editorProvider, bpmock.Object), editor.Object, property.Object);
|
||||
await vm.BindingSources.Task;
|
||||
Assume.That (vm.SelectedBindingSource, Is.EqualTo (sources[0]));
|
||||
|
||||
bpmock.Verify (bp => bp.GetRootElementsAsync (sources[0], target));
|
||||
IReadOnlyList<ObjectTreeElement> roots = await vm.ObjectElementRoots.Task;
|
||||
Assert.That (roots.Count, Is.EqualTo (2), "Unexpected number of roots");
|
||||
CollectionAssert.AreEqual (roots.Select (r => r.Editor.Target), shortRoots);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Description ("If the source has a description, we're on object type source and it's the only one, use a long description")]
|
||||
public async Task ShowLongDescription ()
|
||||
{
|
||||
var sources = new[] {
|
||||
new BindingSource ("Short Description", BindingSourceType.Object, "Short Description"),
|
||||
new BindingSource ("Long Description", BindingSourceType.SingleObject, "Long Description"),
|
||||
};
|
||||
var vm = CreateBasicViewModel (sources: sources);
|
||||
await vm.BindingSources.Task;
|
||||
|
||||
Assume.That (vm.SelectedBindingSource, Is.EqualTo (sources[0]));
|
||||
Assume.That (vm.ShowObjectSelector, Is.True, "Object selector should be showing");
|
||||
Assert.That (vm.ShowLongDescription, Is.False);
|
||||
|
||||
vm.SelectedBindingSource = sources[1];
|
||||
await vm.ObjectElementRoots.Task;
|
||||
Assert.That (vm.ShowLongDescription, Is.True);
|
||||
Assert.That (vm.ShowObjectSelector, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ValueConverters ()
|
||||
{
|
||||
var target = new object ();
|
||||
|
||||
var property = GetBasicProperty ();
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
|
||||
const string objName = "objName";
|
||||
var nameable = editor.As<INameableObject> ();
|
||||
nameable.Setup (n => n.GetNameAsync ()).ReturnsAsync (objName);
|
||||
|
||||
var editorProvider = new MockEditorProvider (editor.Object);
|
||||
|
||||
var sources = new[] {
|
||||
new BindingSource ("Short Description", BindingSourceType.Object, "Short Description"),
|
||||
new BindingSource ("Long Description", BindingSourceType.Object, "Long Description"),
|
||||
};
|
||||
|
||||
var visi = new Resource ("BooleanToVisibilityConverter");
|
||||
|
||||
var bpmock = new Mock<IBindingProvider> ();
|
||||
bpmock.Setup (bp => bp.GetBindingSourcesAsync (target, property.Object)).ReturnsAsync (sources);
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[0], target)).ReturnsAsync (new[] { new object (), new object () });
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[1], target)).ReturnsAsync (new[] { new object () });
|
||||
bpmock.Setup (bp => bp.GetValueConverterResourcesAsync (It.IsAny<object> ())).ReturnsAsync (new [] { visi });
|
||||
|
||||
var vm = new CreateBindingViewModel (new TargetPlatform (editorProvider, bpmock.Object), editor.Object, property.Object);
|
||||
Assert.That (vm.ValueConverters, Is.Not.Null);
|
||||
|
||||
await vm.ValueConverters.Task;
|
||||
Assert.That (vm.ValueConverters.Value, Contains.Item (visi));
|
||||
Assert.That (vm.ValueConverters.Value.Count, Is.EqualTo (3)); // visi, No Converter, Request Converter
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RequestValueConverter ()
|
||||
{
|
||||
var target = new object();
|
||||
|
||||
var vm = CreateBasicViewModel (target: target);
|
||||
Assume.That (vm.ValueConverters, Is.Not.Null);
|
||||
|
||||
await vm.ValueConverters.Task;
|
||||
|
||||
Assume.That (vm.SelectedValueConverter, Is.EqualTo (vm.ValueConverters.Value[0]));
|
||||
Assume.That (vm.ValueConverters.Value.Count, Is.EqualTo (2));
|
||||
|
||||
const string name = "NewConverter";
|
||||
bool requested = false;
|
||||
vm.CreateValueConverterRequested += (o, e) => {
|
||||
e.ConverterType = typeof(object).ToTypeInfo ();
|
||||
e.Name = name;
|
||||
e.Source = MockResourceProvider.ApplicationResourcesSource;
|
||||
requested = true;
|
||||
};
|
||||
|
||||
Assume.That (vm.ValueConverters.Value, Is.InstanceOf (typeof(INotifyCollectionChanged)));
|
||||
|
||||
object newItem = null;
|
||||
((INotifyCollectionChanged) vm.ValueConverters.Value).CollectionChanged += (o, e) => {
|
||||
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count == 1) {
|
||||
newItem = e.NewItems[0];
|
||||
Assert.That (e.NewStartingIndex, Is.EqualTo (1), "New converter was not added below none and above request");
|
||||
}
|
||||
};
|
||||
|
||||
int selectedChanged = 0;
|
||||
vm.PropertyChanged += (o, e) => {
|
||||
if (e.PropertyName == nameof(CreateBindingViewModel.SelectedValueConverter))
|
||||
selectedChanged++;
|
||||
};
|
||||
|
||||
vm.SelectedValueConverter = vm.ValueConverters.Value[1];
|
||||
|
||||
Assert.That (requested, Is.True);
|
||||
Assert.That (selectedChanged, Is.EqualTo (2), "SelectedValueConverter did not fire INPC for request and result changes");
|
||||
Assert.That (newItem, Is.Not.Null);
|
||||
Assert.That (vm.SelectedValueConverter, Is.EqualTo (newItem));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task RequestValueConverterCanceled ()
|
||||
{
|
||||
var vm = CreateBasicViewModel ();
|
||||
Assume.That (vm.ValueConverters, Is.Not.Null);
|
||||
|
||||
await vm.ValueConverters.Task;
|
||||
|
||||
Assume.That (vm.SelectedValueConverter, Is.EqualTo (vm.ValueConverters.Value[0]));
|
||||
Assume.That (vm.ValueConverters.Value.Count, Is.EqualTo (2));
|
||||
|
||||
bool requested = false;
|
||||
vm.CreateValueConverterRequested += (o, e) => {
|
||||
requested = true;
|
||||
};
|
||||
|
||||
vm.SelectedValueConverter = vm.ValueConverters.Value[1];
|
||||
|
||||
Assert.That (requested, Is.True);
|
||||
Assert.That (vm.SelectedValueConverter, Is.EqualTo (vm.ValueConverters.Value[0]),
|
||||
"SelectedValueConverter wasn't set back to No Converter after canceled request");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PropertyRoot ()
|
||||
{
|
||||
var target = new object ();
|
||||
|
||||
var property = new Mock<IPropertyInfo> ();
|
||||
property.SetupGet (p => p.ValueSources).Returns (ValueSources.Local | ValueSources.Binding);
|
||||
property.SetupGet (p => p.Type).Returns (typeof (object));
|
||||
property.SetupGet (p => p.Name).Returns ("name");
|
||||
property.SetupGet (p => p.RealType).Returns (typeof (object).ToTypeInfo ());
|
||||
property.SetupGet (p => p.CanWrite).Returns (true);
|
||||
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
|
||||
var controlTarget = new MockWpfControl ();
|
||||
var controlEditor = new MockObjectEditor (controlTarget);
|
||||
var provider = new MockEditorProvider (controlEditor);
|
||||
|
||||
var source = new BindingSource ("Control", BindingSourceType.Object);
|
||||
var bpmock = new Mock<IBindingProvider> ();
|
||||
bpmock.Setup (bp => bp.GetBindingSourcesAsync (target, property.Object)).ReturnsAsync (new[] { source });
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (source, target)).ReturnsAsync (new[] { controlTarget });
|
||||
bpmock.Setup (bp => bp.GetValueConverterResourcesAsync (It.IsAny<object> ())).ReturnsAsync (new Resource[0]);
|
||||
|
||||
var vm = new CreateBindingViewModel (new TargetPlatform (provider, bpmock.Object), editor.Object, property.Object);
|
||||
Assume.That (vm.SelectedBindingSource, Is.EqualTo (source));
|
||||
|
||||
Assert.That (vm.PropertyRoot, Is.Not.Null);
|
||||
await vm.PropertyRoot.Task;
|
||||
|
||||
var targetType = typeof(MockWpfControl).ToTypeInfo ();
|
||||
Assert.That (vm.PropertyRoot.Value.TargetType, Is.EqualTo (targetType));
|
||||
CollectionAssert.AreEqual (controlEditor.Properties, vm.PropertyRoot.Value.Children.Select (te => te.Property));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task PropertyRootChildren ()
|
||||
{
|
||||
var target = new object();
|
||||
|
||||
var property = new Mock<IPropertyInfo> ();
|
||||
property.SetupGet (p => p.ValueSources).Returns (ValueSources.Local | ValueSources.Binding);
|
||||
property.SetupGet (p => p.Type).Returns (typeof (object));
|
||||
property.SetupGet (p => p.Name).Returns ("name");
|
||||
property.SetupGet (p => p.RealType).Returns (typeof (object).ToTypeInfo ());
|
||||
property.SetupGet (p => p.CanWrite).Returns (true);
|
||||
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
|
||||
var controlTarget = new MockWpfControl();
|
||||
var controlEditor = new MockObjectEditor (controlTarget);
|
||||
var provider = new MockEditorProvider (controlEditor);
|
||||
|
||||
var source = new BindingSource ("Control", BindingSourceType.Object);
|
||||
var bpmock = new Mock<IBindingProvider> ();
|
||||
bpmock.Setup (bp => bp.GetBindingSourcesAsync (target, property.Object)).ReturnsAsync (new[] { source });
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (source, target)).ReturnsAsync (new[] { controlTarget });
|
||||
bpmock.Setup (bp => bp.GetValueConverterResourcesAsync (It.IsAny<object> ())).ReturnsAsync (new Resource[0]);
|
||||
|
||||
var vm = new CreateBindingViewModel (new TargetPlatform (provider, bpmock.Object), editor.Object, property.Object);
|
||||
Assume.That (vm.SelectedBindingSource, Is.EqualTo (source));
|
||||
Assume.That (vm.PropertyRoot, Is.Not.Null);
|
||||
await vm.PropertyRoot.Task;
|
||||
|
||||
var childrenProperty = controlEditor.Properties.First (p => p.Type == typeof(CommonThickness));
|
||||
var element = vm.PropertyRoot.Value.Children.First (p => Equals (p.Property, childrenProperty));
|
||||
|
||||
Assert.That (element.Children, Is.Not.Null);
|
||||
|
||||
await element.Children.Task;
|
||||
var expected = await provider.GetPropertiesForTypeAsync (typeof(CommonThickness).ToTypeInfo ());
|
||||
CollectionAssert.AreEqual (expected, element.Children.Value.Select (te => te.Property));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Path ()
|
||||
{
|
||||
var target = new object ();
|
||||
|
||||
var property = new Mock<IPropertyInfo> ();
|
||||
property.SetupGet (p => p.ValueSources).Returns (ValueSources.Local | ValueSources.Binding);
|
||||
property.SetupGet (p => p.Type).Returns (typeof (object));
|
||||
property.SetupGet (p => p.Name).Returns ("name");
|
||||
property.SetupGet (p => p.RealType).Returns (typeof (object).ToTypeInfo ());
|
||||
property.SetupGet (p => p.CanWrite).Returns (true);
|
||||
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
|
||||
var controlTarget = new MockWpfControl ();
|
||||
var controlEditor = new MockObjectEditor (controlTarget);
|
||||
var provider = new MockEditorProvider (controlEditor);
|
||||
|
||||
var source = new BindingSource ("Control", BindingSourceType.Object);
|
||||
var bpmock = new Mock<IBindingProvider> ();
|
||||
bpmock.Setup (bp => bp.GetBindingSourcesAsync (target, property.Object)).ReturnsAsync (new[] { source });
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (source, target)).ReturnsAsync (new[] { controlTarget });
|
||||
bpmock.Setup (bp => bp.GetValueConverterResourcesAsync (It.IsAny<object> ())).ReturnsAsync (new Resource[0]);
|
||||
|
||||
var vm = new CreateBindingViewModel (new TargetPlatform (provider, bpmock.Object), editor.Object, property.Object);
|
||||
Assume.That (vm.SelectedBindingSource, Is.EqualTo (source));
|
||||
Assume.That (vm.PropertyRoot, Is.Not.Null);
|
||||
await vm.PropertyRoot.Task;
|
||||
|
||||
var element = vm.PropertyRoot.Value.Children.First (p => p.Property.Type == typeof(CommonThickness));
|
||||
await element.Children.Task;
|
||||
var sub = element.Children.Value.First ();
|
||||
vm.SelectedPropertyElement = sub;
|
||||
|
||||
Assert.That (vm.Path, Is.EqualTo ($"{element.Property.Name}.{sub.Property.Name}"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SelectedBindingSource ()
|
||||
{
|
||||
BindingSource[] sources = new[] {
|
||||
new BindingSource ("First", BindingSourceType.Object),
|
||||
new BindingSource ("Second", BindingSourceType.Resource),
|
||||
new BindingSource ("Third", BindingSourceType.Type),
|
||||
};
|
||||
|
||||
var vm = CreateBasicViewModel (sources);
|
||||
|
||||
while (vm.SelectedObjects.Count == 0) {
|
||||
await Task.Delay (1);
|
||||
}
|
||||
|
||||
var binding = (MockBinding)vm.SelectedObjects.First ();
|
||||
|
||||
Assert.That (vm.SelectedBindingSource, Is.EqualTo (sources[0]));
|
||||
Assert.That (binding.Source, Is.EqualTo (sources[0]), "Backing binding object property didn't update");
|
||||
|
||||
bool propertyChanged = false, objectRootsChanged = false, resourceRootsChanged = false, typeRootsChanged = false;
|
||||
vm.PropertyChanged += (o, e) => {
|
||||
if (e.PropertyName == nameof(CreateBindingViewModel.SelectedBindingSource))
|
||||
propertyChanged = true;
|
||||
else if (e.PropertyName == nameof(CreateBindingViewModel.ObjectElementRoots))
|
||||
objectRootsChanged = true;
|
||||
else if (e.PropertyName == nameof(CreateBindingViewModel.SourceResources))
|
||||
resourceRootsChanged = true;
|
||||
else if (e.PropertyName == nameof(CreateBindingViewModel.TypeSelector))
|
||||
typeRootsChanged = true;
|
||||
};
|
||||
|
||||
vm.SelectedBindingSource = sources[1];
|
||||
Assert.That (propertyChanged, Is.True, "INPC did not fire for SelectedBindingSource");
|
||||
Assert.That (binding.Source, Is.EqualTo (sources[1]), "Backing binding object property didn't update");
|
||||
Assert.That (resourceRootsChanged, Is.True, "SourceResources did not update when selected");
|
||||
Assert.That (vm.SourceResources, Is.Not.Null);
|
||||
Assert.That (objectRootsChanged, Is.False);
|
||||
Assert.That (typeRootsChanged, Is.False);
|
||||
|
||||
propertyChanged = objectRootsChanged = resourceRootsChanged = typeRootsChanged = false;
|
||||
vm.SelectedBindingSource = sources[2];
|
||||
Assert.That (propertyChanged, Is.True, "INPC did not fire for SelectedBindingSource");
|
||||
Assert.That (binding.Source, Is.EqualTo (sources[2]), "Backing binding object property didn't update");
|
||||
Assert.That (resourceRootsChanged, Is.False);
|
||||
Assert.That (objectRootsChanged, Is.False);
|
||||
Assert.That (typeRootsChanged, Is.True, "TypeSelector didn't update when selected");
|
||||
Assert.That (vm.TypeSelector, Is.Not.Null);
|
||||
|
||||
propertyChanged = objectRootsChanged = resourceRootsChanged = typeRootsChanged = false;
|
||||
vm.SelectedBindingSource = sources[0];
|
||||
Assert.That (propertyChanged, Is.True, "INPC did not fire for SelectedBindingSource");
|
||||
Assert.That (binding.Source, Is.EqualTo (sources[0]), "Backing binding object property didn't update");
|
||||
Assert.That (resourceRootsChanged, Is.False);
|
||||
Assert.That (objectRootsChanged, Is.True, "ObjectElementRoots didn't update when selected");
|
||||
Assert.That (vm.ObjectElementRoots, Is.Not.Null);
|
||||
Assert.That (typeRootsChanged, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ShowSourceParameterSelectors ()
|
||||
{
|
||||
BindingSource[] sources = new[] {
|
||||
new BindingSource ("First", BindingSourceType.Object),
|
||||
new BindingSource ("Second", BindingSourceType.Resource),
|
||||
new BindingSource ("Third", BindingSourceType.Type),
|
||||
};
|
||||
|
||||
var vm = CreateBasicViewModel (sources);
|
||||
|
||||
while (vm.SelectedObjects.Count == 0) {
|
||||
await Task.Delay (1);
|
||||
}
|
||||
|
||||
Assume.That (vm.SelectedBindingSource, Is.EqualTo (sources[0]));
|
||||
|
||||
bool objectChanged = false, resourceChanged = false, typeChanged = false;
|
||||
vm.PropertyChanged += (o, e) => {
|
||||
if (e.PropertyName == nameof (CreateBindingViewModel.ShowObjectSelector))
|
||||
objectChanged = true;
|
||||
else if (e.PropertyName == nameof (CreateBindingViewModel.ShowResourceSelector))
|
||||
resourceChanged = true;
|
||||
else if (e.PropertyName == nameof (CreateBindingViewModel.ShowTypeSelector))
|
||||
typeChanged = true;
|
||||
};
|
||||
|
||||
vm.SelectedBindingSource = sources[1];
|
||||
Assert.That (resourceChanged, Is.True);
|
||||
Assert.That (vm.ShowResourceSelector, Is.True);
|
||||
Assert.That (objectChanged, Is.True, "Did not signal old selector changed");
|
||||
Assert.That (vm.ShowObjectSelector, Is.False);
|
||||
Assert.That (vm.ShowTypeSelector, Is.False);
|
||||
|
||||
objectChanged = resourceChanged = typeChanged = false;
|
||||
vm.SelectedBindingSource = sources[2];
|
||||
Assert.That (resourceChanged, Is.True);
|
||||
Assert.That (vm.ShowResourceSelector, Is.False);
|
||||
Assert.That (objectChanged, Is.True, "Did not signal old selector changed");
|
||||
Assert.That (vm.ShowObjectSelector, Is.False);
|
||||
Assert.That (vm.ShowTypeSelector, Is.True);
|
||||
|
||||
objectChanged = resourceChanged = typeChanged = false;
|
||||
vm.SelectedBindingSource = sources[0];
|
||||
Assert.That (vm.ShowResourceSelector, Is.False);
|
||||
Assert.That (objectChanged, Is.True);
|
||||
Assert.That (vm.ShowObjectSelector, Is.True);
|
||||
Assert.That (typeChanged, Is.True, "Did not signal old selector changed");
|
||||
Assert.That (vm.ShowTypeSelector, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ResourceRoots ()
|
||||
{
|
||||
object target = new object();
|
||||
var property = GetBasicProperty ();
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
var resources = new MockResourceProvider ();
|
||||
var bindings = GetBasicBindingProvider (target, property.Object);
|
||||
var source = new BindingSource ("Resources", BindingSourceType.Resource);
|
||||
bindings.Setup (bp => bp.GetBindingSourcesAsync (target, property.Object)).ReturnsAsync (new[] { source });
|
||||
bindings.Setup (bp => bp.GetResourcesAsync (source, target))
|
||||
.Returns<BindingSource,object> (async (bs, t) => {
|
||||
var rs = await resources.GetResourcesAsync (target, CancellationToken.None);
|
||||
return rs.ToLookup (r => r.Source);
|
||||
});
|
||||
|
||||
var vm = new CreateBindingViewModel (
|
||||
new TargetPlatform (new MockEditorProvider (editor.Object), resources, bindings.Object), editor.Object,
|
||||
property.Object);
|
||||
|
||||
Assume.That (vm.SelectedBindingSource, Is.EqualTo (source));
|
||||
|
||||
Assert.That (vm.SourceResources, Is.Not.Null);
|
||||
await vm.SourceResources.Task;
|
||||
Assert.That (vm.SourceResources.Value.First().Key, Is.EqualTo (DefaultResourceSources[0]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ResourceProperties ()
|
||||
{
|
||||
object target = new object ();
|
||||
var property = GetBasicProperty ();
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
var resources = new MockResourceProvider ();
|
||||
var source = new BindingSource ("Resources", BindingSourceType.Resource);
|
||||
var bindings = GetBasicBindingProvider (target, property.Object, sources: new [] { source });
|
||||
bindings.Setup (bp => bp.GetResourcesAsync (source, target))
|
||||
.Returns<BindingSource, object> (async (bs, t) => {
|
||||
var rs = await resources.GetResourcesAsync (target, CancellationToken.None);
|
||||
return rs.ToLookup (r => r.Source);
|
||||
});
|
||||
|
||||
var vm = new CreateBindingViewModel (
|
||||
new TargetPlatform (new MockEditorProvider (editor.Object), resources, bindings.Object), editor.Object,
|
||||
property.Object);
|
||||
|
||||
Assume.That (vm.SelectedBindingSource, Is.EqualTo (source));
|
||||
Assume.That (vm.SourceResources, Is.Not.Null);
|
||||
await vm.SourceResources.Task;
|
||||
|
||||
while (vm.SelectedObjects.Count == 0) {
|
||||
await Task.Delay (1);
|
||||
}
|
||||
|
||||
var binding = (MockBinding)vm.SelectedObjects.First();
|
||||
vm.SelectedResource = vm.SourceResources.Value.First ().OfType<Resource<CommonSolidBrush>>().First ();
|
||||
Assert.That (binding.SourceParameter, Is.EqualTo (vm.SelectedResource));
|
||||
Assume.That (vm.PropertyRoot, Is.Not.Null);
|
||||
await vm.PropertyRoot.Task;
|
||||
|
||||
Assert.That (vm.PropertyRoot.Value.TargetType, Is.EqualTo (typeof(CommonSolidBrush).ToTypeInfo ()));
|
||||
CollectionAssert.AreEqual (ReflectionEditorProvider.GetPropertiesForType (typeof(CommonSolidBrush)),
|
||||
vm.PropertyRoot.Value.Children.Select (pe => pe.Property));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Types ()
|
||||
{
|
||||
object target = new object ();
|
||||
var property = GetBasicProperty ();
|
||||
var editor = GetBasicEditor (target, property.Object);
|
||||
var resources = new MockResourceProvider ();
|
||||
var source = new BindingSource ("Resources", BindingSourceType.Type);
|
||||
|
||||
var type = typeof(MockSampleControl).ToTypeInfo ();
|
||||
var bindings = GetBasicBindingProvider (target, property.Object, sources: new[] { source });
|
||||
bindings.Setup (bp => bp.GetSourceTypesAsync (source, target))
|
||||
.ReturnsAsync (new AssignableTypesResult (new[] { type }));
|
||||
|
||||
var provider = new MockEditorProvider (editor.Object);
|
||||
var vm = new CreateBindingViewModel (new TargetPlatform (provider, resources, bindings.Object), editor.Object, property.Object);
|
||||
|
||||
Assume.That (vm.SelectedBindingSource, Is.EqualTo (source));
|
||||
Assume.That (vm.TypeSelector, Is.Not.Null);
|
||||
|
||||
while (vm.SelectedObjects.Count == 0 && vm.TypeSelector.IsLoading) {
|
||||
await Task.Delay (1);
|
||||
}
|
||||
|
||||
var binding = (MockBinding) vm.SelectedObjects.First ();
|
||||
|
||||
Assert.That (vm.TypeSelector.SelectedType, Is.Null);
|
||||
|
||||
bool propertyChanged = false;
|
||||
vm.PropertyChanged += (o, e) => {
|
||||
if (e.PropertyName == nameof(CreateBindingViewModel.PropertyRoot))
|
||||
propertyChanged = true;
|
||||
};
|
||||
|
||||
vm.TypeSelector.SelectedType = type;
|
||||
Assert.That (propertyChanged, Is.True, "INPC didn't change for PropertyRoot on selected source param");
|
||||
Assert.That (vm.PropertyRoot, Is.Not.Null);
|
||||
Assert.That (binding.SourceParameter, Is.EqualTo (type));
|
||||
|
||||
await vm.PropertyRoot.Task;
|
||||
CollectionAssert.AreEqual (provider.GetPropertiesForTypeAsync (type).Result,
|
||||
vm.PropertyRoot.Value.Children.Select (pe => pe.Property));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ExtraProperties ()
|
||||
{
|
||||
var provider = new MockEditorProvider();
|
||||
var vm = CreateBasicViewModel ();
|
||||
|
||||
var editors = (IList<IObjectEditor>)typeof(CreateBindingViewModel).GetProperty ("ObjectEditors", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.GetValue (vm); // Shortcut
|
||||
var editor = editors[0];
|
||||
|
||||
IEnumerable<IPropertyInfo> properties = (await provider.GetPropertiesForTypeAsync (typeof(MockBinding).ToTypeInfo ()));
|
||||
CollectionAssert.AreEqual (properties, vm.Properties.Cast<PropertyViewModel> ().Select (pvm => pvm.Property));
|
||||
|
||||
properties = properties.Where (p => !editor.KnownProperties.ContainsKey (p));
|
||||
CollectionAssert.AreEqual (properties.Where (p => p.Type == typeof(bool)),
|
||||
vm.FlagsProperties.Cast<PropertyViewModel> ().Select (pvm => pvm.Property));
|
||||
CollectionAssert.AreEqual (properties.Where (p => p.Type != typeof (bool)),
|
||||
vm.BindingProperties.Cast<PropertyViewModel> ().Select (pvm => pvm.Property));
|
||||
}
|
||||
|
||||
private AsyncSynchronizationContext syncContext;
|
||||
private static readonly ResourceSource[] DefaultResourceSources = new[] { MockResourceProvider.SystemResourcesSource, MockResourceProvider.ApplicationResourcesSource };
|
||||
|
||||
private Mock<IPropertyInfo> GetBasicProperty (string name = "propertyName")
|
||||
{
|
||||
var property = new Mock<IPropertyInfo> ();
|
||||
property.SetupGet (p => p.ValueSources).Returns (ValueSources.Local | ValueSources.Binding);
|
||||
property.SetupGet (p => p.Type).Returns (typeof (string));
|
||||
property.SetupGet (p => p.Name).Returns (name);
|
||||
property.SetupGet (p => p.RealType).Returns (typeof (string).ToTypeInfo ());
|
||||
property.SetupGet (p => p.CanWrite).Returns (true);
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
private Mock<IObjectEditor> GetBasicEditor (object target, IPropertyInfo property)
|
||||
{
|
||||
var editor = new Mock<IObjectEditor> ();
|
||||
editor.SetupGet (e => e.Properties).Returns (new[] { property });
|
||||
editor.SetupGet (e => e.Target).Returns (target);
|
||||
editor.SetupGet (e => e.TargetType).Returns (typeof (object).ToTypeInfo ());
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
private Mock<IResourceProvider> GetBasicResourceProvider (object target, ResourceSource[] sources = null)
|
||||
{
|
||||
sources = sources ?? DefaultResourceSources;
|
||||
|
||||
var resources = new Mock<IResourceProvider> ();
|
||||
resources.Setup (r => r.GetResourceSourcesAsync (target)).ReturnsAsync (sources);
|
||||
resources.Setup (r => r.CreateResourceAsync (It.IsAny<ResourceSource> (), It.IsAny<string> (), It.IsAny<object> ()))
|
||||
.ReturnsAsync ((Func<ResourceSource, string, object, Resource>) ((s, n, v) => new Resource (s, n)));
|
||||
return resources;
|
||||
}
|
||||
|
||||
private Mock<IBindingProvider> GetBasicBindingProvider (object target, IPropertyInfo property, BindingSource[] sources = null)
|
||||
{
|
||||
var bpmock = new Mock<IBindingProvider> ();
|
||||
|
||||
if (sources == null) {
|
||||
sources = new[] {
|
||||
new BindingSource ("Short Description", BindingSourceType.Object, "Short Description"),
|
||||
new BindingSource ("Long Description", BindingSourceType.SingleObject, "Long Description"),
|
||||
};
|
||||
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[0], target)).ReturnsAsync (new[] { new object (), new object () });
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[1], target)).ReturnsAsync (new[] { new object () });
|
||||
} else {
|
||||
for (int i = 0; i < sources.Length; i++) {
|
||||
int index = i;
|
||||
if (sources[i].Type == BindingSourceType.SingleObject)
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[index], target)).ReturnsAsync (new[] { new object () });
|
||||
else
|
||||
bpmock.Setup (bp => bp.GetRootElementsAsync (sources[index], target)).ReturnsAsync (new[] { new object (), new object() });
|
||||
}
|
||||
}
|
||||
|
||||
bpmock.Setup (bp => bp.GetBindingSourcesAsync (target, property)).ReturnsAsync (sources);
|
||||
bpmock.Setup (bp => bp.GetValueConverterResourcesAsync (It.IsAny<object> ())).ReturnsAsync (new Resource[0]);
|
||||
return bpmock;
|
||||
}
|
||||
|
||||
private CreateBindingViewModel CreateBasicViewModel (BindingSource[] sources = null, object target = null)
|
||||
{
|
||||
target = target ?? new object ();
|
||||
Mock<IPropertyInfo> property = GetBasicProperty ();
|
||||
Mock<IObjectEditor> editor = GetBasicEditor (target, property.Object);
|
||||
|
||||
var editorProvider = new MockEditorProvider (editor.Object);
|
||||
Mock<IResourceProvider> resourceProvider = GetBasicResourceProvider (target);
|
||||
Mock<IBindingProvider> bpmock = GetBasicBindingProvider (target, property.Object, sources);
|
||||
|
||||
return new CreateBindingViewModel (new TargetPlatform (editorProvider, resourceProvider.Object, bpmock.Object), editor.Object, property.Object);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
<<<<<<< HEAD
|
||||
using System.Linq;
|
||||
=======
|
||||
>>>>>>> [Core/Win] Create resource core/window
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.PropertyEditing.Reflection;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Tests
|
||||
{
|
||||
internal class MockBindingEditor
|
||||
: IObjectEditor
|
||||
{
|
||||
public MockBindingEditor (MockBinding binding)
|
||||
{
|
||||
Target = binding;
|
||||
this.editor = new ReflectionObjectEditor (binding);
|
||||
this.editor.PropertyChanged += (sender, args) => {
|
||||
PropertyChanged?.Invoke (this, args);
|
||||
};
|
||||
|
||||
KnownProperties = new Dictionary<IPropertyInfo, KnownProperty> {
|
||||
{ this.editor.Properties.Single (pi => pi.Name == nameof(MockBinding.Source)), PropertyBinding.SourceProperty },
|
||||
{ this.editor.Properties.Single (pi => pi.Name == nameof(MockBinding.SourceParameter)), PropertyBinding.SourceParameterProperty },
|
||||
{ this.editor.Properties.Single (pi => pi.Name == nameof(MockBinding.Path)), PropertyBinding.PathProperty },
|
||||
{ this.editor.Properties.Single (pi => pi.Name == nameof(MockBinding.Converter)), PropertyBinding.ConverterProperty },
|
||||
{ this.editor.Properties.Single (pi => pi.Name == nameof(MockBinding.TypeLevel)), PropertyBinding.TypeLevelProperty }
|
||||
};
|
||||
}
|
||||
|
||||
public event EventHandler<EditorPropertyChangedEventArgs> PropertyChanged;
|
||||
|
||||
public object Target
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ITypeInfo TargetType => this.editor.TargetType;
|
||||
|
||||
public IReadOnlyCollection<IPropertyInfo> Properties => this.editor.Properties;
|
||||
|
||||
public IReadOnlyDictionary<IPropertyInfo, KnownProperty> KnownProperties
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public IObjectEditor Parent => null;
|
||||
|
||||
public IReadOnlyList<IObjectEditor> DirectChildren => null;
|
||||
|
||||
public Task<AssignableTypesResult> GetAssignableTypesAsync (IPropertyInfo property, bool childTypes)
|
||||
{
|
||||
return this.editor.GetAssignableTypesAsync (property, childTypes);
|
||||
}
|
||||
|
||||
public Task SetValueAsync<T> (IPropertyInfo property, ValueInfo<T> value, PropertyVariation variation = null)
|
||||
{
|
||||
return this.editor.SetValueAsync (property, value, variation);
|
||||
}
|
||||
|
||||
public Task<ValueInfo<T>> GetValueAsync<T> (IPropertyInfo property, PropertyVariation variation = null)
|
||||
{
|
||||
return this.editor.GetValueAsync<T> (property, variation);
|
||||
}
|
||||
|
||||
private readonly ReflectionObjectEditor editor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.PropertyEditing.Reflection;
|
||||
using Xamarin.PropertyEditing.Tests.MockControls;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Tests
|
||||
{
|
||||
public class MockBindingProvider
|
||||
: IBindingProvider
|
||||
{
|
||||
public Task<IReadOnlyList<BindingSource>> GetBindingSourcesAsync (object target, IPropertyInfo property)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<BindingSource>> (new[] {
|
||||
new BindingSourceInstance (RelativeSelf, $"Bind [{target.GetType().Name}] to itself."),
|
||||
StaticResource,
|
||||
Ancestor
|
||||
});
|
||||
}
|
||||
|
||||
public Task<AssignableTypesResult> GetSourceTypesAsync (BindingSource source, object target)
|
||||
{
|
||||
return ReflectionObjectEditor.GetAssignableTypes (typeof(MockControl).ToTypeInfo (), childTypes: false);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<object>> GetRootElementsAsync (BindingSource source, object target)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException (nameof(source));
|
||||
if (source.Type != BindingSourceType.Object && source.Type != BindingSourceType.SingleObject)
|
||||
throw new ArgumentException ("source.Type was not Object", nameof(source));
|
||||
|
||||
if (source is BindingSourceInstance instance)
|
||||
source = instance.Original;
|
||||
|
||||
if (source == RelativeSelf)
|
||||
return Task.FromResult<IReadOnlyList<object>> (new [] { target });
|
||||
if (source == StaticResource)
|
||||
return MockResourceProvider.GetResourceSourcesAsync (target).ContinueWith (t => (IReadOnlyList<object>) t.Result);
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<ILookup<ResourceSource, Resource>> GetResourcesAsync (BindingSource source, object target)
|
||||
{
|
||||
var results = await this.resources.GetResourcesAsync (target, CancellationToken.None).ConfigureAwait (false);
|
||||
return results.ToLookup (r => r.Source);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<Resource>> GetValueConverterResourcesAsync (object target)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<Resource>> (Array.Empty<Resource> ());
|
||||
}
|
||||
|
||||
private readonly MockResourceProvider resources = new MockResourceProvider();
|
||||
|
||||
private static readonly BindingSource Ancestor = new BindingSource ("RelativeSource FindAncestor", BindingSourceType.Type);
|
||||
private static readonly BindingSource RelativeSelf = new BindingSource ("RelativeSource Self", BindingSourceType.SingleObject);
|
||||
private static readonly BindingSource StaticResource = new BindingSource ("StaticResource", BindingSourceType.Resource);
|
||||
|
||||
private class BindingSourceInstance
|
||||
: BindingSource
|
||||
{
|
||||
public BindingSourceInstance (BindingSource original, string description)
|
||||
: base (original.Name, original.Type, description)
|
||||
{
|
||||
Original = original;
|
||||
}
|
||||
|
||||
public BindingSource Original
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MockBinding
|
||||
{
|
||||
public string Path
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public BindingSource Source
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public object SourceParameter
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Resource Converter
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string StringFormat
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool IsAsync
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int TypeLevel
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
|
|||
public void AddProperty<T> (string name, string category = null,
|
||||
bool canWrite = true, bool flag = false,
|
||||
IEnumerable<Type> converterTypes = null,
|
||||
string description = null, ValueSources valueSources = ValueSources.Local | ValueSources.Default)
|
||||
string description = null, ValueSources valueSources = ValueSources.Local | ValueSources.Default | ValueSources.Binding)
|
||||
{
|
||||
IPropertyInfo propertyInfo;
|
||||
if (typeof(T).IsEnum) {
|
||||
|
|
|
@ -45,6 +45,13 @@ namespace Xamarin.PropertyEditing.Tests
|
|||
return Task.FromResult<Resource> (r);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<Resource>> GetResourcesAsync (object target, CancellationToken cancelToken)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<Resource>> (this.resources.SelectMany (g => g)
|
||||
.Where (r => !(r.Source is ObjectResourceSource ors) || ReferenceEquals (target, ors.Target))
|
||||
.ToList ());
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<Resource>> GetResourcesAsync (object target, IPropertyInfo property, CancellationToken cancelToken)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<Resource>> (this.resources.SelectMany (g => g)
|
||||
|
@ -52,7 +59,17 @@ namespace Xamarin.PropertyEditing.Tests
|
|||
.ToList());
|
||||
}
|
||||
|
||||
Task<IReadOnlyList<ResourceSource>> IResourceProvider.GetResourceSourcesAsync (object target)
|
||||
{
|
||||
return MockResourceProvider.GetResourceSourcesAsync (target);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<ResourceSource>> GetResourceSourcesAsync (object target, IPropertyInfo property)
|
||||
{
|
||||
return GetResourceSourcesAsync (target);
|
||||
}
|
||||
|
||||
public static Task<IReadOnlyList<ResourceSource>> GetResourceSourcesAsync (object target)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<ResourceSource>> (new[] { SystemResourcesSource, ApplicationResourcesSource, Resources, Window, new ObjectResourceSource (target, target.GetType ().Name, ResourceSourceType.Document) });
|
||||
}
|
||||
|
@ -118,6 +135,15 @@ namespace Xamarin.PropertyEditing.Tests
|
|||
new Resource<CommonSolidBrush> (SystemResourcesSource, "ControlTextBrush", new CommonSolidBrush (0, 0, 0)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "HighlightBrush", new CommonSolidBrush (51, 153, 255)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "TransparentBrush", new CommonSolidBrush (0, 0, 0, 0)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "ATextBrush", new CommonSolidBrush (0, 0, 0)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "ATransparentBrush", new CommonSolidBrush (51, 153, 255)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "AHighlightBrush", new CommonSolidBrush (0, 0, 0, 0)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "BTextBrush", new CommonSolidBrush (0, 0, 0)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "BHighlightBrush", new CommonSolidBrush (51, 153, 255)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "BTransparentBrush", new CommonSolidBrush (0, 0, 0, 0)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "CTextBrush", new CommonSolidBrush (0, 0, 0)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "CHighlightBrush", new CommonSolidBrush (51, 153, 255)),
|
||||
new Resource<CommonSolidBrush> (SystemResourcesSource, "CTransparentBrush", new CommonSolidBrush (0, 0, 0, 0)),
|
||||
new Resource<CommonColor> (SystemResourcesSource, "ControlTextColor", new CommonColor (0, 0, 0)),
|
||||
new Resource<CommonColor> (SystemResourcesSource, "HighlightColor", new CommonColor (51, 153, 255))
|
||||
},
|
||||
|
|
|
@ -8,12 +8,12 @@ namespace Xamarin.PropertyEditing.Tests.MockControls
|
|||
{
|
||||
public MockSampleControl()
|
||||
{
|
||||
AddProperty<bool> ("Boolean", ReadWrite);
|
||||
AddProperty<bool> ("Boolean", ReadWrite, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding);
|
||||
AddProperty<bool> ("UnsetBoolean", ReadWrite, valueSources: ValueSources.Local);
|
||||
AddProperty<int> ("Integer", ReadWrite);
|
||||
AddProperty<int> ("UnsetInteger", ReadWrite, valueSources: ValueSources.Local);
|
||||
AddProperty<float> ("FloatingPoint", ReadWrite);
|
||||
AddProperty<string> ("String", ReadWrite);
|
||||
AddProperty<string> ("String", ReadWrite, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding);
|
||||
AddProperty<Enumeration> ("Enumeration", ReadWrite);
|
||||
AddProperty<FlagsNoValues> ("FlagsNoValues", ReadWrite, canWrite: true, flag: true);
|
||||
AddProperty<FlagsWithValues> ("FlagsWithValues", ReadWrite, canWrite: true, flag: true);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.PropertyEditing.Common;
|
||||
using Xamarin.PropertyEditing.Drawing;
|
||||
using Xamarin.PropertyEditing.Reflection;
|
||||
using Xamarin.PropertyEditing.Tests.MockControls;
|
||||
|
||||
|
@ -11,6 +13,26 @@ namespace Xamarin.PropertyEditing.Tests
|
|||
{
|
||||
public static readonly TargetPlatform MockPlatform = new TargetPlatform (new MockEditorProvider ());
|
||||
|
||||
public MockEditorProvider ()
|
||||
{
|
||||
}
|
||||
|
||||
public MockEditorProvider (IObjectEditor editor)
|
||||
{
|
||||
this.editorCache.Add (editor.Target, editor);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Type, ITypeInfo> KnownTypes
|
||||
{
|
||||
get;
|
||||
} = new Dictionary<Type, ITypeInfo> {
|
||||
{ typeof(PropertyBinding), typeof(MockBinding).ToTypeInfo() },
|
||||
{ typeof(CommonValueConverter), typeof(MockValueConverter).ToTypeInfo() },
|
||||
{ typeof(CommonBrush), typeof(CommonBrush).ToTypeInfo() },
|
||||
{ typeof(CommonSolidBrush), typeof(CommonSolidBrush).ToTypeInfo() },
|
||||
{ typeof(CommonColor), typeof(CommonColor).ToTypeInfo() }
|
||||
};
|
||||
|
||||
public Task<IObjectEditor> GetObjectEditorAsync (object item)
|
||||
{
|
||||
if (this.editorCache.TryGetValue (item, out IObjectEditor cachedEditor)) {
|
||||
|
@ -23,9 +45,25 @@ namespace Xamarin.PropertyEditing.Tests
|
|||
|
||||
public async Task<IReadOnlyCollection<IPropertyInfo>> GetPropertiesForTypeAsync (ITypeInfo type)
|
||||
{
|
||||
object obj = await CreateObjectAsync (type).ConfigureAwait (false);
|
||||
IObjectEditor editor = ChooseEditor (obj);
|
||||
return editor.Properties;
|
||||
Type realType = ReflectionEditorProvider.GetRealType (type);
|
||||
if (realType == null)
|
||||
return Array.Empty<IPropertyInfo> ();
|
||||
|
||||
if (typeof(MockControl).IsAssignableFrom (realType)) {
|
||||
object item = await CreateObjectAsync (type);
|
||||
IObjectEditor editor = ChooseEditor (item);
|
||||
return editor.Properties;
|
||||
}
|
||||
|
||||
return ReflectionEditorProvider.GetPropertiesForType (realType);
|
||||
}
|
||||
|
||||
public Task<AssignableTypesResult> GetAssignableTypesAsync (ITypeInfo type, bool childTypes)
|
||||
{
|
||||
if (type == KnownTypes[typeof(CommonValueConverter)])
|
||||
return Task.FromResult (new AssignableTypesResult (new[] { type }));
|
||||
|
||||
return ReflectionObjectEditor.GetAssignableTypes (type, childTypes);
|
||||
}
|
||||
|
||||
IObjectEditor ChooseEditor (object item)
|
||||
|
@ -35,6 +73,8 @@ namespace Xamarin.PropertyEditing.Tests
|
|||
return new MockObjectEditor (msc);
|
||||
case MockControl mc:
|
||||
return new MockNameableEditor (mc);
|
||||
case MockBinding mb:
|
||||
return new MockBindingEditor (mb);
|
||||
default:
|
||||
return new ReflectionObjectEditor (item);
|
||||
}
|
||||
|
|
|
@ -144,12 +144,14 @@ namespace Xamarin.PropertyEditing.Tests
|
|||
|
||||
public Task<AssignableTypesResult> GetAssignableTypesAsync (IPropertyInfo property, bool childTypes)
|
||||
{
|
||||
if (this.assignableTypes == null) {
|
||||
return ReflectionObjectEditor.GetAssignableTypes (property, childTypes);
|
||||
} else if (!this.assignableTypes.TryGetValue (property, out IReadOnlyList<ITypeInfo> types))
|
||||
return Task.FromResult (new AssignableTypesResult (Enumerable.Empty<ITypeInfo> ().ToArray ()));
|
||||
else
|
||||
return Task.FromResult (new AssignableTypesResult (types));
|
||||
if (this.assignableTypes != null) {
|
||||
if (!this.assignableTypes.TryGetValue (property, out IReadOnlyList<ITypeInfo> types))
|
||||
return Task.FromResult (new AssignableTypesResult (Enumerable.Empty<ITypeInfo> ().ToArray ()));
|
||||
else
|
||||
return Task.FromResult (new AssignableTypesResult (types));
|
||||
}
|
||||
|
||||
return ReflectionObjectEditor.GetAssignableTypes (property.RealType, childTypes);
|
||||
}
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
@ -310,6 +312,25 @@ namespace Xamarin.PropertyEditing.Tests
|
|||
Value = default(T)
|
||||
};
|
||||
}
|
||||
|
||||
public Task<ITypeInfo> GetValueTypeAsync (IPropertyInfo property, PropertyVariation variation = null)
|
||||
{
|
||||
if (variation != null)
|
||||
throw new NotImplementedException();
|
||||
|
||||
Type type = property.Type;
|
||||
if (this.values.TryGetValue (property, out object value)) {
|
||||
Type valueType = value.GetType ();
|
||||
if (valueType.IsConstructedGenericType && valueType.GetGenericTypeDefinition () == typeof(ValueInfo<>)) {
|
||||
value = valueType.GetProperty ("Value").GetValue (value);
|
||||
type = value.GetType ();
|
||||
} else
|
||||
type = valueType;
|
||||
}
|
||||
|
||||
var asm = new AssemblyInfo (type.Assembly.FullName, true);
|
||||
return Task.FromResult<ITypeInfo> (new TypeInfo (asm, type.Namespace, type.Name));
|
||||
}
|
||||
#pragma warning restore CS1998
|
||||
|
||||
internal readonly IDictionary<IPropertyInfo, object> values = new Dictionary<IPropertyInfo, object> ();
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Tests
|
||||
{
|
||||
internal class MockValueConverter
|
||||
{
|
||||
}
|
||||
}
|
|
@ -674,7 +674,6 @@ namespace Xamarin.PropertyEditing.Tests
|
|||
Assert.That (property.Editors.Count, Is.EqualTo (1));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void PropertiesListItemRemoved ()
|
||||
{
|
||||
|
|
|
@ -65,11 +65,15 @@
|
|||
<Compile Include="BrushPropertyViewModelTests.cs" />
|
||||
<Compile Include="BytePropertyViewModelTests.cs" />
|
||||
<Compile Include="CollectionPropertyViewModelTests.cs" />
|
||||
<Compile Include="CreateBindingViewModelTests.cs" />
|
||||
<Compile Include="CreateResourceViewModelTests.cs" />
|
||||
<Compile Include="MaterialDesignColorViewModelTests.cs" />
|
||||
<Compile Include="CombinablePredefinedViewModelTests.cs" />
|
||||
<Compile Include="MockBindingEditor.cs" />
|
||||
<Compile Include="MockBindingProvider.cs" />
|
||||
<Compile Include="MockControls\MockResourceProvider.cs" />
|
||||
<Compile Include="MockPropertyInfo\MockBrushPropertyInfo.cs" />
|
||||
<Compile Include="MockValueConverter.cs" />
|
||||
<Compile Include="NumericTests.cs" />
|
||||
<Compile Include="NumericViewModelTests.cs" />
|
||||
<Compile Include="ResourceSelectorViewModelTests.cs" />
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
xmlns:local="clr-namespace:Xamarin.PropertyEditing.Windows.Standalone"
|
||||
xmlns:xamarinprops="clr-namespace:Xamarin.PropertyEditing.Windows;assembly=Xamarin.PropertyEditing.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Property editor" Height="600" Width="525">
|
||||
Title="Property editor" Height="600" Width="625">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Xamarin.PropertyEditing.Windows.Standalone
|
|||
public MainWindow ()
|
||||
{
|
||||
InitializeComponent ();
|
||||
this.panel.TargetPlatform = new TargetPlatform (new MockEditorProvider()) {
|
||||
this.panel.TargetPlatform = new TargetPlatform (new MockEditorProvider(), new MockResourceProvider(), new MockBindingProvider()) {
|
||||
SupportsCustomExpressions = true,
|
||||
SupportsMaterialDesign = true,
|
||||
SupportsBrushOpacity = false,
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
<local:WindowEx x:Class="Xamarin.PropertyEditing.Windows.CreateBindingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Xamarin.PropertyEditing.Windows"
|
||||
xmlns:prop="clr-namespace:Xamarin.PropertyEditing.Properties;assembly=Xamarin.PropertyEditing"
|
||||
xmlns:vms="clr-namespace:Xamarin.PropertyEditing.ViewModels;assembly=Xamarin.PropertyEditing"
|
||||
mc:Ignorable="d" x:ClassModifier="internal"
|
||||
Background="{DynamicResource DialogBackgroundBrush}" Foreground="{DynamicResource DialogForegroundBrush}"
|
||||
MinWidth="400" MinHeight="400" Width="600" Height="600" ShowMaximize="False" ShowMinimize="False" ShowIcon="False" WindowStartupLocation="CenterOwner"
|
||||
Title="{Binding PropertyDisplay}">
|
||||
<local:WindowEx.Resources>
|
||||
<ResourceDictionary>
|
||||
<local:InvertedVisibilityConverter x:Key="InvertedVisibilityConverter" />
|
||||
|
||||
<Style x:Key="ExpanderDownHeaderStyle" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Border Padding="{TemplateBinding Padding}">
|
||||
<Grid Background="Transparent" SnapsToDevicePixels="False">
|
||||
<Path x:Name="arrow" Data="M0,0 L0,6 L6,0 z" HorizontalAlignment="Left" Width="11" Height="11" SnapsToDevicePixels="True" Stroke="{DynamicResource TreeViewItem.TreeArrow.Static.Stroke}" StrokeThickness="1" VerticalAlignment="Bottom">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="135" CenterY="3" CenterX="3" />
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
<ContentPresenter HorizontalAlignment="Left" Margin="13,0,0,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="RenderTransform" TargetName="arrow">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="180" CenterY="3" CenterX="3"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Fill" TargetName="arrow" Value="{DynamicResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
|
||||
<Setter Property="Stroke" TargetName="arrow" Value="{DynamicResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Stroke" TargetName="arrow" Value="{DynamicResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="arrow" Value="{DynamicResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
<Condition Property="IsChecked" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Stroke" TargetName="arrow" Value="{DynamicResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="arrow" Value="{DynamicResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MoreSettings" TargetType="{x:Type Expander}">
|
||||
<Setter Property="Foreground" Value="{DynamicResource DialogForegroundBrush}"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Expander}">
|
||||
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="3" SnapsToDevicePixels="true">
|
||||
<DockPanel>
|
||||
<ToggleButton x:Name="HeaderSite" ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" Content="{TemplateBinding Header}" DockPanel.Dock="Top" Foreground="{TemplateBinding Foreground}" FontWeight="{TemplateBinding FontWeight}" FocusVisualStyle="{DynamicResource GenericVisualFocusStyle}" FontStyle="{TemplateBinding FontStyle}" FontStretch="{TemplateBinding FontStretch}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" MinWidth="0" MinHeight="0" Padding="{TemplateBinding Padding}" Style="{StaticResource ExpanderDownHeaderStyle}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
<ContentPresenter x:Name="ExpandSite" DockPanel.Dock="Bottom" Focusable="false" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Visibility="Collapsed" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsExpanded" Value="true">
|
||||
<Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="false">
|
||||
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Themes/DialogResources.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</local:WindowEx.Resources>
|
||||
|
||||
<Grid Margin="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="{x:Static prop:Resources.BindingType}" />
|
||||
<ComboBox Grid.Row="1" Grid.Column="0" ItemsSource="{Binding BindingSources.Value}" SelectedItem="{Binding SelectedBindingSource,Mode=TwoWay}" DisplayMemberPath="Name" Margin="0,4,0,0" />
|
||||
|
||||
<TextBlock Grid.Row="2" Margin="0,8,0,0" Text="{Binding SelectedBindingSource.Description}" Visibility="{Binding ElementName=longDescription,Path=Visibility,Converter={StaticResource InvertedVisibilityConverter}}" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="2" Margin="0,8,0,0" Text="{x:Static prop:Resources.Path}" />
|
||||
<CheckBox Name="content" Grid.Row="2" Grid.Column="2" Margin="0,8,0,0" HorizontalAlignment="Right" Content="{x:Static prop:Resources.Custom}" Foreground="{DynamicResource DialogForegroundBrush}" />
|
||||
|
||||
<local:TextBoxEx Grid.Row="3" Grid.Column="2" VerticalAlignment="Top" Margin="0,4,0,4" Visibility="{Binding ElementName=content,Path=IsChecked,Converter={StaticResource BoolToVisibilityConverter}}" Text="{Binding Path,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
|
||||
|
||||
<TextBlock Name="longDescription" Grid.Row="3" Grid.RowSpan="2" Grid.Column="0" VerticalAlignment="Stretch" Text="{Binding SelectedBindingSource.Description}" Visibility="{Binding ShowLongDescription,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<local:TreeViewEx Grid.Row="3" Grid.RowSpan="2" Grid.Column="0" Margin="0,4,0,0" Style="{DynamicResource SelectionTreeView}" ItemsSource="{Binding ObjectElementRoots.Value}" DisplayMemberPath="Name.Value" Visibility="{Binding ShowObjectSelector,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<local:TreeViewEx Grid.Row="3" Grid.RowSpan="2" Grid.Column="0" Margin="0,4,0,0" Style="{DynamicResource AutoExpandSelectionTreeView}" ItemsSource="{Binding SourceResources.Value}" Visibility="{Binding ShowResourceSelector,Converter={StaticResource BoolToVisibilityConverter}}" SelectedDataItem="{Binding SelectedResource,Mode=TwoWay}">
|
||||
<local:TreeViewEx.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Mode=OneTime}">
|
||||
<HierarchicalDataTemplate.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name,Mode=OneTime}" />
|
||||
</DataTemplate>
|
||||
</HierarchicalDataTemplate.ItemTemplate>
|
||||
<TextBlock Text="{Binding Key.Name,Mode=OneTime}" />
|
||||
</HierarchicalDataTemplate>
|
||||
</local:TreeViewEx.ItemTemplate>
|
||||
</local:TreeViewEx>
|
||||
<local:TypeSelectorControl Grid.Row="3" Grid.RowSpan="2" Grid.Column="0" Margin="0,4,0,0" Visibility="{Binding DataContext.ShowTypeSelector,Converter={StaticResource BoolToVisibilityConverter},ElementName=propertiesTree}" ShowTypeLevel="{Binding DataContext.ShowTypeLevel,ElementName=propertiesTree}" DataContext="{Binding TypeSelector}" TypeLevel="{Binding TypeLevel,Mode=TwoWay}" />
|
||||
|
||||
<local:TreeViewEx x:Name="propertiesTree" Grid.Row="4" Grid.Column="2" Margin="0,4,0,0" Style="{DynamicResource SelectionTreeView}" SelectedDataItem="{Binding SelectedPropertyElement,Mode=TwoWay}">
|
||||
<local:TreeViewItemEx Header="{Binding PropertyRoot.Value.TargetType.Name}" IsExpanded="True" ItemsSource="{Binding PropertyRoot.Value.Children}" ItemContainerStyle="{DynamicResource SelectionTreeViewItem}">
|
||||
<local:TreeViewItemEx.ItemTemplate>
|
||||
<HierarchicalDataTemplate DataType="vms:PropertyTreeElement" ItemsSource="{Binding Children.Value}">
|
||||
<TextBlock>
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="{}{0}: ({1})">
|
||||
<Binding Path="Property.Name" Mode="OneTime" />
|
||||
<Binding Path="Property.RealType.Name" Mode="OneTime" />
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</HierarchicalDataTemplate>
|
||||
</local:TreeViewItemEx.ItemTemplate>
|
||||
</local:TreeViewItemEx>
|
||||
</local:TreeViewEx>
|
||||
|
||||
<StackPanel Grid.Column="0" Grid.Row="5">
|
||||
<TextBlock Text="Converter" Margin="0,8,0,4" />
|
||||
<ComboBox ItemsSource="{Binding ValueConverters.Value}" SelectedItem="{Binding SelectedValueConverter,Mode=TwoWay}" DisplayMemberPath="Name" />
|
||||
</StackPanel>
|
||||
|
||||
<Expander Name="moreSettings" Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="6" Header="More Settings" Margin="0,8,0,0" Style="{StaticResource MoreSettings}" Expanded="OnMoreSettingsExpanded" Collapsed="OnMoreSettingsCollapsed">
|
||||
<Border Margin="0,4,0,0" Background="{DynamicResource GroupBackgroundBrush}" BorderBrush="{DynamicResource GroupBorderBrush}">
|
||||
<Grid Margin="8,0,8,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ItemsControl Grid.Column="0" ItemsSource="{Binding BindingProperties}" Focusable="False">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Margin="0,8,0,0">
|
||||
<TextBlock Text="{Binding Property.Name,Mode=OneTime}" Margin="0,0,0,4" />
|
||||
<ContentPresenter ContentTemplateSelector="{DynamicResource PropertyEditorSelector}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<ItemsControl Grid.Column="2" ItemsSource="{Binding FlagsProperties}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox Margin="0,8,0,0" VerticalContentAlignment="Center" Foreground="{DynamicResource DialogForegroundBrush}" Content="{Binding Property.Name,Mode=OneTime}" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" IsChecked="{Binding Value}" IsEnabled="{Binding Property.CanWrite,Mode=OneTime}" VerticalAlignment="Center" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Expander>
|
||||
|
||||
<StackPanel Grid.Row="7" Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button MinHeight="23" MinWidth="75" IsEnabled="{Binding CanCreateBinding}" Content="{x:Static prop:Resources.OK}" IsDefault="True" Click="OnOkClicked" />
|
||||
<Button MinHeight="23" MinWidth="75" Margin="4,0,0,0" Content="{x:Static prop:Resources.Cancel}" IsCancel="True" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</local:WindowEx>
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
using Xamarin.PropertyEditing.Common;
|
||||
using Xamarin.PropertyEditing.ViewModels;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Windows
|
||||
{
|
||||
internal partial class CreateBindingWindow
|
||||
: WindowEx
|
||||
{
|
||||
public CreateBindingWindow (IEnumerable<ResourceDictionary> merged, TargetPlatform platform, IObjectEditor editor, IPropertyInfo property)
|
||||
{
|
||||
var vm = new CreateBindingViewModel (platform, editor, property);
|
||||
vm.CreateValueConverterRequested += OnCreateValueConverterRequested;
|
||||
|
||||
DataContext = vm;
|
||||
InitializeComponent ();
|
||||
Resources.MergedDictionaries.AddItems (merged);
|
||||
}
|
||||
|
||||
private void OnCreateValueConverterRequested (object sender, CreateValueConverterEventArgs e)
|
||||
{
|
||||
var vm = (CreateBindingViewModel) DataContext;
|
||||
ITypeInfo valueConverter = vm.TargetPlatform.EditorProvider.KnownTypes[typeof(CommonValueConverter)];
|
||||
|
||||
var typesTask = vm.TargetPlatform.EditorProvider.GetAssignableTypesAsync (valueConverter, childTypes: false)
|
||||
.ContinueWith (t => t.Result.GetTypeTree(), TaskScheduler.Default);
|
||||
|
||||
var result = CreateValueConverterWindow.RequestConverter (this, vm.TargetPlatform, vm.Target, new AsyncValue<IReadOnlyDictionary<IAssemblyInfo, ILookup<string, ITypeInfo>>> (typesTask));
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
e.Name = result.Item1;
|
||||
e.ConverterType = result.Item2;
|
||||
e.Source = result.Item3;
|
||||
}
|
||||
|
||||
private void OnOkClicked (object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = true;
|
||||
}
|
||||
|
||||
private void OnCancelClicked (object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
}
|
||||
|
||||
internal static object CreateBinding (FrameworkElement owner, TargetPlatform platform, IObjectEditor editor, IPropertyInfo property)
|
||||
{
|
||||
Window ownerWindow = Window.GetWindow (owner);
|
||||
var window = new CreateBindingWindow (owner.Resources.MergedDictionaries, platform, editor, property) {
|
||||
Owner = ownerWindow
|
||||
};
|
||||
bool? result = window.ShowDialog ();
|
||||
if (!result.HasValue || !result.Value)
|
||||
return null;
|
||||
|
||||
var vm = (CreateBindingViewModel)window.DataContext;
|
||||
return vm.SelectedObjects.Single();
|
||||
}
|
||||
|
||||
private void OnMoreSettingsExpanded (object sender, RoutedEventArgs e)
|
||||
{
|
||||
var fe = this.moreSettings.Content as FrameworkElement;
|
||||
if (fe == null)
|
||||
return;
|
||||
|
||||
fe.Measure (new Size (Double.PositiveInfinity, Double.PositiveInfinity));
|
||||
Height += fe.DesiredSize.Height;
|
||||
}
|
||||
|
||||
private void OnMoreSettingsCollapsed (object sender, RoutedEventArgs e)
|
||||
{
|
||||
var fe = this.moreSettings.Content as FrameworkElement;
|
||||
if (fe == null)
|
||||
return;
|
||||
|
||||
Height -= fe.DesiredSize.Height;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
xmlns:prop="clr-namespace:Xamarin.PropertyEditing.Properties;assembly=Xamarin.PropertyEditing"
|
||||
mc:Ignorable="d" Title="{x:Static prop:Resources.AddValueConverterTitle}" x:ClassModifier="internal"
|
||||
Background="{DynamicResource DialogBackgroundBrush}" Foreground="{DynamicResource DialogForegroundBrush}"
|
||||
d:DesignHeight="450" d:DesignWidth="400" ShowMaximize="False" ShowIcon="False" ShowMinimize="False">
|
||||
Height="500" Width="450" ShowMaximize="False" ShowIcon="False" ShowMinimize="False" WindowStartupLocation="CenterOwner">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary Source="Themes/DialogResources.xaml" />
|
||||
</Window.Resources>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<TextBlock Text="{x:Static prop:Resources.ValueConverterName}" Grid.Row="0" />
|
||||
<local:TextBoxEx x:Name="converterName" Text="{Binding ConverterName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Margin="0,4,0,0" />
|
||||
|
||||
<local:TypeSelectorControl x:Name="typeSelector" Grid.Row="2" SelectedItem="{Binding SelectedType,Mode=OneWayToSource}" SelectedItemChanged="OnSelectedItemChanged" ItemActivated="OnItemActivated" />
|
||||
<local:TypeSelectorControl x:Name="typeSelector" Grid.Row="2" SelectedItemChanged="OnSelectedItemChanged" ItemActivated="OnItemActivated" />
|
||||
|
||||
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Name="ok" MinHeight="23" MinWidth="75" IsEnabled="False" Content="{x:Static prop:Resources.OK}" IsDefault="True" Click="OnOkClicked" />
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
Resources.MergedDictionaries.AddItems (mergedResources);
|
||||
}
|
||||
|
||||
internal static Tuple<string, ITypeInfo> RequestConverter (FrameworkElement owner, TargetPlatform platform, object target, AsyncValue<IReadOnlyDictionary<IAssemblyInfo, ILookup<string, ITypeInfo>>> assignableTypes)
|
||||
internal static Tuple<string, ITypeInfo, ResourceSource> RequestConverter (FrameworkElement owner, TargetPlatform platform, object target, AsyncValue<IReadOnlyDictionary<IAssemblyInfo, ILookup<string, ITypeInfo>>> assignableTypes)
|
||||
{
|
||||
Window hostWindow = Window.GetWindow (owner);
|
||||
var w = new CreateValueConverterWindow (owner.Resources.MergedDictionaries, platform, target, assignableTypes) {
|
||||
|
@ -26,7 +26,9 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
if (!w.ShowDialog () ?? false)
|
||||
return null;
|
||||
|
||||
return new Tuple<string, ITypeInfo> (w.converterName.Text, w.typeSelector.SelectedItem as ITypeInfo);
|
||||
var vm = (AddValueConverterViewModel) w.DataContext;
|
||||
|
||||
return new Tuple<string, ITypeInfo, ResourceSource> (w.converterName.Text, vm.SelectedType, vm.Source);
|
||||
}
|
||||
|
||||
private void OnSelectedItemChanged (object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Windows
|
||||
{
|
||||
internal class InvertedVisibilityConverter
|
||||
: IValueConverter
|
||||
{
|
||||
public object Convert (object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return ((Visibility) value == Visibility.Collapsed) ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Windows
|
||||
{
|
||||
internal class NullVisibilityConverter
|
||||
: IValueConverter
|
||||
{
|
||||
public object Convert (object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value == null)
|
||||
return Visibility.Collapsed;
|
||||
|
||||
return Visibility.Visible;
|
||||
}
|
||||
|
||||
public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -101,12 +101,14 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
{
|
||||
if (e.OldValue is PropertyViewModel pvm) {
|
||||
pvm.ResourceRequested -= OnResourceRequested;
|
||||
pvm.CreateBindingRequested -= OnCreateBindingRequested;
|
||||
pvm.CreateResourceRequested -= OnCreateResourceRequested;
|
||||
}
|
||||
|
||||
this.vm = e.NewValue as PropertyViewModel;
|
||||
if (this.vm != null) {
|
||||
this.vm.ResourceRequested += OnResourceRequested;
|
||||
this.vm.CreateBindingRequested += OnCreateBindingRequested;
|
||||
this.vm.CreateResourceRequested += OnCreateResourceRequested;
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +123,7 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
ToolTip = Properties.Resources.Local;
|
||||
break;
|
||||
case ValueSource.Binding:
|
||||
ToolTip = Properties.Resources.Binding;
|
||||
break;
|
||||
case ValueSource.Inherited:
|
||||
ToolTip = Properties.Resources.Inherited;
|
||||
|
@ -142,6 +145,14 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
}
|
||||
}
|
||||
|
||||
private void OnCreateBindingRequested (object sender, CreateBindingRequestedEventArgs e)
|
||||
{
|
||||
var panel = this.FindPropertiesHost ();
|
||||
var pvm = (PropertyViewModel) DataContext;
|
||||
|
||||
e.BindingObject = CreateBindingWindow.CreateBinding (panel, pvm.TargetPlatform, pvm.Editors.Single(), pvm.Property);
|
||||
}
|
||||
|
||||
private void OnResourceRequested (object sender, ResourceRequestedEventArgs e)
|
||||
{
|
||||
var panel = this.FindPropertiesHost();
|
||||
|
|
|
@ -153,11 +153,8 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
this.vm.PropertyChanged -= OnVmPropertyChanged;
|
||||
|
||||
PanelViewModel newVm = null;
|
||||
if (TargetPlatform != null) {
|
||||
newVm = new PanelViewModel (TargetPlatform) {
|
||||
ResourceProvider = ResourceProvider
|
||||
};
|
||||
}
|
||||
if (TargetPlatform != null)
|
||||
newVm = new PanelViewModel (TargetPlatform);
|
||||
|
||||
this.root.DataContext = this.vm = newVm;
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Windows
|
||||
{
|
||||
[TemplatePart (Name = "up", Type = typeof(ButtonBase))]
|
||||
[TemplatePart (Name = "down", Type = typeof(ButtonBase))]
|
||||
internal class Spinner
|
||||
: Control
|
||||
{
|
||||
static Spinner ()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata (typeof(Spinner), new FrameworkPropertyMetadata (typeof(Spinner)));
|
||||
FocusableProperty.OverrideMetadata (typeof(Spinner), new FrameworkPropertyMetadata (false));
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register (
|
||||
"Value", typeof(int), typeof(Spinner), new FrameworkPropertyMetadata (default(int), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (d,p) => ((Spinner)d).OnValueChanged ()));
|
||||
|
||||
public int Value
|
||||
{
|
||||
get { return (int) GetValue (ValueProperty); }
|
||||
set { SetValue (ValueProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MinimumValueProperty = DependencyProperty.Register (
|
||||
"MinimumValue", typeof(int), typeof(Spinner), new PropertyMetadata (default(int)));
|
||||
|
||||
public int MinimumValue
|
||||
{
|
||||
get { return (int) GetValue (MinimumValueProperty); }
|
||||
set { SetValue (MinimumValueProperty, value); }
|
||||
}
|
||||
|
||||
public override void OnApplyTemplate ()
|
||||
{
|
||||
base.OnApplyTemplate ();
|
||||
|
||||
this.up = GetTemplateChild ("up") as ButtonBase;
|
||||
this.down = GetTemplateChild ("down") as ButtonBase;
|
||||
this.display = GetTemplateChild("display") as TextBlock;
|
||||
|
||||
if (this.up == null || this.down == null)
|
||||
throw new InvalidOperationException ("Spinner's template needs an up and down button");
|
||||
|
||||
this.up.Click += (sender, args) => {
|
||||
args.Handled = true;
|
||||
Adjust (1);
|
||||
};
|
||||
this.down.Click += (sender, args) => {
|
||||
args.Handled = true;
|
||||
Adjust (-1);
|
||||
};
|
||||
|
||||
Adjust (MinimumValue);
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private ButtonBase up, down;
|
||||
private TextBlock display;
|
||||
|
||||
private void Adjust (int d)
|
||||
{
|
||||
SetCurrentValue (ValueProperty, Value + d);
|
||||
this.down.IsEnabled = Value > MinimumValue;
|
||||
}
|
||||
|
||||
private void OnValueChanged ()
|
||||
{
|
||||
if (this.display == null)
|
||||
return;
|
||||
|
||||
this.display.Text = Value.ToString ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
<Color x:Key="DropShadowBackgroundColor">#72000000</Color>
|
||||
<SolidColorBrush x:Key="LiteralMarkerBrush">#55000000</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="ResourceMarkerBrush">#FF8BD44A</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="BindingMarkerBrush">#FFFFCF00</SolidColorBrush>
|
||||
|
||||
<Style x:Key="GenericVisualFocusStyle" TargetType="Control">
|
||||
<Setter Property="Margin" Value="1" />
|
||||
|
@ -70,6 +71,30 @@
|
|||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type local:Spinner}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:Spinner">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Name="display" Grid.Column="0" />
|
||||
<StackPanel Grid.Column="1" Orientation="Vertical" Margin="4,0,4,0">
|
||||
<RepeatButton Name="up" Style="{DynamicResource SpinnerButton}">
|
||||
<Geometry>M0,5L9,5 9,4 8,4 8,3 7,3 7,2 6,2 6,1 5,1 5,0 4,0 4,1 3,1 3,2 2,2 2,3 1,3 1,4 0,4z</Geometry>
|
||||
</RepeatButton>
|
||||
<RepeatButton Name="down" Style="{DynamicResource SpinnerButton}">
|
||||
<Geometry>M0,0L9,0 9,1 8,1 8,2 8,2 7,2 7,3 6,3 6,4 5,4 5,5 4,5 4,4 3,4 3,3 2,3 2,2 1,2 1,1 0,1z</Geometry>
|
||||
</RepeatButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="GroupedPropertyValuePreview">
|
||||
<DataTemplate.Resources>
|
||||
<DataTemplate DataType="{x:Type drawing:CommonColor}">
|
||||
|
@ -392,6 +417,7 @@
|
|||
|
||||
<Rectangle x:Shared="False" x:Key="LiteralMarker" Style="{StaticResource MarkerStyle}" Fill="{DynamicResource LiteralMarkerBrush}" />
|
||||
<Rectangle x:Shared="False" x:Key="ResourceMarker" Style="{StaticResource MarkerStyle}" Fill="{DynamicResource ResourceMarkerBrush}" />
|
||||
<Rectangle x:Shared="False" x:Key="BindingMarker" Style="{StaticResource MarkerStyle}" Fill="{DynamicResource BindingMarkerBrush}" />
|
||||
<Rectangle x:Shared="False" x:Key="SourceMarker" Style="{StaticResource MarkerStyle}" Fill="{Binding Foreground,RelativeSource={RelativeSource AncestorType=local:PropertyButton,Mode=FindAncestor}}" />
|
||||
|
||||
<Style TargetType="local:PropertyButton">
|
||||
|
@ -412,9 +438,9 @@
|
|||
<Separator Visibility="{Binding SupportsResources,Mode=OneTime,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<MenuItem Header="{x:Static prop:Resources.ResourceEllipsis}" Command="{Binding RequestResourceCommand,Mode=OneTime}" Icon="{StaticResource ResourceMarker}" Visibility="{Binding SupportsResources,Mode=OneTime,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<MenuItem Header="{x:Static prop:Resources.ConvertToNewResourceEllipsis}" Command="{Binding RequestCreateResourceCommand}" Icon="{StaticResource ResourceMarker}" Visibility="{Binding CanCreateResources,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<!--<Separator />
|
||||
<MenuItem Header="{x:Static prop:Resources.CreateDataBindingEllipse}" />
|
||||
<MenuItem Header="{x:Static prop:Resources.TemplateBinding}" />!-->
|
||||
<Separator Visibility="{Binding SupportsBindings,Mode=OneTime,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<MenuItem Header="{x:Static prop:Resources.CreateDataBindingMenuItem}" Command="{Binding RequestCreateBindingCommand,Mode=OneTime}" Icon="{StaticResource BindingMarker}" Visibility="{Binding SupportsBindings,Mode=OneTime,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<!--<MenuItem Header="{x:Static prop:Resources.TemplateBinding}" />!-->
|
||||
<Separator Visibility="{Binding SupportsValueSourceNavigation,Mode=OneTime,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<!--<MenuItem Header="{x:Static prop:Resources.RecordCurrentValue}" />!-->
|
||||
<MenuItem Header="{x:Static prop:Resources.GoToSource}" Command="{Binding NavigateToValueSourceCommand,Mode=OneTime}" Icon="{StaticResource SourceMarker}" Visibility="{Binding SupportsValueSourceNavigation,Mode=OneTime,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
|
@ -1783,6 +1809,56 @@
|
|||
<Setter Property="IsChecked" Value="{Binding IsChecked,Mode=TwoWay}" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="IconRepeatButton" TargetType="RepeatButton">
|
||||
<Setter Property="Height" Value="16" />
|
||||
<Setter Property="Width" Value="16" />
|
||||
<Setter Property="Background" Value="{DynamicResource IconButtonSimpleBackgroundBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource IconButtonForegroundBrush}" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<Rectangle VerticalAlignment="Center" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}">
|
||||
<Rectangle.Fill>
|
||||
<DrawingBrush Stretch="Uniform">
|
||||
<DrawingBrush.Drawing>
|
||||
<GeometryDrawing Brush="{Binding Foreground,RelativeSource={RelativeSource AncestorType=RepeatButton}}" Geometry="{Binding Content,RelativeSource={RelativeSource AncestorType=RepeatButton}}" />
|
||||
</DrawingBrush.Drawing>
|
||||
</DrawingBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Assumed inline shown during selection !-->
|
||||
<Style x:Key="SpinnerButton" TargetType="RepeatButton" BasedOn="{StaticResource IconRepeatButton}">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ListItemSelectedForegroundBrush}" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Height" Value="9" />
|
||||
<Setter Property="Width" Value="7" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type RepeatButton}">
|
||||
<ContentPresenter x:Name="contentPresenter" Focusable="False" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="true">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ListItemMouseOverForegroundBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="true">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ListItemHighlightForegroundBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ListItemDisabledForegroundBrush}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="IconButton" TargetType="Button">
|
||||
<Setter Property="Background" Value="{DynamicResource IconButtonSimpleBackgroundBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource IconButtonForegroundBrush}" />
|
||||
|
@ -1826,11 +1902,6 @@
|
|||
<Setter Property="Background" TargetName="border" Value="{DynamicResource SearchControlButtonPressedBackgroundBrush}" />
|
||||
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource SearchControlButtonPressedBorderBrush}" />
|
||||
</Trigger>
|
||||
<!--<Trigger Property="IsEnabled" Value="false">
|
||||
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
|
||||
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
|
||||
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
|
||||
</Trigger>!-->
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
|
@ -2109,7 +2180,6 @@
|
|||
<Setter Property="Foreground" Value="{DynamicResource ListItemForegroundBrush}" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
|
||||
<Setter Property="Padding" Value="1,0,0,0"/>
|
||||
<Setter Property="FocusVisualStyle">
|
||||
<Setter.Value>
|
||||
<Style BasedOn="{StaticResource GenericVisualFocusStyle}" TargetType="Control">
|
||||
|
@ -2135,8 +2205,8 @@
|
|||
</Grid.Margin>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition MinWidth="19" Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
|
||||
|
@ -2191,8 +2261,64 @@
|
|||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="AutoExpandSelectionTreeViewItem" TargetType="local:TreeViewItemEx" BasedOn="{StaticResource SelectionTreeViewItem}">
|
||||
<Setter Property="IsExpanded" Value="True" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="SelectionTreeView" TargetType="local:TreeViewEx">
|
||||
<Setter Property="ItemContainerStyle" Value="{StaticResource SelectionTreeViewItem}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ListBackgroundBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ListBackgroundBrush}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource PanelGroupForegroundBrush}" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="AutoExpandSelectionTreeView" TargetType="local:TreeViewEx" BasedOn="{StaticResource SelectionTreeView}">
|
||||
<Setter Property="ItemContainerStyle" Value="{StaticResource AutoExpandSelectionTreeViewItem}" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="TypeTreeView" TargetType="local:TreeViewEx" BasedOn="{StaticResource SelectionTreeView}">
|
||||
<Setter Property="ItemTemplate">
|
||||
<Setter.Value>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Value,Mode=OneTime}">
|
||||
<HierarchicalDataTemplate.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding Value,Mode=OneTime}">
|
||||
<HierarchicalDataTemplate.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Icon -->
|
||||
<TextBlock Grid.Column="1" Margin="8,0,0,0" Text="{Binding Name,Mode=OneTime}" />
|
||||
<local:Spinner Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Right" Value="{Binding RelativeSource={RelativeSource AncestorType=local:TypeSelectorControl},Path=TypeLevel,Mode=TwoWay}" MinimumValue="1">
|
||||
<local:Spinner.Visibility>
|
||||
<MultiBinding Converter="{StaticResource BoolsToVisibilityConverter}">
|
||||
<Binding RelativeSource="{RelativeSource AncestorType=local:TreeViewItemEx}" Path="IsSelected" />
|
||||
<Binding RelativeSource="{RelativeSource AncestorType=local:TypeSelectorControl}" Path="ShowTypeLevel" />
|
||||
</MultiBinding>
|
||||
</local:Spinner.Visibility>
|
||||
</local:Spinner>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</HierarchicalDataTemplate.ItemTemplate>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<!-- Icon -->
|
||||
<TextBlock Text="{Binding Key}" Margin="8,0,0,0" />
|
||||
</StackPanel>
|
||||
</HierarchicalDataTemplate>
|
||||
</HierarchicalDataTemplate.ItemTemplate>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<!-- Icon -->
|
||||
<TextBlock Margin="8,0,0,0" Text="{Binding Key,Mode=OneTime}" />
|
||||
</StackPanel>
|
||||
</HierarchicalDataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="RepeatButtonTransparent" TargetType="{x:Type RepeatButton}">
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
<SolidColorBrush x:Key="PanelHeaderBackgroundBrush">#252526</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PropertiesPanelIconBackgroundBrush">#252526</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="GroupBackgroundBrush">#252526</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="GroupBorderBrush">#434346</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="AdvancedExpanderCollapsedForegroundBrush">#F1F1F1</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AdvancedExpanderMouseOverForegroundBrush">#007ACC</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AdvancedExpanderMouseOverBorderBrush">#3E3E40</SolidColorBrush>
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
<SolidColorBrush x:Key="PanelHeaderBackgroundBrush">#F5F5F5</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PropertiesPanelIconBackgroundBrush">#F5F5F5</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="GroupBackgroundBrush">#FFFFFF</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="GroupBorderBrush">#CCCEDB</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="AdvancedExpanderMouseOverForegroundBrush">#007ACC</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AdvancedExpanderMouseOverBorderBrush">#C9DEF5</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AdvancedExpanderMouseOverBackgroundBrush">#C9DEF5</SolidColorBrush>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
@ -17,6 +18,15 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
|
||||
public event EventHandler ItemActivated;
|
||||
|
||||
public static readonly DependencyProperty SelectedDataItemProperty = DependencyProperty.Register (
|
||||
"SelectedDataItem", typeof(object), typeof(TreeViewEx), new PropertyMetadata (default(object), (o, args) => ((TreeViewEx)o).OnSelectedDataItemChanged()));
|
||||
|
||||
public object SelectedDataItem
|
||||
{
|
||||
get { return GetValue (SelectedDataItemProperty); }
|
||||
set { SetValue (SelectedDataItemProperty, value); }
|
||||
}
|
||||
|
||||
protected override DependencyObject GetContainerForItemOverride ()
|
||||
{
|
||||
return new TreeViewItemEx();
|
||||
|
@ -29,11 +39,14 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
|
||||
protected override void OnMouseDoubleClick (MouseButtonEventArgs e)
|
||||
{
|
||||
var inputElement = InputHitTest (e.GetPosition (this)) as UIElement;
|
||||
if (inputElement != null) {
|
||||
if (InputHitTest (e.GetPosition (this)) is UIElement inputElement) {
|
||||
var item = inputElement.FindParentOrSelf<TreeViewItemEx>();
|
||||
if (item != null && item.IsSelectable)
|
||||
ItemActivated?.Invoke (this, EventArgs.Empty);
|
||||
if (item != null) {
|
||||
if (item.IsSelectable)
|
||||
ItemActivated?.Invoke (this, EventArgs.Empty);
|
||||
else if (item.HasItems)
|
||||
item.IsExpanded = !item.IsExpanded;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnMouseDoubleClick (e);
|
||||
|
@ -44,6 +57,9 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
get { return this.selectedTreeItem; }
|
||||
set
|
||||
{
|
||||
if (value != null && !value.IsSelectable)
|
||||
return;
|
||||
|
||||
if (this.selectedTreeItem != null) {
|
||||
if (this.selectedTreeItem != value)
|
||||
this.selectedTreeItem.IsSelected = false;
|
||||
|
@ -59,10 +75,60 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
}
|
||||
|
||||
SetSelectedItem (oldItem, value?.DataContext);
|
||||
this.fromTreeItem = true;
|
||||
SetCurrentValue (SelectedDataItemProperty, value?.DataContext);
|
||||
this.fromTreeItem = false;
|
||||
}
|
||||
}
|
||||
|
||||
private TreeViewItemEx selectedTreeItem;
|
||||
private bool fromTreeItem;
|
||||
|
||||
private void OnSelectedDataItemChanged ()
|
||||
{
|
||||
if (this.fromTreeItem)
|
||||
return;
|
||||
|
||||
object item = SelectedDataItem;
|
||||
if (item == null) {
|
||||
SelectedTreeItem = null;
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedTreeItem = FindItem (ItemContainerGenerator, item);
|
||||
}
|
||||
|
||||
private TreeViewItemEx FindItem (ItemContainerGenerator generator, object item)
|
||||
{
|
||||
TreeViewItemEx treeItem;
|
||||
if (DataContext is IProvidePath pathProvider) {
|
||||
var path = pathProvider.GetItemParents (item);
|
||||
|
||||
for (int i = 0; i < path.Count; i++) {
|
||||
treeItem = (TreeViewItemEx)generator.ContainerFromItem (path[i]);
|
||||
treeItem.IsExpanded = true;
|
||||
|
||||
generator = treeItem.ItemContainerGenerator;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a fallback that will fail if virtualized away
|
||||
treeItem = generator.ContainerFromItem (item) as TreeViewItemEx;
|
||||
if (treeItem != null)
|
||||
return treeItem;
|
||||
|
||||
foreach (object element in generator.Items) {
|
||||
treeItem = generator.ContainerFromItem (element) as TreeViewItemEx;
|
||||
if (treeItem == null)
|
||||
continue;
|
||||
|
||||
treeItem = FindItem (treeItem.ItemContainerGenerator, item);
|
||||
if (treeItem != null)
|
||||
return treeItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SetSelectedItem (object oldItem, object item)
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<local:TextBoxEx Margin="0,4,0,0" Style="{DynamicResource SearchTextBox}" MinHeight="20" Text="{Binding FilterText,UpdateSourceTrigger=PropertyChanged}" ShowClearButton="True" Hint="{x:Static prop:Resources.SearchObjectsTitle}" />
|
||||
|
||||
<ProgressBar Grid.Row="1" IsIndeterminate="True" Height="10" Visibility="{Binding IsLoading,Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<local:TreeViewEx x:Name="tree" Grid.Row="1" Margin="0,4,0,0" Style="{DynamicResource TypeTreeView}" ItemsSource="{Binding Types}" />
|
||||
<local:TreeViewEx x:Name="tree" Grid.Row="1" Margin="0,4,0,0" Style="{DynamicResource TypeTreeView}" ItemsSource="{Binding Types}" SelectedDataItem="{Binding SelectedType,Mode=TwoWay}" />
|
||||
|
||||
<CheckBox Grid.Row="2" HorizontalAlignment="Left" Margin="0,4,0,0" IsChecked="{Binding ShowAllAssemblies}" Foreground="{DynamicResource PanelGroupForegroundBrush}" Content="{x:Static prop:Resources.ShowAllAssemblies}" />
|
||||
</Grid>
|
||||
|
|
|
@ -25,25 +25,22 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
remove { this.tree.ItemActivated -= value; }
|
||||
}
|
||||
|
||||
public override void OnApplyTemplate ()
|
||||
{
|
||||
base.OnApplyTemplate ();
|
||||
public static readonly DependencyProperty ShowTypeLevelProperty = DependencyProperty.Register (
|
||||
"ShowTypeLevel", typeof(bool), typeof(TypeSelectorControl), new PropertyMetadata (default(bool)));
|
||||
|
||||
this.tree.SelectedItemChanged += OnSelectedItemChanged;
|
||||
public bool ShowTypeLevel
|
||||
{
|
||||
get { return (bool) GetValue (ShowTypeLevelProperty); }
|
||||
set { SetValue (ShowTypeLevelProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register (
|
||||
"SelectedItem", typeof(object), typeof(TypeSelectorControl), new PropertyMetadata (default(object)));
|
||||
public static readonly DependencyProperty TypeLevelProperty = DependencyProperty.Register (
|
||||
"TypeLevel", typeof(int), typeof(TypeSelectorControl), new PropertyMetadata (default(int)));
|
||||
|
||||
public object SelectedItem
|
||||
public int TypeLevel
|
||||
{
|
||||
get { return (object) GetValue (SelectedItemProperty); }
|
||||
set { SetValue (SelectedItemProperty, value); }
|
||||
}
|
||||
|
||||
private void OnSelectedItemChanged (object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
SetCurrentValue (SelectedItemProperty, e.NewValue);
|
||||
get { return (int) GetValue (TypeLevelProperty); }
|
||||
set { SetValue (TypeLevelProperty, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Xamarin.PropertyEditing.Windows
|
|||
if (!w.ShowDialog () ?? false)
|
||||
return null;
|
||||
|
||||
return w.typeSelector.SelectedItem as ITypeInfo;
|
||||
return ((TypeSelectorViewModel)w.DataContext).SelectedType;
|
||||
}
|
||||
|
||||
private void OnSelectedItemChanged (object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
|
|
|
@ -81,6 +81,9 @@
|
|||
<Compile Include="ColorEditorControlBase.cs" />
|
||||
<Compile Include="ColorHelper.cs" />
|
||||
<Compile Include="CommonColorToColorConverter.cs" />
|
||||
<Compile Include="CreateBindingWindow.xaml.cs">
|
||||
<DependentUpon>CreateBindingWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="CreateResourceWindow.xaml.cs">
|
||||
<DependentUpon>CreateResourceWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -93,15 +96,18 @@
|
|||
<Compile Include="DoubleToPercentageConverter.cs" />
|
||||
<Compile Include="DoubleToQuantityConverter.cs" />
|
||||
<Compile Include="HasItemsToVisibilityConverter.cs" />
|
||||
<Compile Include="InvertedVisibilityConverter.cs" />
|
||||
<Compile Include="IPropertiesHost.cs" />
|
||||
<Compile Include="MaterialDesignColorEditorControl.cs" />
|
||||
<Compile Include="MultiplyMarginConverter.cs" />
|
||||
<Compile Include="NullVisibilityConverter.cs" />
|
||||
<Compile Include="ObjectEditorControl.cs" />
|
||||
<Compile Include="PreviewTemplateSelector.cs" />
|
||||
<Compile Include="ResourceBrushEditorControl.cs" />
|
||||
<Compile Include="ResourceSelectorWindow.xaml.cs">
|
||||
<DependentUpon>ResourceSelectorWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Spinner.cs" />
|
||||
<Compile Include="TextBoxEx.cs" />
|
||||
<Compile Include="ToggleButtonEx.cs" />
|
||||
<Compile Include="EditorPropertySelector.cs" />
|
||||
|
@ -145,6 +151,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="CreateBindingWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="CreateResourceWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xamarin.PropertyEditing.Common
|
||||
{
|
||||
public static class CommonValueConverter
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xamarin.PropertyEditing
|
||||
{
|
||||
public enum BindingSourceType
|
||||
{
|
||||
/// <summary>
|
||||
/// The binding source is that of an existing object that can be targeted, Ex. {Binding ElementName=..}
|
||||
/// </summary>
|
||||
Object = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The binding source is that of a type and the property path comes from the selected type. Ex. {RelativeSource AncestorType=..}
|
||||
/// </summary>
|
||||
Type = 1,
|
||||
Resource = 2,
|
||||
|
||||
/// <summary>
|
||||
/// SingleObject behaves the same as <see cref="Object"/>, but <see cref="BindingSource.Description"/> is treated as a long description
|
||||
/// replacing any object selection UI. Ex. {RelativeSource Self}
|
||||
/// </summary>
|
||||
SingleObject = 3
|
||||
}
|
||||
|
||||
public class BindingSource
|
||||
{
|
||||
public BindingSource (string name, BindingSourceType type)
|
||||
{
|
||||
if (name == null)
|
||||
throw new ArgumentNullException (nameof(name));
|
||||
|
||||
Name = name;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <param name="description">The localized description provided to the end user when the binding source is singular (such as self binding).</param>
|
||||
public BindingSource (string name, BindingSourceType type, string description)
|
||||
: this (name, type)
|
||||
{
|
||||
if (description == null)
|
||||
throw new ArgumentNullException (nameof(description));
|
||||
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public BindingSourceType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IBindingProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list of the binding sources that can be applied to a binding for this property.
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks></remarks>
|
||||
Task<IReadOnlyList<BindingSource>> GetBindingSourcesAsync (object target, IPropertyInfo property);
|
||||
|
||||
/// <exception cref="ArgumentException"><paramref name="source"/>'s <see cref="BindingSource.Type"/> is not <see cref="BindingSourceType.Resource"/>.</exception>
|
||||
Task<ILookup<ResourceSource, Resource>> GetResourcesAsync (BindingSource source, object target);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of the types selectable as a source in the binding <paramref name="source"/>.
|
||||
/// </summary>
|
||||
Task<AssignableTypesResult> GetSourceTypesAsync (BindingSource source, object target);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root element objects for a binding <paramref name="source"/> of <see cref="BindingSourceType.Object"/> type.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException"><paramref name="source"/>'s <see cref="BindingSource.Type"/> is not <see cref="BindingSourceType.Object"/>.</exception>
|
||||
Task<IReadOnlyList<object>> GetRootElementsAsync (BindingSource source, object target);
|
||||
|
||||
Task<IReadOnlyList<Resource>> GetValueConverterResourcesAsync (object target);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,34 @@ namespace Xamarin.PropertyEditing
|
|||
{
|
||||
public interface IEditorProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a mapping of known types (such as CommonSolidBrush) to a real-type analog.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The "real-type analog" <see cref="ITypeInfo" /> does not need to be the real version of the type. It simply needs to be a type
|
||||
/// that <see cref="CreateObjectAsync"/> into <see cref="GetObjectEditorAsync"/> can understand for the purposes of creating an
|
||||
/// <see cref="IObjectEditor"/> of it.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Expected known types are:
|
||||
/// - Applicable Common*Brush types
|
||||
/// - <see cref="PropertyBinding"/> if the platform supports binding.
|
||||
/// - <see cref="Xamarin.PropertyEditing.Common.CommonValueConverter"/> for XAML platforms.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
IReadOnlyDictionary<Type, ITypeInfo> KnownTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object editor for the given target <paramref name="item"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <param>
|
||||
/// It is not recommended that property value retrieval happens eagerly if the returned task waits for that to complete.
|
||||
/// Either do so in a separate-async fashion, or wait for the first <see cref="IObjectEditor.GetValueAsync{T}"/> call,
|
||||
/// as numerous <see cref="IObjectEditor"/>s may be requested that never retrieve their full value suite.
|
||||
/// </param>
|
||||
/// </remarks>
|
||||
Task<IObjectEditor> GetObjectEditorAsync (object item);
|
||||
|
||||
Task<IReadOnlyCollection<IPropertyInfo>> GetPropertiesForTypeAsync (ITypeInfo type);
|
||||
|
@ -15,19 +43,16 @@ namespace Xamarin.PropertyEditing
|
|||
/// </summary>
|
||||
Task<object> CreateObjectAsync (ITypeInfo type);
|
||||
|
||||
/// <summary>
|
||||
/// Gets types assignable to the given base <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The base type to retrieve assignable types for.</param>
|
||||
/// <param name="childTypes">Whether or not to look for children of a collection <paramref name="type"/> or just <paramref name="type"/>.</param>
|
||||
Task<AssignableTypesResult> GetAssignableTypesAsync (ITypeInfo type, bool childTypes);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children targets of the given target <paramref name="item"/>.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<object>> GetChildrenAsync (object item);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a mapping of known types (such as CommonSolidBrush) to a real-type analog.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The "real-type analog" <see cref="ITypeInfo" /> does not need to be the real version of the type. It simply needs to be a type
|
||||
/// that <see cref="CreateObjectAsync"/> into <see cref="GetObjectEditorAsync"/> can understand for the purposes of creating an
|
||||
/// <see cref="IObjectEditor"/> of it.
|
||||
/// </remarks>
|
||||
Task<IReadOnlyDictionary<Type, ITypeInfo>> GetKnownTypesAsync (IReadOnlyCollection<Type> knownTypes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xamarin.PropertyEditing
|
||||
{
|
||||
interface IProvidePath
|
||||
{
|
||||
IReadOnlyList<object> GetItemParents (object item);
|
||||
}
|
||||
}
|
|
@ -29,6 +29,11 @@ namespace Xamarin.PropertyEditing
|
|||
/// <summary>
|
||||
/// Gets resource sources relative to the provided <paramref name="target"/>.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ResourceSource>> GetResourceSourcesAsync (object target);
|
||||
|
||||
/// <summary>
|
||||
/// Gets resource sources relative to the provided <paramref name="target"/> and <paramref name="property"/>.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<ResourceSource>> GetResourceSourcesAsync (object target, IPropertyInfo property);
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
namespace Xamarin.PropertyEditing
|
||||
{
|
||||
public class KnownProperty
|
||||
public abstract class KnownProperty
|
||||
{
|
||||
}
|
||||
|
||||
public class KnownProperty<T>
|
||||
: KnownProperty
|
||||
{
|
||||
}
|
||||
}
|
|
@ -123,6 +123,15 @@ namespace Xamarin.PropertyEditing.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Binding type.
|
||||
/// </summary>
|
||||
public static string BindingType {
|
||||
get {
|
||||
return ResourceManager.GetString("BindingType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Black.
|
||||
/// </summary>
|
||||
|
@ -249,6 +258,24 @@ namespace Xamarin.PropertyEditing.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Create New Binding….
|
||||
/// </summary>
|
||||
public static string CreateDataBindingMenuItem {
|
||||
get {
|
||||
return ResourceManager.GetString("CreateDataBindingMenuItem", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Create Data Binding for {0}.
|
||||
/// </summary>
|
||||
public static string CreateDataBindingTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("CreateDataBindingTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Create {0} Resource.
|
||||
/// </summary>
|
||||
|
@ -267,6 +294,15 @@ namespace Xamarin.PropertyEditing.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Custom.
|
||||
/// </summary>
|
||||
public static string Custom {
|
||||
get {
|
||||
return ResourceManager.GetString("Custom", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Custom expression.
|
||||
/// </summary>
|
||||
|
@ -816,6 +852,15 @@ namespace Xamarin.PropertyEditing.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No value converter.
|
||||
/// </summary>
|
||||
public static string NoValueConverter {
|
||||
get {
|
||||
return ResourceManager.GetString("NoValueConverter", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OK.
|
||||
/// </summary>
|
||||
|
@ -843,6 +888,15 @@ namespace Xamarin.PropertyEditing.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Path.
|
||||
/// </summary>
|
||||
public static string Path {
|
||||
get {
|
||||
return ResourceManager.GetString("Path", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Properties.
|
||||
/// </summary>
|
||||
|
@ -951,6 +1005,15 @@ namespace Xamarin.PropertyEditing.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Resources.
|
||||
/// </summary>
|
||||
public static string ResourcePlural {
|
||||
get {
|
||||
return ResourceManager.GetString("ResourcePlural", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Resource ({0}).
|
||||
/// </summary>
|
||||
|
|
|
@ -454,7 +454,7 @@
|
|||
<data name="ResourceWithName" xml:space="preserve">
|
||||
<value>Resource ({0})</value>
|
||||
<comment>Resource with the raw embedded resource name</comment>
|
||||
</data>
|
||||
</data>
|
||||
<data name="Binding" xml:space="preserve">
|
||||
<value>Binding</value>
|
||||
</data>
|
||||
|
@ -514,13 +514,35 @@
|
|||
<data name="ResourceDictionary" xml:space="preserve">
|
||||
<value>Resource dictionary</value>
|
||||
</data>
|
||||
<data name="CreateDataBindingMenuItem" xml:space="preserve">
|
||||
<value>Create New Binding…</value>
|
||||
</data>
|
||||
<data name="AddValueConverterEllipsis" xml:space="preserve">
|
||||
<value>Add value converter…</value>
|
||||
</data>
|
||||
<data name="AddValueConverterTitle" xml:space="preserve">
|
||||
<value>Add Value Converter</value>
|
||||
</data>
|
||||
<data name="NoValueConverter" xml:space="preserve">
|
||||
<value>No value converter</value>
|
||||
</data>
|
||||
<data name="ValueConverterName" xml:space="preserve">
|
||||
<value>Value converter name</value>
|
||||
</data>
|
||||
<data name="CreateDataBindingTitle" xml:space="preserve">
|
||||
<value>Create Data Binding for {0}</value>
|
||||
<comment>Create Data Binding for Object.Property</comment>
|
||||
</data>
|
||||
<data name="BindingType" xml:space="preserve">
|
||||
<value>Binding type</value>
|
||||
</data>
|
||||
<data name="Path" xml:space="preserve">
|
||||
<value>Path</value>
|
||||
</data>
|
||||
<data name="ResourcePlural" xml:space="preserve">
|
||||
<value>Resources</value>
|
||||
</data>
|
||||
<data name="Custom" xml:space="preserve">
|
||||
<value>Custom</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,11 @@
|
|||
namespace Xamarin.PropertyEditing
|
||||
{
|
||||
public static class PropertyBinding
|
||||
{
|
||||
public static readonly KnownProperty<string> PathProperty = new KnownProperty<string>();
|
||||
public static readonly KnownProperty<BindingSource> SourceProperty = new KnownProperty<BindingSource>();
|
||||
public static readonly KnownProperty<object> SourceParameterProperty = new KnownProperty<object> ();
|
||||
public static readonly KnownProperty<Resource> ConverterProperty = new KnownProperty<Resource>();
|
||||
public static readonly KnownProperty<int?> TypeLevelProperty = new KnownProperty<int?> ();
|
||||
}
|
||||
}
|
|
@ -10,6 +10,13 @@ namespace Xamarin.PropertyEditing.Reflection
|
|||
public class ReflectionEditorProvider
|
||||
: IEditorProvider
|
||||
{
|
||||
public IReadOnlyDictionary<Type, ITypeInfo> KnownTypes
|
||||
{
|
||||
get;
|
||||
} = new Dictionary<Type, ITypeInfo> {
|
||||
|
||||
};
|
||||
|
||||
public Task<IObjectEditor> GetObjectEditorAsync (object item)
|
||||
{
|
||||
return Task.FromResult<IObjectEditor> (new ReflectionObjectEditor (item));
|
||||
|
@ -33,6 +40,11 @@ namespace Xamarin.PropertyEditing.Reflection
|
|||
return Task.FromResult (instance);
|
||||
}
|
||||
|
||||
public Task<AssignableTypesResult> GetAssignableTypesAsync (ITypeInfo type, bool childTypes)
|
||||
{
|
||||
return ReflectionObjectEditor.GetAssignableTypes (type, childTypes);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<object>> GetChildrenAsync (object item)
|
||||
{
|
||||
return Task.FromResult ((IReadOnlyList<object>)Array.Empty<object> ());
|
||||
|
@ -43,6 +55,11 @@ namespace Xamarin.PropertyEditing.Reflection
|
|||
return Task.FromResult<IReadOnlyDictionary<Type, ITypeInfo>> (new Dictionary<Type, ITypeInfo> ());
|
||||
}
|
||||
|
||||
public ITypeInfo GetRealType<T> (T item)
|
||||
{
|
||||
return item?.GetType ().ToTypeInfo ();
|
||||
}
|
||||
|
||||
public static Type GetRealType (ITypeInfo type)
|
||||
{
|
||||
return Type.GetType ($"{type.NameSpace}.{type.Name}, {type.Assembly.Name}");
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace Xamarin.PropertyEditing.Reflection
|
|||
|
||||
public Task<AssignableTypesResult> GetAssignableTypesAsync (IPropertyInfo property, bool childTypes)
|
||||
{
|
||||
return GetAssignableTypes (property, childTypes);
|
||||
return GetAssignableTypes (property.RealType, childTypes);
|
||||
}
|
||||
|
||||
public async Task SetValueAsync<T> (IPropertyInfo property, ValueInfo<T> value, PropertyVariation variation = null)
|
||||
|
@ -98,6 +98,18 @@ namespace Xamarin.PropertyEditing.Reflection
|
|||
OnPropertyChanged (info);
|
||||
}
|
||||
|
||||
public Task<ITypeInfo> GetValueTypeAsync (IPropertyInfo property, PropertyVariation variation = null)
|
||||
{
|
||||
if (property == null)
|
||||
throw new ArgumentNullException (nameof (property));
|
||||
|
||||
ReflectionPropertyInfo info = property as ReflectionPropertyInfo;
|
||||
if (info == null)
|
||||
throw new ArgumentException();
|
||||
|
||||
return Task.FromResult (info.GetValueType (Target));
|
||||
}
|
||||
|
||||
public async Task<ValueInfo<T>> GetValueAsync<T> (IPropertyInfo property, PropertyVariation variation = null)
|
||||
{
|
||||
if (property == null)
|
||||
|
@ -115,23 +127,23 @@ namespace Xamarin.PropertyEditing.Reflection
|
|||
};
|
||||
}
|
||||
|
||||
internal static Task<AssignableTypesResult> GetAssignableTypes (IPropertyInfo property, bool childTypes)
|
||||
internal static Task<AssignableTypesResult> GetAssignableTypes (ITypeInfo type, bool childTypes)
|
||||
{
|
||||
return Task.Run (() => {
|
||||
var types = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (a => a.GetTypes ()).AsParallel ()
|
||||
.Where (t => t.Namespace != null && !t.IsAbstract && !t.IsInterface && t.IsPublic && t.GetConstructor (Type.EmptyTypes) != null);
|
||||
|
||||
Type type = property.Type;
|
||||
Type realType = ReflectionEditorProvider.GetRealType (type);
|
||||
if (childTypes) {
|
||||
var generic = property.Type.GetInterface ("ICollection`1");
|
||||
var generic = realType.GetInterface ("ICollection`1");
|
||||
if (generic != null) {
|
||||
type = generic.GetGenericArguments()[0];
|
||||
realType = generic.GetGenericArguments()[0];
|
||||
} else {
|
||||
type = typeof(object);
|
||||
realType = typeof(object);
|
||||
}
|
||||
}
|
||||
|
||||
types = types.Where (t => type.IsAssignableFrom (t));
|
||||
types = types.Where (t => realType.IsAssignableFrom (t));
|
||||
|
||||
return new AssignableTypesResult (types.Select (t => {
|
||||
string asmName = t.Assembly.GetName ().Name;
|
||||
|
|
|
@ -84,6 +84,15 @@ namespace Xamarin.PropertyEditing.Reflection
|
|||
}
|
||||
#pragma warning restore CS1998
|
||||
|
||||
public ITypeInfo GetValueType (object target)
|
||||
{
|
||||
object value = this.propertyInfo.GetValue (target);
|
||||
Type type = value?.GetType () ?? Type;
|
||||
|
||||
var asm = new AssemblyInfo (type.Assembly.FullName, true);
|
||||
return new TypeInfo (asm, type.Namespace, type.Name);
|
||||
}
|
||||
|
||||
public bool Equals (ReflectionPropertyInfo other)
|
||||
{
|
||||
if (ReferenceEquals (null, other))
|
||||
|
|
|
@ -25,6 +25,24 @@ namespace Xamarin.PropertyEditing
|
|||
ResourceProvider = resourceProvider;
|
||||
}
|
||||
|
||||
public TargetPlatform (IEditorProvider editorProvider, IBindingProvider bindingProvider)
|
||||
: this (editorProvider)
|
||||
{
|
||||
if (bindingProvider == null)
|
||||
throw new ArgumentNullException (nameof (bindingProvider));
|
||||
|
||||
BindingProvider = bindingProvider;
|
||||
}
|
||||
|
||||
public TargetPlatform (IEditorProvider editorProvider, IResourceProvider resourceProvider, IBindingProvider bindingProvider)
|
||||
: this (editorProvider, resourceProvider)
|
||||
{
|
||||
if (bindingProvider == null)
|
||||
throw new ArgumentNullException (nameof(bindingProvider));
|
||||
|
||||
BindingProvider = bindingProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IEditorProvider"/> associated with this platform.
|
||||
/// </summary>
|
||||
|
@ -39,6 +57,12 @@ namespace Xamarin.PropertyEditing
|
|||
private set;
|
||||
}
|
||||
|
||||
public IBindingProvider BindingProvider
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the platform supports custom expressions (default false).
|
||||
/// </summary>
|
||||
|
@ -88,6 +112,7 @@ namespace Xamarin.PropertyEditing
|
|||
|
||||
return new TargetPlatform (provider) {
|
||||
ResourceProvider = ResourceProvider,
|
||||
BindingProvider = BindingProvider,
|
||||
SupportsMaterialDesign = SupportsMaterialDesign,
|
||||
SupportsCustomExpressions = SupportsCustomExpressions,
|
||||
SupportsBrushOpacity = SupportsBrushOpacity,
|
||||
|
|
|
@ -19,6 +19,12 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
|
||||
this.platform = platform;
|
||||
this.target = target;
|
||||
this.source = GetDefaultResourceSourceAsync ();
|
||||
}
|
||||
|
||||
public ResourceSource Source
|
||||
{
|
||||
get { return this.source.Result; }
|
||||
}
|
||||
|
||||
public string ConverterName
|
||||
|
@ -52,8 +58,34 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private readonly Task<ResourceSource> source;
|
||||
private readonly TargetPlatform platform;
|
||||
private readonly object target;
|
||||
private string converterName;
|
||||
|
||||
private async Task<ResourceSource> GetDefaultResourceSourceAsync ()
|
||||
{
|
||||
var sources = new List<ResourceSource> (await this.platform.ResourceProvider.GetResourceSourcesAsync (this.target));
|
||||
sources.Sort (new SourcePrioritySorter ());
|
||||
return sources.FirstOrDefault (s => s.Type != ResourceSourceType.System);
|
||||
}
|
||||
|
||||
private class SourcePrioritySorter
|
||||
: IComparer<ResourceSource>
|
||||
{
|
||||
public int Compare (ResourceSource x, ResourceSource y)
|
||||
{
|
||||
if (x.Type == y.Type)
|
||||
return 0;
|
||||
if (x.Type == ResourceSourceType.Document)
|
||||
return -1;
|
||||
if (x.Type == ResourceSourceType.Application)
|
||||
return -1;
|
||||
if (x.Type == ResourceSourceType.ResourceDictionary)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -218,6 +218,8 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
this.realProvider = realProvider;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Type, ITypeInfo> KnownTypes => this.realProvider.KnownTypes;
|
||||
|
||||
public void Add (IObjectEditor editor)
|
||||
{
|
||||
this.editors.Add (editor.Target, editor);
|
||||
|
@ -256,16 +258,16 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
return editor;
|
||||
}
|
||||
|
||||
public Task<AssignableTypesResult> GetAssignableTypesAsync (ITypeInfo type, bool childTypes)
|
||||
{
|
||||
return this.realProvider.GetAssignableTypesAsync (type, childTypes);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<object>> GetChildrenAsync (object item)
|
||||
{
|
||||
return this.realProvider.GetChildrenAsync (item);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyDictionary<Type, ITypeInfo>> GetKnownTypesAsync (IReadOnlyCollection<Type> knownTypes)
|
||||
{
|
||||
return this.realProvider.GetKnownTypesAsync (knownTypes);
|
||||
}
|
||||
|
||||
public Task<object> CreateObjectAsync (ITypeInfo type)
|
||||
{
|
||||
return this.realProvider.CreateObjectAsync (type);
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
|
||||
namespace Xamarin.PropertyEditing.ViewModels
|
||||
{
|
||||
internal class CreateBindingRequestedEventArgs
|
||||
: EventArgs
|
||||
{
|
||||
public object BindingObject
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,684 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.PropertyEditing.Properties;
|
||||
|
||||
namespace Xamarin.PropertyEditing.ViewModels
|
||||
{
|
||||
internal class CreateValueConverterEventArgs
|
||||
: EventArgs
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ITypeInfo ConverterType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ResourceSource Source
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CreateBindingViewModel
|
||||
: PropertiesViewModel, IProvidePath
|
||||
{
|
||||
public CreateBindingViewModel (TargetPlatform platform, IObjectEditor targetEditor, IPropertyInfo property, PropertyVariation variation = null)
|
||||
: base (platform)
|
||||
{
|
||||
if (platform == null)
|
||||
throw new ArgumentNullException (nameof(platform));
|
||||
if (targetEditor == null)
|
||||
throw new ArgumentNullException (nameof(targetEditor));
|
||||
if (platform.BindingProvider == null)
|
||||
throw new ArgumentException ("Null BindingProvider on TargetPlatform", nameof(platform));
|
||||
if (property == null)
|
||||
throw new ArgumentNullException (nameof(property));
|
||||
|
||||
this.editorProvider = platform.EditorProvider;
|
||||
this.targetEditor = targetEditor;
|
||||
this.property = property;
|
||||
this.provider = platform.BindingProvider;
|
||||
this.variation = variation;
|
||||
|
||||
PropertyDisplay = String.Format (Resources.CreateDataBindingTitle, $"[{this.targetEditor.TargetType.Name}].{property.Name}");
|
||||
RequestNamedDisplay ();
|
||||
|
||||
BindingSources = new AsyncValue<IReadOnlyList<BindingSource>> (
|
||||
platform.BindingProvider.GetBindingSourcesAsync (targetEditor.Target, property));
|
||||
|
||||
RequestBindingObject();
|
||||
}
|
||||
|
||||
private async void RequestBindingObject ()
|
||||
{
|
||||
if (!TargetPlatform.EditorProvider.KnownTypes.TryGetValue (typeof (PropertyBinding), out ITypeInfo bindingType))
|
||||
throw new InvalidOperationException ("IEditorProvider does not have a known type for PropertyBinding");
|
||||
|
||||
object binding = await TargetPlatform.EditorProvider.CreateObjectAsync (bindingType);
|
||||
SelectedObjects.Add (binding);
|
||||
}
|
||||
|
||||
private async void RequestNamedDisplay ()
|
||||
{
|
||||
if (!(this.targetEditor is INameableObject nameable))
|
||||
return;
|
||||
|
||||
string name = await nameable.GetNameAsync ();
|
||||
if (String.IsNullOrWhiteSpace (name))
|
||||
return;
|
||||
|
||||
PropertyDisplay = String.Format (Resources.CreateDataBindingTitle, $"{name}.{this.property.Name}");
|
||||
}
|
||||
|
||||
public event EventHandler<CreateValueConverterEventArgs> CreateValueConverterRequested;
|
||||
|
||||
public object Target => this.targetEditor.Target;
|
||||
|
||||
public string PropertyDisplay
|
||||
{
|
||||
get { return this.propertyDisplay; }
|
||||
private set
|
||||
{
|
||||
if (this.propertyDisplay == value)
|
||||
return;
|
||||
|
||||
this.propertyDisplay = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanCreateBinding
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedBindingSource == null)
|
||||
return false;
|
||||
|
||||
switch (SelectedBindingSource.Type) {
|
||||
case BindingSourceType.SingleObject:
|
||||
case BindingSourceType.Object:
|
||||
return SelectedObjectTreeElement != null;
|
||||
case BindingSourceType.Resource:
|
||||
return SelectedResource != null;
|
||||
case BindingSourceType.Type:
|
||||
return TypeSelector.SelectedType != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowLongDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedBindingSource == null)
|
||||
return false;
|
||||
|
||||
return SelectedBindingSource.Type == BindingSourceType.SingleObject;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowTypeSelector
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedBindingSource == null || ShowLongDescription)
|
||||
return false;
|
||||
|
||||
return SelectedBindingSource.Type == BindingSourceType.Type;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowTypeLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ObjectEditors.Count == 0)
|
||||
return false;
|
||||
|
||||
IObjectEditor bindingEditor = ObjectEditors[0];
|
||||
return bindingEditor.KnownProperties.Values.Contains (PropertyBinding.TypeLevelProperty);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowObjectSelector
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedBindingSource == null || ShowLongDescription)
|
||||
return false;
|
||||
|
||||
return SelectedBindingSource.Type == BindingSourceType.Object;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowResourceSelector
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedBindingSource == null || ShowLongDescription)
|
||||
return false;
|
||||
|
||||
return SelectedBindingSource.Type == BindingSourceType.Resource;
|
||||
}
|
||||
}
|
||||
|
||||
public TypeSelectorViewModel TypeSelector
|
||||
{
|
||||
get { return this.typeSelector; }
|
||||
private set
|
||||
{
|
||||
if (this.typeSelector == value)
|
||||
return;
|
||||
|
||||
if (this.typeSelector != null)
|
||||
this.typeSelector.PropertyChanged -= OnTypeSelectorPropertyChanged;
|
||||
|
||||
this.typeSelector = value;
|
||||
if (this.typeSelector != null)
|
||||
this.typeSelector.PropertyChanged += OnTypeSelectorPropertyChanged;
|
||||
|
||||
OnPropertyChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncValue<IReadOnlyList<BindingSource>> BindingSources
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public BindingSource SelectedBindingSource
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!KnownPropertiesSetup)
|
||||
return null;
|
||||
|
||||
return GetKnownPropertyViewModel (PropertyBinding.SourceProperty).Value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var vm = GetKnownPropertyViewModel (PropertyBinding.SourceProperty);
|
||||
if (vm.Value == value)
|
||||
return;
|
||||
|
||||
if (vm.Value != null) {
|
||||
switch (vm.Value.Type) {
|
||||
case BindingSourceType.SingleObject:
|
||||
case BindingSourceType.Object:
|
||||
SelectedObjectTreeElement = null;
|
||||
break;
|
||||
case BindingSourceType.Resource:
|
||||
SelectedResource = null;
|
||||
break;
|
||||
case BindingSourceType.Type:
|
||||
TypeSelector.SelectedType = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vm.Value = value;
|
||||
OnPropertyChanged();
|
||||
UpdateShowProperties();
|
||||
RequestUpdateSources();
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncValue<ILookup<ResourceSource, Resource>> SourceResources
|
||||
{
|
||||
get { return this.resources; }
|
||||
private set
|
||||
{
|
||||
this.resources = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Resource SelectedResource
|
||||
{
|
||||
get { return this.selectedResource; }
|
||||
set
|
||||
{
|
||||
if (this.selectedResource == value)
|
||||
return;
|
||||
|
||||
this.selectedResource = value;
|
||||
BindingSourceTarget = value;
|
||||
OnPropertyChanged ();
|
||||
OnPropertyChanged (nameof(CanCreateBinding));
|
||||
|
||||
RequestUpdateProperties ();
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncValue<IReadOnlyList<ObjectTreeElement>> ObjectElementRoots
|
||||
{
|
||||
get { return this.objectElementRoots; }
|
||||
private set
|
||||
{
|
||||
if (this.objectElementRoots == value)
|
||||
return;
|
||||
|
||||
this.objectElementRoots = value;
|
||||
OnPropertyChanged ();
|
||||
|
||||
RequestUpdateProperties ();
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectTreeElement SelectedObjectTreeElement
|
||||
{
|
||||
get { return this.selectedObjectTreeElement; }
|
||||
set
|
||||
{
|
||||
if (this.selectedObjectTreeElement == value)
|
||||
return;
|
||||
|
||||
this.selectedObjectTreeElement = value;
|
||||
BindingSourceTarget = value?.Editor.Target;
|
||||
OnPropertyChanged ();
|
||||
OnPropertyChanged (nameof (CanCreateBinding));
|
||||
|
||||
RequestUpdateProperties ();
|
||||
}
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!KnownPropertiesSetup)
|
||||
return null;
|
||||
|
||||
return GetKnownPropertyViewModel<string> (PropertyBinding.PathProperty).Value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var vm = GetKnownPropertyViewModel<string> (PropertyBinding.PathProperty);
|
||||
if (vm.Value == value)
|
||||
return;
|
||||
|
||||
vm.Value = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncValue<PropertyTreeRoot> PropertyRoot
|
||||
{
|
||||
get { return this.propertyRoot; }
|
||||
private set
|
||||
{
|
||||
if (this.propertyRoot == value)
|
||||
return;
|
||||
|
||||
this.propertyRoot = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyTreeElement SelectedPropertyElement
|
||||
{
|
||||
get { return this.selectedPropertyElement; }
|
||||
set
|
||||
{
|
||||
if (this.selectedPropertyElement == value)
|
||||
return;
|
||||
|
||||
this.selectedPropertyElement = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
UpdatePath ();
|
||||
}
|
||||
}
|
||||
|
||||
public AsyncValue<IReadOnlyList<Resource>> ValueConverters
|
||||
{
|
||||
get { return this.valueConvertersAsync; }
|
||||
private set
|
||||
{
|
||||
if (this.valueConvertersAsync == value)
|
||||
return;
|
||||
|
||||
this.valueConvertersAsync = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public Resource SelectedValueConverter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!KnownPropertiesSetup)
|
||||
return null;
|
||||
|
||||
var vm = GetKnownPropertyViewModel (PropertyBinding.ConverterProperty);
|
||||
if (vm.Value == null)
|
||||
return NoValueConverter;
|
||||
|
||||
return vm.Value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var vm = GetKnownPropertyViewModel (PropertyBinding.ConverterProperty);
|
||||
if (vm.Value == value)
|
||||
return;
|
||||
|
||||
if (value == NoValueConverter)
|
||||
value = null;
|
||||
|
||||
vm.Value = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
if (value == AddValueConverter) {
|
||||
var request = RequestCreateValueConverter ();
|
||||
if (request.ConverterType != null)
|
||||
RequestCreateRequestedValueConverter (request);
|
||||
else
|
||||
SelectedValueConverter = NoValueConverter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<EditorViewModel> FlagsProperties
|
||||
{
|
||||
get { return this.bindingFlagsProperties; }
|
||||
private set
|
||||
{
|
||||
if (this.bindingFlagsProperties == value)
|
||||
return;
|
||||
|
||||
this.bindingFlagsProperties = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<EditorViewModel> BindingProperties
|
||||
{
|
||||
get { return this.bindingProperties; }
|
||||
private set
|
||||
{
|
||||
if (this.bindingProperties == value)
|
||||
return;
|
||||
|
||||
this.bindingProperties = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAddEditors (IEnumerable<EditorViewModel> editors)
|
||||
{
|
||||
base.OnAddEditors (editors);
|
||||
ValueConverters = new AsyncValue<IReadOnlyList<Resource>> (GetValueConvertersAsync());
|
||||
|
||||
var flags = new List<EditorViewModel> ();
|
||||
var regular = new List<EditorViewModel> ();
|
||||
// We do not support grouped properties here
|
||||
foreach (EditorViewModel vm in Properties) {
|
||||
var propvm = vm as PropertyViewModel;
|
||||
if (propvm == null)
|
||||
continue;
|
||||
if (BindingEditor.KnownProperties.ContainsKey (propvm.Property))
|
||||
continue;
|
||||
|
||||
if (propvm.Property.Type == typeof(bool))
|
||||
flags.Add (vm);
|
||||
else
|
||||
regular.Add (vm);
|
||||
}
|
||||
|
||||
BindingProperties = regular;
|
||||
FlagsProperties = flags;
|
||||
|
||||
BindingSources.Task.ContinueWith (t => {
|
||||
SelectedBindingSource = t.Result.FirstOrDefault ();
|
||||
}, TaskScheduler.FromCurrentSynchronizationContext ());
|
||||
|
||||
OnPropertyChanged (nameof(Path));
|
||||
OnPropertyChanged (nameof(SelectedValueConverter));
|
||||
OnPropertyChanged (nameof(ShowTypeLevel));
|
||||
}
|
||||
|
||||
private static readonly Resource NoValueConverter = new Resource (Resources.NoValueConverter);
|
||||
private static readonly Resource AddValueConverter = new Resource ("<" + Resources.AddValueConverterEllipsis + ">");
|
||||
|
||||
private readonly PropertyVariation variation;
|
||||
private readonly IObjectEditor targetEditor;
|
||||
private readonly IPropertyInfo property;
|
||||
private readonly IBindingProvider provider;
|
||||
private readonly IEditorProvider editorProvider;
|
||||
private readonly ObservableCollectionEx<Resource> valueConverters = new ObservableCollectionEx<Resource> ();
|
||||
|
||||
private TypeSelectorViewModel typeSelector;
|
||||
private ObjectTreeElement selectedObjectTreeElement;
|
||||
private PropertyTreeElement selectedPropertyElement;
|
||||
private AsyncValue<PropertyTreeRoot> propertyRoot;
|
||||
private AsyncValue<IReadOnlyList<ObjectTreeElement>> objectElementRoots;
|
||||
private AsyncValue<IReadOnlyList<Resource>> valueConvertersAsync;
|
||||
private IReadOnlyList<EditorViewModel> bindingFlagsProperties;
|
||||
private IReadOnlyList<EditorViewModel> bindingProperties;
|
||||
private string propertyDisplay;
|
||||
private AsyncValue<ILookup<ResourceSource, Resource>> resources;
|
||||
private Resource selectedResource;
|
||||
|
||||
private bool KnownPropertiesSetup => ValueConverters != null;
|
||||
|
||||
private IObjectEditor BindingEditor => ObjectEditors[0];
|
||||
|
||||
private object BindingSourceTarget
|
||||
{
|
||||
get { return GetKnownPropertyViewModel<object> (PropertyBinding.SourceParameterProperty).Value; }
|
||||
set { GetKnownPropertyViewModel<object> (PropertyBinding.SourceParameterProperty).Value = value; }
|
||||
}
|
||||
|
||||
private void UpdateShowProperties ()
|
||||
{
|
||||
OnPropertyChanged (nameof (ShowResourceSelector));
|
||||
OnPropertyChanged (nameof (ShowObjectSelector));
|
||||
OnPropertyChanged (nameof (ShowTypeSelector));
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<Resource>> GetValueConvertersAsync ()
|
||||
{
|
||||
this.valueConverters.Clear ();
|
||||
this.valueConverters.Add (NoValueConverter);
|
||||
|
||||
var converters = await TargetPlatform.BindingProvider.GetValueConverterResourcesAsync (this.targetEditor.Target);
|
||||
this.valueConverters.AddRange (converters);
|
||||
this.valueConverters.Add (AddValueConverter);
|
||||
|
||||
return this.valueConverters;
|
||||
}
|
||||
|
||||
private async void RequestUpdateSources ()
|
||||
{
|
||||
Task task = null;
|
||||
switch (SelectedBindingSource.Type) {
|
||||
case BindingSourceType.SingleObject:
|
||||
case BindingSourceType.Object:
|
||||
SelectedObjectTreeElement = null;
|
||||
ObjectElementRoots = new AsyncValue<IReadOnlyList<ObjectTreeElement>> (GetRootElementsAsync ());
|
||||
task = ObjectElementRoots.Task;
|
||||
break;
|
||||
case BindingSourceType.Type:
|
||||
TypeSelector = new TypeSelectorViewModel (new AsyncValue<IReadOnlyDictionary<IAssemblyInfo, ILookup<string, ITypeInfo>>> (GetBindingSourceTypesAsync()));
|
||||
break;
|
||||
case BindingSourceType.Resource:
|
||||
SelectedResource = null;
|
||||
SourceResources = new AsyncValue<ILookup<ResourceSource, Resource>> (GetSourceResourcesAsync ());
|
||||
break;
|
||||
}
|
||||
|
||||
if (task != null)
|
||||
await task;
|
||||
|
||||
switch (SelectedBindingSource.Type) {
|
||||
case BindingSourceType.SingleObject:
|
||||
case BindingSourceType.Object:
|
||||
if (ObjectElementRoots.Value.Count == 1) {
|
||||
var root = ObjectElementRoots.Value[0];
|
||||
var children = await root.Children.Task;
|
||||
if (children.Count == 0)
|
||||
SelectedObjectTreeElement = root;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateShowProperties ();
|
||||
OnPropertyChanged (nameof (ShowLongDescription));
|
||||
}
|
||||
|
||||
private void OnTypeSelectorPropertyChanged (object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName) {
|
||||
case nameof(TypeSelector.SelectedType):
|
||||
OnPropertyChanged (nameof (CanCreateBinding));
|
||||
BindingSourceTarget = TypeSelector.SelectedType;
|
||||
RequestUpdateProperties ();
|
||||
break;
|
||||
case nameof(TypeSelector.TypeLevel):
|
||||
var vm = GetKnownPropertyViewModel (PropertyBinding.TypeLevelProperty);
|
||||
if (vm != null)
|
||||
vm.Value = TypeSelector.TypeLevel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private Task<ILookup<ResourceSource, Resource>> GetSourceResourcesAsync ()
|
||||
{
|
||||
return this.provider.GetResourcesAsync (SelectedBindingSource, Target);
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyDictionary<IAssemblyInfo, ILookup<string, ITypeInfo>>> GetBindingSourceTypesAsync ()
|
||||
{
|
||||
var results = await this.provider.GetSourceTypesAsync (SelectedBindingSource, this.targetEditor.Target).ConfigureAwait (false);
|
||||
return results.GetTypeTree();
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<ObjectTreeElement>> GetRootElementsAsync ()
|
||||
{
|
||||
var root = await this.provider.GetRootElementsAsync (SelectedBindingSource, this.targetEditor.Target).ConfigureAwait (false);
|
||||
IObjectEditor[] editors = await Task.WhenAll (root.Select (o => this.editorProvider.GetObjectEditorAsync (o))).ConfigureAwait (false);
|
||||
return editors.Select (oe => new ObjectTreeElement (this.editorProvider, oe)).ToArray ();
|
||||
}
|
||||
|
||||
private void RequestUpdateProperties ()
|
||||
{
|
||||
SelectedPropertyElement = null;
|
||||
|
||||
ITypeInfo type = null;
|
||||
switch (SelectedBindingSource.Type) {
|
||||
case BindingSourceType.Type:
|
||||
type = TypeSelector.SelectedType;
|
||||
break;
|
||||
case BindingSourceType.SingleObject:
|
||||
case BindingSourceType.Object:
|
||||
type = SelectedObjectTreeElement?.Editor.TargetType;
|
||||
break;
|
||||
case BindingSourceType.Resource:
|
||||
if (SelectedResource != null)
|
||||
type = GetRealType (SelectedResource);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
PropertyRoot = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Task<PropertyTreeRoot> task = this.editorProvider.GetPropertiesForTypeAsync (type)
|
||||
.ContinueWith (t => new PropertyTreeRoot (this.editorProvider, type, t.Result), TaskScheduler.Default);
|
||||
|
||||
PropertyRoot = new AsyncValue<PropertyTreeRoot> (task);
|
||||
}
|
||||
|
||||
private ITypeInfo GetRealType (Resource resource)
|
||||
{
|
||||
if (resource.RepresentationType.IsPrimitive)
|
||||
return resource.RepresentationType.ToTypeInfo (isRelevant: false);
|
||||
if (this.editorProvider.KnownTypes.TryGetValue (resource.RepresentationType, out ITypeInfo type))
|
||||
return type;
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private void UpdatePath ()
|
||||
{
|
||||
if (SelectedPropertyElement == null) {
|
||||
Path = null;
|
||||
return;
|
||||
}
|
||||
|
||||
string newPath = String.Empty;
|
||||
PropertyTreeElement element = SelectedPropertyElement;
|
||||
while (element != null) {
|
||||
string sep = (newPath != String.Empty) ? ((!element.IsCollection) ? "." : "/") : String.Empty;
|
||||
newPath = element.Property.Name + sep + newPath;
|
||||
element = element.Parent;
|
||||
}
|
||||
|
||||
Path = newPath;
|
||||
}
|
||||
|
||||
private CreateValueConverterEventArgs RequestCreateValueConverter ()
|
||||
{
|
||||
var e = new CreateValueConverterEventArgs();
|
||||
CreateValueConverterRequested?.Invoke (this, e);
|
||||
return e;
|
||||
}
|
||||
|
||||
private async void RequestCreateRequestedValueConverter (CreateValueConverterEventArgs e)
|
||||
{
|
||||
if (e.ConverterType == null || e.Name == null) {
|
||||
SelectedValueConverter = NoValueConverter;
|
||||
return;
|
||||
}
|
||||
|
||||
object converter = await TargetPlatform.EditorProvider.CreateObjectAsync (e.ConverterType);
|
||||
if (e.Source == null) {
|
||||
// TODO: Set directly outside of a resource
|
||||
return;
|
||||
}
|
||||
|
||||
Resource resource = await TargetPlatform.ResourceProvider.CreateResourceAsync (e.Source, e.Name, converter);
|
||||
this.valueConverters.Insert (this.valueConverters.Count - 1, resource);
|
||||
SelectedValueConverter = resource;
|
||||
}
|
||||
|
||||
IReadOnlyList<object> IProvidePath.GetItemParents (object item)
|
||||
{
|
||||
switch (item) {
|
||||
case PropertyTreeElement prop:
|
||||
List<object> path = new List<object> ();
|
||||
var current = prop;
|
||||
while (current != null) {
|
||||
path.Insert (0, current);
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
path.Insert (0, PropertyRoot.Value);
|
||||
return path;
|
||||
case Resource resource:
|
||||
return new object[] { resource.Source, resource };
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -108,7 +108,6 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
return;
|
||||
|
||||
if (!value) {
|
||||
<<<<<<< HEAD
|
||||
if (HasDocumentSources)
|
||||
DefineInDocument = true;
|
||||
else if (HasApplicationSources)
|
||||
|
@ -116,9 +115,6 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
else
|
||||
DefineInApplication = true;
|
||||
|
||||
=======
|
||||
DefineInDocument = true;
|
||||
>>>>>>> [Core/Win] Create resource core/window
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xamarin.PropertyEditing.ViewModels
|
||||
{
|
||||
internal class ObjectTreeElement
|
||||
{
|
||||
public ObjectTreeElement (IEditorProvider provider, IObjectEditor editor)
|
||||
{
|
||||
if (provider == null)
|
||||
throw new ArgumentNullException (nameof(provider));
|
||||
if (editor == null)
|
||||
throw new ArgumentNullException (nameof(editor));
|
||||
|
||||
Editor = editor;
|
||||
Children = new AsyncValue<IReadOnlyList<ObjectTreeElement>> (QueryChildrenAsync (provider));
|
||||
|
||||
string typeName = $"[{Editor.TargetType.Name}]";
|
||||
|
||||
Task<string> nameTask;
|
||||
INameableObject nameable = Editor as INameableObject;
|
||||
if (nameable != null) {
|
||||
nameTask = nameable.GetNameAsync ().ContinueWith (t =>
|
||||
(!String.IsNullOrWhiteSpace (t.Result)) ? $"{typeName} \"{t.Result}\"" : typeName, TaskScheduler.Default);
|
||||
} else
|
||||
nameTask = Task.FromResult (typeName);
|
||||
|
||||
Name = new AsyncValue<string> (nameTask, typeName);
|
||||
}
|
||||
|
||||
public AsyncValue<string> Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public IObjectEditor Editor
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public AsyncValue<IReadOnlyList<ObjectTreeElement>> Children
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<ObjectTreeElement>> QueryChildrenAsync (IEditorProvider provider)
|
||||
{
|
||||
var targets = await provider.GetChildrenAsync (Editor.Target);
|
||||
|
||||
List<Task<IObjectEditor>> editorTasks = new List<Task<IObjectEditor>> ();
|
||||
foreach (object target in targets) {
|
||||
editorTasks.Add (provider.GetObjectEditorAsync (target));
|
||||
}
|
||||
|
||||
IObjectEditor[] editors = await Task.WhenAll (editorTasks);
|
||||
return editors.Select (e => new ObjectTreeElement (provider, e)).ToArray ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ using System.ComponentModel;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Cadenza.Collections;
|
||||
using Xamarin.PropertyEditing.Drawing;
|
||||
|
||||
namespace Xamarin.PropertyEditing.ViewModels
|
||||
|
@ -25,20 +26,6 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
|
||||
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
|
||||
|
||||
public IResourceProvider ResourceProvider
|
||||
{
|
||||
get { return this.resourceProvider; }
|
||||
set
|
||||
{
|
||||
if (this.resourceProvider == value)
|
||||
return;
|
||||
|
||||
this.resourceProvider = value;
|
||||
UpdateResourceProvider();
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>Consumers should check for <see cref="INotifyCollectionChanged"/> and hook appropriately.</remarks>
|
||||
public IReadOnlyList<EditorViewModel> Properties => this.editors;
|
||||
|
||||
|
@ -106,8 +93,6 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
|
||||
public bool HasErrors => this.errors.IsValueCreated && this.errors.Value.Count > 0;
|
||||
|
||||
protected IReadOnlyList<IObjectEditor> ObjectEditors => this.objEditors;
|
||||
|
||||
public IEnumerable GetErrors (string propertyName)
|
||||
{
|
||||
if (!this.errors.IsValueCreated)
|
||||
|
@ -120,6 +105,24 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
return Enumerable.Empty<string> ();
|
||||
}
|
||||
|
||||
public PropertyViewModel<T> GetKnownPropertyViewModel<T> (KnownProperty<T> property)
|
||||
{
|
||||
if (property == null)
|
||||
throw new ArgumentNullException (nameof (property));
|
||||
if (this.knownEditors == null)
|
||||
throw new InvalidOperationException ("Querying for known properties before they've been setup");
|
||||
if (!this.knownEditors.TryGetValue (property, out EditorViewModel model))
|
||||
throw new KeyNotFoundException ();
|
||||
|
||||
var vm = model as PropertyViewModel<T>;
|
||||
if (vm == null)
|
||||
throw new InvalidOperationException ("KnownProperty doesn't return expected property view model type");
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
protected IReadOnlyList<IObjectEditor> ObjectEditors => this.objEditors;
|
||||
|
||||
/// <param name="newError">The error message or <c>null</c> to clear the error.</param>
|
||||
protected void SetError (string property, string newError)
|
||||
{
|
||||
|
@ -206,11 +209,11 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
{
|
||||
}
|
||||
|
||||
private IResourceProvider resourceProvider;
|
||||
private INameableObject nameable;
|
||||
private bool nameReadOnly;
|
||||
private bool eventsEnabled;
|
||||
private string typeName, objectName;
|
||||
private BidirectionalDictionary<KnownProperty, EditorViewModel> knownEditors;
|
||||
private readonly List<IObjectEditor> objEditors = new List<IObjectEditor> ();
|
||||
private readonly ObservableCollectionEx<EditorViewModel> editors = new ObservableCollectionEx<EditorViewModel> ();
|
||||
private readonly ObservableCollectionEx<object> selectedObjects = new ObservableCollectionEx<object> ();
|
||||
|
@ -224,12 +227,34 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
|
||||
private void AddProperties (IEnumerable<EditorViewModel> newEditors)
|
||||
{
|
||||
if (this.knownEditors != null) {
|
||||
// Only properties common across obj editors will be listed, so knowns should also be common
|
||||
var knownProperties = newEditors.First ().Editors.First().KnownProperties;
|
||||
if (knownProperties != null && knownProperties.Count > 0) {
|
||||
foreach (var editorvm in newEditors) {
|
||||
var prop = editorvm as PropertyViewModel;
|
||||
if (prop == null)
|
||||
continue;
|
||||
|
||||
if (knownProperties.TryGetValue (prop.Property, out KnownProperty known)) {
|
||||
this.knownEditors[known] = editorvm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.editors.AddRange (newEditors);
|
||||
OnAddEditors (newEditors);
|
||||
}
|
||||
|
||||
private void RemoveProperties (IEnumerable<EditorViewModel> oldEditors)
|
||||
{
|
||||
if (this.knownEditors != null) {
|
||||
foreach (EditorViewModel old in oldEditors) {
|
||||
this.knownEditors.Inverse.Remove (old);
|
||||
}
|
||||
}
|
||||
|
||||
this.editors.RemoveRange (oldEditors);
|
||||
OnRemoveEditors (oldEditors);
|
||||
}
|
||||
|
@ -315,10 +340,16 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
|
||||
if (newTypeName != editor.TargetType.Name)
|
||||
newTypeName = String.Format (PropertyEditing.Properties.Resources.MultipleTypesSelected, this.objEditors.Count);
|
||||
|
||||
if (!knownProperties)
|
||||
knownProperties = (editor.KnownProperties?.Count ?? 0) > 0;
|
||||
}
|
||||
|
||||
TypeName = newTypeName;
|
||||
|
||||
if (knownProperties && this.knownEditors == null)
|
||||
this.knownEditors = new BidirectionalDictionary<KnownProperty, EditorViewModel> ();
|
||||
|
||||
UpdateProperties (newPropertySet, removedEditors, newEditors);
|
||||
|
||||
EventsEnabled = events != null;
|
||||
|
@ -417,13 +448,6 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
return newEditors;
|
||||
}
|
||||
|
||||
private void UpdateResourceProvider()
|
||||
{
|
||||
foreach (PropertyViewModel vm in this.editors) {
|
||||
vm.ResourceProvider = this.resourceProvider;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnObjectEditorPropertiesChanged (object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateMembers();
|
||||
|
@ -431,9 +455,7 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
|
||||
private PropertyViewModel GetViewModel (IPropertyInfo property)
|
||||
{
|
||||
var vm = GetViewModelCore (property);
|
||||
vm.ResourceProvider = ResourceProvider;
|
||||
return vm;
|
||||
return GetViewModelCore (property);
|
||||
}
|
||||
|
||||
private PropertyViewModel GetViewModelCore (IPropertyInfo property)
|
||||
|
@ -473,7 +495,9 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
{ typeof(CommonRectangle), (tp,p,e) => new RectanglePropertyViewModel (tp, p, e) },
|
||||
{ typeof(CommonThickness), (tp,p,e) => new ThicknessPropertyViewModel (tp, p, e) },
|
||||
{ typeof(IList), (tp,p,e) => new CollectionPropertyViewModel (tp,p,e) },
|
||||
{ typeof(object), (tp,p,e) => new ObjectPropertyViewModel (tp,p,e) },
|
||||
{ typeof(BindingSource), (tp,p,e) => new PropertyViewModel<BindingSource> (tp, p, e) },
|
||||
{ typeof(Resource), (tp,p,e) => new PropertyViewModel<Resource> (tp, p, e) },
|
||||
{ typeof(object), (tp,p,e) => new ObjectPropertyViewModel (tp, p, e) },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.PropertyEditing.Drawing;
|
||||
|
||||
namespace Xamarin.PropertyEditing.ViewModels
|
||||
{
|
||||
internal class PropertyTreeRoot
|
||||
{
|
||||
internal PropertyTreeRoot (IEditorProvider provider, ITypeInfo type, IReadOnlyCollection<IPropertyInfo> properties)
|
||||
{
|
||||
if (provider == null)
|
||||
throw new ArgumentNullException (nameof(provider));
|
||||
if (type == null)
|
||||
throw new ArgumentNullException (nameof(type));
|
||||
if (properties == null)
|
||||
throw new ArgumentNullException (nameof(properties));
|
||||
|
||||
TargetType = type;
|
||||
Children = properties.Select (pi => new PropertyTreeElement (provider, pi)).ToArray ();
|
||||
}
|
||||
|
||||
public ITypeInfo TargetType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<PropertyTreeElement> Children
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
internal class PropertyTreeElement
|
||||
{
|
||||
internal PropertyTreeElement (IEditorProvider provider, IPropertyInfo property)
|
||||
{
|
||||
if (provider == null)
|
||||
throw new ArgumentNullException (nameof(provider));
|
||||
if (property == null)
|
||||
throw new ArgumentNullException (nameof(property));
|
||||
|
||||
Property = property;
|
||||
this.provider = provider;
|
||||
|
||||
this.properties = this.provider.GetPropertiesForTypeAsync (property.RealType);
|
||||
}
|
||||
|
||||
private PropertyTreeElement (IEditorProvider provider, IPropertyInfo property, PropertyTreeElement parent)
|
||||
: this (provider, property)
|
||||
{
|
||||
if (parent == null)
|
||||
throw new ArgumentNullException (nameof(parent));
|
||||
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
public IPropertyInfo Property
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool IsCollection
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public PropertyTreeElement Parent
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public AsyncValue<IReadOnlyCollection<PropertyTreeElement>> Children
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.children == null) {
|
||||
this.children = new AsyncValue<IReadOnlyCollection<PropertyTreeElement>> (
|
||||
this.properties.ContinueWith<IReadOnlyCollection<PropertyTreeElement>> (t =>
|
||||
t.Result.Select (p => new PropertyTreeElement (this.provider, p, this)).ToArray (), TaskScheduler.Default));
|
||||
}
|
||||
|
||||
|
||||
return this.children;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IEditorProvider provider;
|
||||
private readonly PropertyVariation variation;
|
||||
private readonly Task<IReadOnlyCollection<IPropertyInfo>> properties;
|
||||
private AsyncValue<IReadOnlyCollection<PropertyTreeElement>> children;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -28,6 +29,7 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
this.valueNavigator = property as ICanNavigateToSource;
|
||||
this.isNullable = (!property.ValueSources.HasFlag (ValueSources.Default) || property.Type.Name == NullableName);
|
||||
|
||||
RequestCreateBindingCommand = new RelayCommand (OnCreateBinding, CanCreateBinding);
|
||||
RequestCreateResourceCommand = new RelayCommand (OnCreateResource, CanCreateResource);
|
||||
NavigateToValueSourceCommand = new RelayCommand (OnNavigateToSource, CanNavigateToSource);
|
||||
SetValueResourceCommand = new RelayCommand<Resource> (OnSetValueToResource, CanSetValueToResource);
|
||||
|
@ -340,6 +342,23 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
OnSetValueToResource (resource);
|
||||
}
|
||||
|
||||
private bool CanCreateBinding ()
|
||||
{
|
||||
return SupportsBindings && Editors.Count == 1;
|
||||
}
|
||||
|
||||
private async void OnCreateBinding ()
|
||||
{
|
||||
var e = RequestCreateBinding ();
|
||||
if (e.BindingObject == null)
|
||||
return;
|
||||
|
||||
await SetValueAsync (new ValueInfo<TValue> {
|
||||
Source = ValueSource.Binding,
|
||||
ValueDescriptor = e.BindingObject
|
||||
});
|
||||
}
|
||||
|
||||
private static TValue DefaultValue;
|
||||
}
|
||||
|
||||
|
@ -356,10 +375,12 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
SetupConstraints ();
|
||||
|
||||
this.requestResourceCommand = new RelayCommand (OnRequestResource, CanRequestResource);
|
||||
|
||||
}
|
||||
|
||||
public event EventHandler<ResourceRequestedEventArgs> ResourceRequested;
|
||||
public event EventHandler<CreateResourceRequestedEventArgs> CreateResourceRequested;
|
||||
public event EventHandler<CreateBindingRequestedEventArgs> CreateBindingRequested;
|
||||
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
|
||||
|
||||
public IPropertyInfo Property
|
||||
|
@ -395,6 +416,11 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
get { return SupportsResources && (ResourceProvider?.CanCreateResources ?? false); }
|
||||
}
|
||||
|
||||
public bool SupportsBindings
|
||||
{
|
||||
get { return Property.CanWrite && TargetPlatform.BindingProvider != null && Property.ValueSources.HasFlag (ValueSources.Binding); }
|
||||
}
|
||||
|
||||
public abstract Resource Resource
|
||||
{
|
||||
get;
|
||||
|
@ -438,6 +464,12 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
|
||||
public ICommand RequestResourceCommand => this.requestResourceCommand;
|
||||
|
||||
public ICommand RequestCreateBindingCommand
|
||||
{
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
public ICommand RequestCreateResourceCommand
|
||||
{
|
||||
get;
|
||||
|
@ -521,6 +553,12 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
protected override void OnEditorsChanged (object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
base.OnEditorsChanged (sender, e);
|
||||
((RelayCommand)RequestCreateBindingCommand)?.ChangeCanExecute();
|
||||
}
|
||||
|
||||
protected override void SetupEditor (IObjectEditor editor)
|
||||
{
|
||||
if (editor == null)
|
||||
|
@ -539,6 +577,13 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
editor.PropertyChanged -= OnEditorPropertyChanged;
|
||||
}
|
||||
|
||||
protected CreateBindingRequestedEventArgs RequestCreateBinding ()
|
||||
{
|
||||
var e = new CreateBindingRequestedEventArgs ();
|
||||
CreateBindingRequested?.Invoke (this, e);
|
||||
return e;
|
||||
}
|
||||
|
||||
protected CreateResourceRequestedEventArgs RequestCreateResource ()
|
||||
{
|
||||
var e = new CreateResourceRequestedEventArgs ();
|
||||
|
|
|
@ -35,6 +35,32 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
}, TaskScheduler.FromCurrentSynchronizationContext());
|
||||
}
|
||||
|
||||
public ITypeInfo SelectedType
|
||||
{
|
||||
get { return this.selectedType; }
|
||||
set
|
||||
{
|
||||
if (this.selectedType == value)
|
||||
return;
|
||||
|
||||
this.selectedType = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public int TypeLevel
|
||||
{
|
||||
get { return this.typeLevel; }
|
||||
set
|
||||
{
|
||||
if (this.typeLevel == value)
|
||||
return;
|
||||
|
||||
this.typeLevel = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable Types
|
||||
{
|
||||
get { return this.assemblyView; }
|
||||
|
@ -80,7 +106,7 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
this.filterText = value;
|
||||
OnPropertyChanged();
|
||||
this.typeOptions.Filter = (!String.IsNullOrWhiteSpace (FilterText))
|
||||
? (o => ((ITypeInfo)o).Name.StartsWith (FilterText, StringComparison.OrdinalIgnoreCase))
|
||||
? (o => ((ITypeInfo)o).Name.Contains (FilterText, StringComparison.OrdinalIgnoreCase))
|
||||
: (Predicate<object>)null;
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +116,8 @@ namespace Xamarin.PropertyEditing.ViewModels
|
|||
private bool isLoading;
|
||||
private bool showAllAssemblies;
|
||||
private SimpleCollectionView assemblyView;
|
||||
private ITypeInfo selectedType;
|
||||
private int typeLevel = 1;
|
||||
|
||||
private bool AssemblyFilter (object item)
|
||||
{
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
<Compile Include="AsyncWorkQueue.cs" />
|
||||
<Compile Include="BidirectionalDictionary.cs" />
|
||||
<Compile Include="CategoryComparer.cs" />
|
||||
<Compile Include="Common\CommonValueConverter.cs" />
|
||||
<Compile Include="Drawing\CommonAlignment.cs" />
|
||||
<Compile Include="Drawing\CommonBrush.cs" />
|
||||
<Compile Include="Drawing\CommonBrushMappingMode.cs" />
|
||||
|
@ -75,6 +76,7 @@
|
|||
<Compile Include="Drawing\CommonImageBrush.cs" />
|
||||
<Compile Include="IAssemblyInfo.cs" />
|
||||
<Compile Include="IAvailabilityConstraint.cs" />
|
||||
<Compile Include="IBindingProvider.cs" />
|
||||
<Compile Include="ICanNavigateToSource.cs" />
|
||||
<Compile Include="IClampedPropertyInfo.cs" />
|
||||
<Compile Include="IColorSpaced.cs" />
|
||||
|
@ -86,6 +88,7 @@
|
|||
<Compile Include="IObjectEditor.cs" />
|
||||
<Compile Include="IHavePredefinedValues.cs" />
|
||||
<Compile Include="IPropertyInfo.cs" />
|
||||
<Compile Include="IProvidePath.cs" />
|
||||
<Compile Include="IResourceProvider.cs" />
|
||||
<Compile Include="ISelfConstrainedPropertyInfo.cs" />
|
||||
<Compile Include="ITypeInfo.cs" />
|
||||
|
@ -102,6 +105,7 @@
|
|||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="PropertyBinding.cs" />
|
||||
<Compile Include="PropertyVariation.cs" />
|
||||
<Compile Include="Reflection\ReflectionEditorProvider.cs" />
|
||||
<Compile Include="Reflection\ReflectionEnumPropertyInfo.cs" />
|
||||
|
@ -117,11 +121,14 @@
|
|||
<Compile Include="ViewModels\ArrangeModeViewModel.cs" />
|
||||
<Compile Include="ViewModels\BrushPropertyViewModel.cs" />
|
||||
<Compile Include="ViewModels\CollectionPropertyViewModel.cs" />
|
||||
<Compile Include="ViewModels\CreateBindingRequestedEventArgs.cs" />
|
||||
<Compile Include="ViewModels\CreateBindingViewModel.cs" />
|
||||
<Compile Include="ViewModels\CreateResourceRequestedEventArgs.cs" />
|
||||
<Compile Include="ViewModels\CreateResourceViewModel.cs" />
|
||||
<Compile Include="ViewModels\MaterialColorScale.cs" />
|
||||
<Compile Include="ViewModels\MaterialDesignColorViewModel.cs" />
|
||||
<Compile Include="ViewModels\CombinablePropertyViewModel.cs" />
|
||||
<Compile Include="ViewModels\PropertyTreeElement.cs" />
|
||||
<Compile Include="ViewModels\ResourceRequestedEventArgs.cs" />
|
||||
<Compile Include="ViewModels\ResourceSelectorViewModel.cs" />
|
||||
<Compile Include="ViewModels\SimpleCollectionView.cs" />
|
||||
|
@ -153,6 +160,7 @@
|
|||
<Compile Include="Controls\StringConversionExtensions.cs" />
|
||||
<Compile Include="ViewModels\RectanglePropertyViewModel.cs" />
|
||||
<Compile Include="ViewModels\ThicknessPropertyViewModel.cs" />
|
||||
<Compile Include="ViewModels\ObjectTreeElement.cs" />
|
||||
<Compile Include="ViewModels\TypeSelectorViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -163,6 +171,7 @@
|
|||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.*.resx">
|
||||
<Visible>false</Visible>
|
||||
|
|
Загрузка…
Ссылка в новой задаче