diff options
author | Eric Maupin <me@ermau.com> | 2018-09-18 18:01:24 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-18 18:01:24 +0300 |
commit | 3a99673a4082de39bedc5018857d5f52fea66e1a (patch) | |
tree | 5f0005e59c60346ceb093811ce4e984a9cd08bed | |
parent | cdf1ae946a46435a78ae8dc6d0a668c48aca436f (diff) | |
parent | 06c76b699655fd789a9c2c9ef3610d028b07cda9 (diff) |
Merge pull request #406 from xamarin/ermau-fix-403
[Win] Support unconstrained values in editor with predefined values
8 files changed, 127 insertions, 26 deletions
diff --git a/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs b/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs index b7685ba..8004461 100644 --- a/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs +++ b/Xamarin.PropertyEditing.Tests/MockControls/MockControl.cs @@ -14,14 +14,14 @@ 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 | ValueSources.Binding) + string description = null, bool constrained = true, ValueSources valueSources = ValueSources.Local | ValueSources.Default | ValueSources.Binding) { IPropertyInfo propertyInfo; if (typeof(T).IsEnum) { var underlyingType = typeof (T).GetEnumUnderlyingType (); var enumPropertyInfoType = typeof (MockEnumPropertyInfo<,>) .MakeGenericType (underlyingType, typeof (T)); - propertyInfo = (IPropertyInfo)Activator.CreateInstance (enumPropertyInfoType, name, description, category, canWrite, flag, converterTypes); + propertyInfo = (IPropertyInfo)Activator.CreateInstance (enumPropertyInfoType, name, description, category, canWrite, flag, converterTypes, constrained); } else { propertyInfo = new MockPropertyInfo<T> (name, description, category, canWrite, converterTypes, valueSources); } diff --git a/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs b/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs index faafc83..3fe6e51 100644 --- a/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs +++ b/Xamarin.PropertyEditing.Tests/MockControls/MockSampleControl.cs @@ -16,7 +16,7 @@ namespace Xamarin.PropertyEditing.Tests.MockControls AddProperty<int> ("UnsetInteger", ReadWrite, valueSources: ValueSources.Local); AddProperty<float> ("FloatingPoint", ReadWrite); AddProperty<string> ("String", ReadWrite, valueSources: ValueSources.Local | ValueSources.Resource | ValueSources.Binding); - AddProperty<Enumeration> ("Enumeration", ReadWrite); + AddProperty<Enumeration> ("Enumeration", ReadWrite, constrained: false); AddProperty<FlagsNoValues> ("FlagsNoValues", ReadWrite, canWrite: true, flag: true); AddProperty<FlagsWithValues> ("FlagsWithValues", ReadWrite, canWrite: true, flag: true); AddProperty<CommonPoint> ("Point", ReadWrite); diff --git a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockEnumPropertyInfo.cs b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockEnumPropertyInfo.cs index 42342f9..fd4be86 100644 --- a/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockEnumPropertyInfo.cs +++ b/Xamarin.PropertyEditing.Tests/MockPropertyInfo/MockEnumPropertyInfo.cs @@ -15,7 +15,7 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo where TUnderlying : struct where TEnum : struct { - public MockEnumPropertyInfo (string name, string description = null, string category = "", bool canWrite = true, bool flag = false, IEnumerable<Type> converterTypes = null) + public MockEnumPropertyInfo (string name, string description = null, string category = "", bool canWrite = true, bool flag = false, IEnumerable<Type> converterTypes = null, bool constrained = true) : base (name, description, category, canWrite, converterTypes) { var names = Enum.GetNames (typeof (TEnum)); @@ -33,12 +33,13 @@ namespace Xamarin.PropertyEditing.Tests.MockPropertyInfo PredefinedValues = predefinedValues; } + IsConstrainedToPredefined = constrained; IsValueCombinable = flag; } public override Type Type => typeof (TEnum); - public bool IsConstrainedToPredefined => true; + public bool IsConstrainedToPredefined { get; } public bool IsValueCombinable { get; } diff --git a/Xamarin.PropertyEditing.Tests/PredefinedValuesViewModelTests.cs b/Xamarin.PropertyEditing.Tests/PredefinedValuesViewModelTests.cs index bb33343..f62b7d6 100644 --- a/Xamarin.PropertyEditing.Tests/PredefinedValuesViewModelTests.cs +++ b/Xamarin.PropertyEditing.Tests/PredefinedValuesViewModelTests.cs @@ -270,14 +270,38 @@ namespace Xamarin.PropertyEditing.Tests [TestFixture] internal class UnconstrainedPredefinedViewModelTests - : PredefinedValuesViewModelTests<string> + : PredefinedValuesViewModelTests<int> { [Test] + public async Task ValueNameRepresentsValueDescriptor () + { + var value = GetNonDefaultRandomTestValue (); + + var property = GetPropertyMock (); + var predefined = property.As<IHavePredefinedValues<int>> (); + predefined.SetupGet (p => p.PredefinedValues).Returns (new Dictionary<string, int> { + { "Value", 0 }, + }); + predefined.SetupGet (p => p.IsConstrainedToPredefined).Returns (false); + + var outOfBounds = "random"; + + var editor = GetBasicEditor (property.Object); + await editor.SetValueAsync (property.Object, new ValueInfo<int> { + Source = ValueSource.Local, + ValueDescriptor = outOfBounds + }); + + var vm = GetViewModel (property.Object, new[] { editor }); + Assert.That (vm.ValueName, Is.EqualTo (outOfBounds)); + } + + [Test] public void ValueDescriptorForUnconstrained () { var property = GetPropertyMock (); - var predefined = property.As<IHavePredefinedValues<string>> (); - predefined.SetupGet (p => p.PredefinedValues).Returns (new Dictionary<string, string> { + var predefined = property.As<IHavePredefinedValues<int>> (); + predefined.SetupGet (p => p.PredefinedValues).Returns (new Dictionary<string, int> { { "Value", GetNonDefaultRandomTestValue () }, }); predefined.SetupGet (p => p.IsConstrainedToPredefined).Returns (false); @@ -289,7 +313,7 @@ namespace Xamarin.PropertyEditing.Tests vm.ValueName = "test"; - var info = editor.values[property.Object] as ValueInfo<string>; + var info = editor.values[property.Object] as ValueInfo<int>; Assert.That (info, Is.Not.Null); Assert.That (info.ValueDescriptor, Is.EqualTo ("test")); Assert.That (info.Source, Is.EqualTo (ValueSource.Local)); @@ -300,8 +324,8 @@ namespace Xamarin.PropertyEditing.Tests public void ValueDescriptorEmptyNotNullUnconstrained () { var property = GetPropertyMock (); - var predefined = property.As<IHavePredefinedValues<string>> (); - predefined.SetupGet (p => p.PredefinedValues).Returns (new Dictionary<string, string> { + var predefined = property.As<IHavePredefinedValues<int>> (); + predefined.SetupGet (p => p.PredefinedValues).Returns (new Dictionary<string, int> { { "Value", GetNonDefaultRandomTestValue () }, }); predefined.SetupGet (p => p.IsConstrainedToPredefined).Returns (false); @@ -314,28 +338,39 @@ namespace Xamarin.PropertyEditing.Tests vm.ValueName = "test"; vm.ValueName = null; - var info = editor.values[property.Object] as ValueInfo<string>; + var info = editor.values[property.Object] as ValueInfo<int>; Assert.That (info, Is.Not.Null); Assert.That (info.ValueDescriptor, Is.EqualTo (String.Empty)); Assert.That (info.Source, Is.EqualTo (ValueSource.Local)); } - protected override string GetRandomTestValue (Random rand) + protected override int GetRandomTestValue (Random rand) { - string[] names = Enum.GetNames (typeof(PredefinedEnumTest)); - int index = rand.Next (0, names.Length); - return names[index]; + int[] values = (int[])Enum.GetValues (typeof(PredefinedEnumTest)); + int index = rand.Next (0, values.Length); + return values[index]; } - protected override PredefinedValuesViewModel<string> GetViewModel (TargetPlatform platform, IPropertyInfo property, IEnumerable<IObjectEditor> editors) + protected override PredefinedValuesViewModel<int> GetViewModel (TargetPlatform platform, IPropertyInfo property, IEnumerable<IObjectEditor> editors) { - return new PredefinedValuesViewModel<string> (platform, property, editors); + return new PredefinedValuesViewModel<int> (platform, property, editors); } protected override bool IsConstrained => false; - protected override IReadOnlyDictionary<string, string> Values => - Enum.GetNames (typeof(PredefinedEnumTest)).ToDictionary (s => s, s => s); + protected override IReadOnlyDictionary<string, int> Values + { + get + { + string[] names = Enum.GetNames (typeof (PredefinedEnumTest)); + int[] values = (int[])Enum.GetValues (typeof(PredefinedEnumTest)); + var dict = new Dictionary<string, int> (); + for (int i = 0; i < names.Length; i++) + dict.Add (names[i], values[i]); + + return dict; + } + } } [TestFixture] diff --git a/Xamarin.PropertyEditing.Windows/ComboBoxEx.cs b/Xamarin.PropertyEditing.Windows/ComboBoxEx.cs new file mode 100644 index 0000000..d4369ce --- /dev/null +++ b/Xamarin.PropertyEditing.Windows/ComboBoxEx.cs @@ -0,0 +1,60 @@ +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace Xamarin.PropertyEditing.Windows +{ + internal class ComboBoxEx + : ComboBox + { + static ComboBoxEx () + { + DefaultStyleKeyProperty.OverrideMetadata (typeof (ComboBoxEx), new FrameworkPropertyMetadata (typeof (ComboBoxEx))); + } + + public static readonly DependencyProperty EnableSubmitProperty = DependencyProperty.Register ( + "EnableSubmit", typeof (bool), typeof (ComboBoxEx), new PropertyMetadata (true)); + + public bool EnableSubmit + { + get { return (bool)GetValue (EnableSubmitProperty); } + set { SetValue (EnableSubmitProperty, value); } + } + + protected override void OnSelectionChanged (SelectionChangedEventArgs e) + { + base.OnSelectionChanged (e); + Submit(); + } + + protected override void OnKeyDown (KeyEventArgs e) + { + if (e.Key == Key.Enter) { + Submit (); + e.Handled = true; + } + + base.OnKeyDown (e); + } + + protected override void OnLostKeyboardFocus (KeyboardFocusChangedEventArgs e) + { + base.OnLostKeyboardFocus (e); + Submit (); + } + + protected virtual void OnSubmit () + { + var expression = GetBindingExpression (TextProperty); + expression?.UpdateSource (); + } + + private void Submit () + { + if (!EnableSubmit) + return; + + OnSubmit(); + } + } +} diff --git a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml index 75b5b92..3f61759 100644 --- a/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml +++ b/Xamarin.PropertyEditing.Windows/Themes/Resources.xaml @@ -65,7 +65,7 @@ <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:EnumEditorControl}"> - <ComboBox x:Name="ComboBox" ItemsSource="{Binding PossibleValues,Mode=OneTime}" SelectedItem="{Binding ValueName}" IsEditable="{Binding IsConstrainedToPredefined,Mode=OneTime,Converter={StaticResource OppositeBoolConverter}}" VerticalContentAlignment="Center" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" /> + <local:ComboBoxEx x:Name="ComboBox" ItemsSource="{Binding PossibleValues,Mode=OneTime}" IsEditable="{Binding IsConstrainedToPredefined,Mode=OneTime,Converter={StaticResource OppositeBoolConverter}}" Text="{Binding ValueName,Mode=TwoWay,UpdateSourceTrigger=Explicit}" VerticalContentAlignment="Center" AutomationProperties.Name="{Binding Property.Name,Mode=OneTime}" /> </ControlTemplate> </Setter.Value> </Setter> @@ -2864,4 +2864,7 @@ </Trigger> </Style.Triggers> </Style> + + <Style TargetType="{x:Type local:ComboBoxEx}" BasedOn="{StaticResource {x:Type ComboBox}}"> + </Style> </ResourceDictionary> diff --git a/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj b/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj index 29b4c67..0f9563f 100644 --- a/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj +++ b/Xamarin.PropertyEditing.Windows/Xamarin.PropertyEditing.Windows.csproj @@ -71,6 +71,7 @@ <DependentUpon>CollectionEditorWindow.xaml</DependentUpon> </Compile> <Compile Include="CombinablePredefinedValuesEditorControl.cs" /> + <Compile Include="ComboBoxEx.cs" /> <Compile Include="CommonBrushToBrushConverter.cs" /> <Compile Include="ByteToPercentageConverter.cs" /> <Compile Include="ChoiceControl.cs" /> diff --git a/Xamarin.PropertyEditing/ViewModels/PredefinedValuesViewModel.cs b/Xamarin.PropertyEditing/ViewModels/PredefinedValuesViewModel.cs index 3a6744d..2e0dbf7 100644 --- a/Xamarin.PropertyEditing/ViewModels/PredefinedValuesViewModel.cs +++ b/Xamarin.PropertyEditing/ViewModels/PredefinedValuesViewModel.cs @@ -38,7 +38,7 @@ namespace Xamarin.PropertyEditing.ViewModels public string ValueName { get { return this.valueName; } - set { SetValue (value); } + set { SetValueName (value); } } public bool IsConstrainedToPredefined => this.predefinedValues.IsConstrainedToPredefined; @@ -64,7 +64,7 @@ namespace Xamarin.PropertyEditing.ViewModels private string valueName; private readonly IHavePredefinedValues<TValue> predefinedValues; - private async void SetValue (string value) + private async void SetValueName (string value) { if (value == this.valueName) return; @@ -84,9 +84,6 @@ namespace Xamarin.PropertyEditing.ViewModels }); } else Value = realValue; - - this.valueName = value; - OnPropertyChanged (); } private bool IsValueDefined (TValue value) @@ -113,6 +110,10 @@ namespace Xamarin.PropertyEditing.ViewModels if (ValueSource == ValueSource.Unset) { this.valueName = String.Empty; OnPropertyChanged (nameof(ValueName)); + // Order relevant: Value may default() to a valid value, so ValueDescriptor takes precedence + } else if (!IsConstrainedToPredefined && CurrentValue != null && CurrentValue.ValueDescriptor is string custom) { + this.valueName = custom; + OnPropertyChanged (nameof (ValueName)); } else if (TryGetValueName (Value, out string newValueName)) { this.valueName = newValueName; OnPropertyChanged (nameof(ValueName)); |