diff options
author | Eric Maupin <ermaup@microsoft.com> | 2017-01-13 03:51:48 +0300 |
---|---|---|
committer | Eric Maupin <ermaup@microsoft.com> | 2017-01-13 04:08:06 +0300 |
commit | 97b9ce0721dbc556434c58823cedac31ec124dc7 (patch) | |
tree | 20e951ab5ca808409e727293ad1ba967e8768e61 | |
parent | 6e36173e085923ea2c89c35fdf6c38fa309e5fe7 (diff) |
Reflection editor, property coalescing
Still need to implement equality for IPropertyInfo, the properties setup
will not match like properties yet without it.
15 files changed, 279 insertions, 35 deletions
diff --git a/Xamarin.PropertyEditing.Windows.Standalone/MainWindow.xaml b/Xamarin.PropertyEditing.Windows.Standalone/MainWindow.xaml index b3e5ff7..634152b 100644 --- a/Xamarin.PropertyEditing.Windows.Standalone/MainWindow.xaml +++ b/Xamarin.PropertyEditing.Windows.Standalone/MainWindow.xaml @@ -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="MainWindow" Height="350" Width="525"> + Title="Property editor" Height="600" Width="525"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> diff --git a/Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs b/Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs new file mode 100644 index 0000000..641437c --- /dev/null +++ b/Xamarin.PropertyEditing.Windows/EditorPropertySelector.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using Xamarin.PropertyEditing.ViewModels; + +namespace Xamarin.PropertyEditing.Windows +{ + internal class EditorPropertySelector + : DataTemplateSelector + { + public override DataTemplate SelectTemplate (object item, DependencyObject container) + { + if (item != null) { + Type type = item.GetType (); + + DataTemplate template = null; + if (!this.templates.TryGetValue (type, out template)) { + Type controlType; + if (TypeMap.TryGetValue (type, out controlType)) { + this.templates[type] = template = new DataTemplate (type) { + VisualTree = new FrameworkElementFactory (controlType) + }; + } + } + + if (template != null) + return template; + } + + return base.SelectTemplate (item, container); + } + + private readonly Dictionary<Type, DataTemplate> templates = new Dictionary<Type, DataTemplate> (); + + // We can improve on this, but it'll get us started + private static readonly Dictionary<Type, Type> TypeMap = new Dictionary<Type, Type> { + { typeof(StringPropertyViewModel), typeof(StringEditorControl) } + }; + } +} diff --git a/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs b/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs index e985912..dfb4837 100644 --- a/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs +++ b/Xamarin.PropertyEditing.Windows/PropertyEditorPanel.cs @@ -1,13 +1,54 @@ -using System.Windows.Controls; +using System.Collections; +using System.Collections.ObjectModel; +using System.Windows; +using System.Windows.Controls; +using Xamarin.PropertyEditing.ViewModels; namespace Xamarin.PropertyEditing.Windows { + [TemplatePart (Name = "propertyItems", Type = typeof(ItemsControl))] public class PropertyEditorPanel - : ItemsControl + : Control { public PropertyEditorPanel () { DefaultStyleKey = typeof(PropertyEditorPanel); + SelectedItems = new ObservableCollection<object> (); + } + + public static readonly DependencyProperty EditorProviderProperty = DependencyProperty.Register ( + "EditorProvider", typeof(IEditorProvider), typeof(PropertyEditorPanel), new PropertyMetadata (default(IEditorProvider))); + + public IEditorProvider EditorProvider + { + get { return (IEditorProvider) GetValue (EditorProviderProperty); } + set { SetValue (EditorProviderProperty, value); } + } + + private static readonly DependencyPropertyKey SelectedItemsPropertyKey = DependencyProperty.RegisterReadOnly ( + "SelectedItems", typeof(IList), typeof(PropertyEditorPanel), new PropertyMetadata (default(IList))); + + public static readonly DependencyProperty SelectedItemsProperty = SelectedItemsPropertyKey.DependencyProperty; + + public IList SelectedItems + { + get { return (IList) GetValue (SelectedItemsProperty); } + private set { SetValue (SelectedItemsPropertyKey, value); } + } + + public override void OnApplyTemplate () + { + base.OnApplyTemplate (); + + this.items = (ItemsControl)GetTemplateChild ("propertyItems"); + this.items.DataContext = new PanelViewModel (EditorProvider); + } + + private ItemsControl items; + + private void OnEditorChanged () + { + } } }
\ No newline at end of file diff --git a/Xamarin.PropertyEditing.Windows/Resources.xaml b/Xamarin.PropertyEditing.Windows/Resources.xaml index bba0ef9..d79ee64 100644 --- a/Xamarin.PropertyEditing.Windows/Resources.xaml +++ b/Xamarin.PropertyEditing.Windows/Resources.xaml @@ -21,5 +21,16 @@ <Style TargetType="local:PropertyEditorPanel"> <Setter Property="Grid.IsSharedSizeScope" Value="True" /> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate> + <ItemsControl Name="propertyItems"> + <ItemsControl.ItemTemplateSelector> + <local:EditorPropertySelector /> + </ItemsControl.ItemTemplateSelector> + </ItemsControl> + </ControlTemplate> + </Setter.Value> + </Setter> </Style> </ResourceDictionary>
\ No newline at end of file diff --git a/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj b/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj index 4dda26f..308ddf2 100644 --- a/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj +++ b/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj @@ -35,6 +35,7 @@ <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Windows.Presentation" /> + <Reference Include="System.Xaml" /> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="Microsoft.CSharp" /> @@ -47,6 +48,7 @@ <Compile Include="..\Xamarin.PropertyEditing\Properties\GlobalAssemblyInfo.cs"> <Link>Properties\GlobalAssemblyInfo.cs</Link> </Compile> + <Compile Include="EditorPropertySelector.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="PropertyEditorControl.cs" /> <Compile Include="PropertyEditorPanel.cs" /> @@ -58,5 +60,11 @@ <Generator>MSBuild:Compile</Generator> </Page> </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Xamarin.PropertyEditing\Xamarin.PropertyEditing.csproj"> + <Project>{A0B6FE73-D046-4E1C-BA9D-F20683889C5A}</Project> + <Name>Xamarin.PropertyEditing</Name> + </ProjectReference> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/Xamarin.sln b/Xamarin.PropertyEditing.sln index 88a0925..88a0925 100644 --- a/Xamarin.sln +++ b/Xamarin.PropertyEditing.sln diff --git a/Xamarin.PropertyEditing/Reflection/ReflectionEditorProvider.cs b/Xamarin.PropertyEditing/Reflection/ReflectionEditorProvider.cs index d49fd2c..9826f3d 100644 --- a/Xamarin.PropertyEditing/Reflection/ReflectionEditorProvider.cs +++ b/Xamarin.PropertyEditing/Reflection/ReflectionEditorProvider.cs @@ -8,7 +8,7 @@ namespace Xamarin.PropertyEditing.Reflection { public Task<IObjectEditor> GetObjectEditorAsync (object item) { - throw new NotImplementedException (); + return Task.FromResult<IObjectEditor> (new ReflectionObjectEditor (item)); } } }
\ No newline at end of file diff --git a/Xamarin.PropertyEditing/Reflection/ReflectionObjectEditor.cs b/Xamarin.PropertyEditing/Reflection/ReflectionObjectEditor.cs index f9cc694..4a36475 100644 --- a/Xamarin.PropertyEditing/Reflection/ReflectionObjectEditor.cs +++ b/Xamarin.PropertyEditing/Reflection/ReflectionObjectEditor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; namespace Xamarin.PropertyEditing.Reflection @@ -7,31 +8,56 @@ namespace Xamarin.PropertyEditing.Reflection public class ReflectionObjectEditor : IObjectEditor { - public event EventHandler<EditorPropertyChangedEventArgs> PropertyChanged; - - public IReadOnlyCollection<IPropertyInfo> Properties + public ReflectionObjectEditor (object target) { - get; - } + if (target == null) + throw new ArgumentNullException (nameof (target)); - public IObjectEditor Parent - { - get; - } + this.target = target; - public IReadOnlyList<IObjectEditor> DirectChildren - { - get; + foreach (PropertyInfo property in target.GetType ().GetProperties ()) { + this.properties.Add (new ReflectionPropertyInfo (property)); + } } - - public Task SetValueAsync<T> (IPropertyInfo property, ValueInfo<T> value, PropertyVariation variation = null) + + public event EventHandler<EditorPropertyChangedEventArgs> PropertyChanged; + + public IReadOnlyCollection<IPropertyInfo> Properties => this.properties; + + public IObjectEditor Parent => null; + + public IReadOnlyList<IObjectEditor> DirectChildren => EmptyDirectChildren; + + public async Task SetValueAsync<T> (IPropertyInfo property, ValueInfo<T> value, PropertyVariation variation = null) { - throw new NotImplementedException (); + if (property == null) + throw new ArgumentNullException (nameof (property)); + + ReflectionPropertyInfo info = property as ReflectionPropertyInfo; + if (info == null) + throw new ArgumentException(); + + info.SetValue (this.target, value.Value); } public Task<ValueInfo<T>> GetValueAsync<T> (IPropertyInfo property, PropertyVariation variation = null) { - throw new NotImplementedException (); + if (property == null) + throw new ArgumentNullException (nameof (property)); + + ReflectionPropertyInfo info = property as ReflectionPropertyInfo; + if (info == null) + throw new ArgumentException(); + + return Task.FromResult (new ValueInfo<T> { + Source = ValueSource.Local, + Value = info.GetValue<T> (this.target) + }); } + + private readonly object target; + private readonly List<ReflectionPropertyInfo> properties = new List<ReflectionPropertyInfo> (); + + private static readonly IObjectEditor[] EmptyDirectChildren = new IObjectEditor[0]; } }
\ No newline at end of file diff --git a/Xamarin.PropertyEditing/Reflection/ReflectionPropertyInfo.cs b/Xamarin.PropertyEditing/Reflection/ReflectionPropertyInfo.cs new file mode 100644 index 0000000..c02b131 --- /dev/null +++ b/Xamarin.PropertyEditing/Reflection/ReflectionPropertyInfo.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; + +namespace Xamarin.PropertyEditing.Reflection +{ + public class ReflectionPropertyInfo + : IPropertyInfo + { + public ReflectionPropertyInfo (PropertyInfo propertyInfo) + { + this.propertyInfo = propertyInfo; + + this.category = new Lazy<string> (() => { + CategoryAttribute categoryAttribute = this.propertyInfo.GetCustomAttribute<CategoryAttribute> (); + return categoryAttribute?.Category; + }); + } + + public string Name => this.propertyInfo.Name; + + public Type Type => this.propertyInfo.PropertyType; + + public string Category => this.category.Value; + + public ValueSources ValueSources => ValueSources.Local; + + public IReadOnlyList<PropertyVariation> Variations => EmtpyVariations; + + public IReadOnlyList<IAvailabilityConstraint> AvailabilityConstraints => EmptyConstraints; + + public void SetValue<T> (object target, T value) + { + this.propertyInfo.SetValue (target, value); + } + + public T GetValue<T> (object target) + { + return (T)this.propertyInfo.GetValue (target); + } + + private readonly Lazy<string> category; + + private readonly PropertyInfo propertyInfo; + + private static readonly IAvailabilityConstraint[] EmptyConstraints = new IAvailabilityConstraint[0]; + private static readonly PropertyVariation[] EmtpyVariations = new PropertyVariation[0]; + } +} diff --git a/Xamarin.PropertyEditing/ViewModels/Extensions.cs b/Xamarin.PropertyEditing/ViewModels/Extensions.cs new file mode 100644 index 0000000..2dfc072 --- /dev/null +++ b/Xamarin.PropertyEditing/ViewModels/Extensions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Xamarin.PropertyEditing.ViewModels +{ + internal static class Extensions + { + public static void AddRange<T> (this ICollection<T> self, IEnumerable<T> range) + { + foreach (T item in range) + self.Add (item); + } + } +} diff --git a/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs index db6e4af..6cc61e5 100644 --- a/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs +++ b/Xamarin.PropertyEditing/ViewModels/PanelViewModel.cs @@ -24,14 +24,21 @@ namespace Xamarin.PropertyEditing.ViewModels get; } - protected override Task<IReadOnlyList<IObjectEditor>> GetEditorsAsync () + protected override async Task<IReadOnlyList<IObjectEditor>> GetEditorsAsync () { - throw new NotImplementedException(); + int i = 0; + Task<IObjectEditor>[] editorTasks = new Task<IObjectEditor>[SelectedObjects.Count]; + foreach (object item in SelectedObjects) { + editorTasks[i++] = EditorProvider.GetObjectEditorAsync (item); + } + + return await Task.WhenAll (editorTasks).ConfigureAwait (false); } private async void OnSelectedObjectsChanged (object sender, NotifyCollectionChangedEventArgs e) { - + // this needs to be more connected with the object changes to allow for better reuse + OnPropertiesChanged (); } } }
\ No newline at end of file diff --git a/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs index e5209c7..4377a47 100644 --- a/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs +++ b/Xamarin.PropertyEditing/ViewModels/PropertiesViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Threading.Tasks; namespace Xamarin.PropertyEditing.ViewModels @@ -27,9 +28,39 @@ namespace Xamarin.PropertyEditing.ViewModels protected async void OnPropertiesChanged () { + IReadOnlyList<IObjectEditor> editors = await GetEditorsAsync (); + if (editors.Count == 0) { + this.properties.Clear(); + return; + } + var newSet = new HashSet<IPropertyInfo> (this.properties.Select (vm => vm.Property)); + for (int i = 0; i < editors.Count; i++) { + newSet.IntersectWith (editors[i].Properties); + } + + foreach (PropertyViewModel vm in this.properties.ToArray()) { + if (!newSet.Remove (vm.Property)) { + this.properties.Remove (vm); + continue; + } + + foreach (IObjectEditor editor in editors) { + if (!vm.Editors.Contains (editor)) + vm.Editors.Add (editor); + } + } + + foreach (IPropertyInfo property in newSet) { + this.properties.Add (GetViewModel (property, editors)); + } } - private readonly ObservableCollection<PropertyViewModel> properties; + private readonly ObservableCollection<PropertyViewModel> properties = new ObservableCollection<PropertyViewModel> (); + + private PropertyViewModel GetViewModel (IPropertyInfo property, IEnumerable<IObjectEditor> editors) + { + return new StringPropertyViewModel (property, editors); + } } }
\ No newline at end of file diff --git a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs index 964c859..9c7a1b0 100644 --- a/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs +++ b/Xamarin.PropertyEditing/ViewModels/PropertyViewModel.cs @@ -10,8 +10,8 @@ namespace Xamarin.PropertyEditing.ViewModels { private TValue value; - protected PropertyViewModel (IPropertyInfo property, IObjectEditor editor) - : base (property, editor) + protected PropertyViewModel (IPropertyInfo property, IEnumerable<IObjectEditor> editors) + : base (property, editors) { } @@ -29,19 +29,19 @@ namespace Xamarin.PropertyEditing.ViewModels internal abstract class PropertyViewModel : ViewModelBase { - public PropertyViewModel (IPropertyInfo property, IObjectEditor editor) + protected PropertyViewModel (IPropertyInfo property, IEnumerable<IObjectEditor> editors) { if (property == null) throw new ArgumentNullException (nameof (property)); - if (editor == null) - throw new ArgumentNullException (nameof (editor)); + if (editors == null) + throw new ArgumentNullException (nameof (editors)); Property = property; - var editors = new ObservableCollection<IObjectEditor> (); - editors.CollectionChanged += OnEditorsChanged; - editors.Add (editor); - Editors = editors; + var observableEditors = new ObservableCollection<IObjectEditor>(); + observableEditors.CollectionChanged += OnEditorsChanged; + observableEditors.AddRange (editors); // Purposefully after the event hookup + Editors = observableEditors; } public IPropertyInfo Property @@ -89,8 +89,8 @@ namespace Xamarin.PropertyEditing.ViewModels private set { this.valueModel = value; - OnPropertyChanged(); - OnPropertyChanged(nameof (CanDelve)); + OnPropertyChanged (); + OnPropertyChanged (nameof (CanDelve)); } } diff --git a/Xamarin.PropertyEditing/ViewModels/StringPropertyViewModel.cs b/Xamarin.PropertyEditing/ViewModels/StringPropertyViewModel.cs new file mode 100644 index 0000000..06a374d --- /dev/null +++ b/Xamarin.PropertyEditing/ViewModels/StringPropertyViewModel.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Xamarin.PropertyEditing.ViewModels +{ + internal class StringPropertyViewModel + : PropertyViewModel<string> + { + public StringPropertyViewModel (IPropertyInfo property, IEnumerable<IObjectEditor> editors) + : base (property, editors) + { + } + } +} diff --git a/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj b/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj index 91bdaf1..bcff41e 100644 --- a/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj +++ b/Xamarin.PropertyEditing/Xamarin.PropertyEditing.csproj @@ -52,13 +52,16 @@ <Compile Include="PropertyVariation.cs" /> <Compile Include="Reflection\ReflectionEditorProvider.cs" /> <Compile Include="Reflection\ReflectionObjectEditor.cs" /> + <Compile Include="Reflection\ReflectionPropertyInfo.cs" /> <Compile Include="ValueInfo.cs" /> <Compile Include="ValueSource.cs" /> + <Compile Include="ViewModels\Extensions.cs" /> <Compile Include="ViewModels\ObjectViewModel.cs" /> <Compile Include="ViewModels\PanelViewModel.cs" /> <Compile Include="ViewModels\PropertiesViewModel.cs" /> <Compile Include="ViewModels\PropertyViewModel.cs" /> <Compile Include="ViewModels\RelayCommand.cs" /> + <Compile Include="ViewModels\StringPropertyViewModel.cs" /> <Compile Include="ViewModels\ViewModelBase.cs" /> </ItemGroup> <ItemGroup /> |