diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs b/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs index c27d99a8f9a06..12b23cdc6f93e 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs @@ -109,7 +109,20 @@ public bool TryFetch(OptionKey2 optionKey, string storageKey, out object? value) var underlyingType = Nullable.GetUnderlyingType(storageType); if (underlyingType?.IsEnum == true) - return manager.TryGetValue(storageKey, out int? value) == GetValueResult.Success ? (value.HasValue ? Enum.ToObject(underlyingType, value.Value) : null) : default(Optional); + { + if (manager.TryGetValue(storageKey, out int? nullableValue) == GetValueResult.Success) + { + return nullableValue.HasValue ? Enum.ToObject(underlyingType, nullableValue.Value) : null; + } + else if (manager.TryGetValue(storageKey, out int value) == GetValueResult.Success) + { + return Enum.ToObject(underlyingType, value); + } + else + { + return default; + } + } if (storageType == typeof(NamingStylePreferences)) { diff --git a/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs b/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs index 3ad47a5d3fa47..45aeb866aeb75 100644 --- a/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs +++ b/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs @@ -191,6 +191,40 @@ private protected void BindToOption(ComboBox comboBox, PerLanguageOption2 _bindingExpressions.Add(bindingExpression); } + private protected void BindToOption(RadioButton radiobutton, Option2 optionKey, T optionValue) + { + var binding = new Binding() + { + Source = new OptionBinding(OptionStore, optionKey), + Path = new PropertyPath("Value"), + UpdateSourceTrigger = UpdateSourceTrigger.Default, + Converter = new RadioButtonCheckedConverter(), + ConverterParameter = optionValue + }; + + AddSearchHandler(radiobutton); + + var bindingExpression = radiobutton.SetBinding(RadioButton.IsCheckedProperty, binding); + _bindingExpressions.Add(bindingExpression); + } + + private protected void BindToOption(RadioButton radioButton, Option2 nullableOptionKey, T optionValue, Func onNullValue) where T : struct + { + var binding = new Binding() + { + Source = new OptionBinding(OptionStore, nullableOptionKey), + Path = new PropertyPath("Value"), + UpdateSourceTrigger = UpdateSourceTrigger.Default, + Converter = new RadioButtonCheckedConverter(onNullValue), + ConverterParameter = optionValue, + }; + + AddSearchHandler(radioButton); + + var bindingExpression = radioButton.SetBinding(RadioButton.IsCheckedProperty, binding); + _bindingExpressions.Add(bindingExpression); + } + private protected void BindToOption(RadioButton radiobutton, PerLanguageOption2 optionKey, T optionValue, string languageName) { var binding = new Binding() @@ -255,19 +289,26 @@ private protected void AddSearchHandler(ContentControl control) } } - public class RadioButtonCheckedConverter : IValueConverter + public sealed class RadioButtonCheckedConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, - System.Globalization.CultureInfo culture) - { - return value.Equals(parameter); - } + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + => value.Equals(parameter); - public object ConvertBack(object value, Type targetType, object parameter, - System.Globalization.CultureInfo culture) - { - return value.Equals(true) ? parameter : Binding.DoNothing; - } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + => value.Equals(true) ? parameter : Binding.DoNothing; + } + + public sealed class RadioButtonCheckedConverter(Func onNullValue) : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + => value switch + { + null => onNullValue(), + _ => value.Equals(parameter), + }; + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + => value.Equals(true) ? parameter : Binding.DoNothing; } public class ComboBoxItemTagToIndexConverter : IValueConverter diff --git a/src/VisualStudio/Core/Impl/Options/Converters/NullableBoolOptionConverter.cs b/src/VisualStudio/Core/Impl/Options/Converters/NullableBoolOptionConverter.cs index ffe8fd7fd083a..1d485871823e2 100644 --- a/src/VisualStudio/Core/Impl/Options/Converters/NullableBoolOptionConverter.cs +++ b/src/VisualStudio/Core/Impl/Options/Converters/NullableBoolOptionConverter.cs @@ -7,25 +7,18 @@ using System.Windows; using System.Windows.Data; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options.Converters +namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options.Converters; + +internal sealed class NullableBoolOptionConverter(Func onNullValue) : IValueConverter { - internal class NullableBoolOptionConverter : IValueConverter - { - private readonly Func _onNullValue; - public NullableBoolOptionConverter(Func onNullValue) + public object Convert(object? value, Type targetType, object parameter, CultureInfo culture) + => value switch { - _onNullValue = onNullValue; - } - - public object Convert(object? value, Type targetType, object parameter, CultureInfo culture) - => value switch - { - null => _onNullValue(), - bool b => b, - _ => DependencyProperty.UnsetValue - }; + null => onNullValue(), + bool b => b, + _ => DependencyProperty.UnsetValue + }; - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - => value; - } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + => value; } diff --git a/src/VisualStudio/Core/Test.Next/Options/VisualStudioSettingsOptionPersisterTests.cs b/src/VisualStudio/Core/Test.Next/Options/VisualStudioSettingsOptionPersisterTests.cs index 59fa7df3cca64..7381a03351a4f 100644 --- a/src/VisualStudio/Core/Test.Next/Options/VisualStudioSettingsOptionPersisterTests.cs +++ b/src/VisualStudio/Core/Test.Next/Options/VisualStudioSettingsOptionPersisterTests.cs @@ -100,17 +100,6 @@ private static (object? value, object? storageValue) GetSomeOptionValue(Type opt optionType == typeof(ImmutableArray) ? (ImmutableArray.Create("a", "b"), new[] { "a", "b" }) : throw ExceptionUtilities.UnexpectedValue(optionType); - private static Type GetStorageType(Type optionType) - => optionType.IsEnum ? typeof(int) : - (Nullable.GetUnderlyingType(optionType)?.IsEnum == true) ? typeof(int?) : - optionType == typeof(NamingStylePreferences) ? typeof(string) : - typeof(ICodeStyleOption2).IsAssignableFrom(optionType) ? typeof(string) : - optionType == typeof(ImmutableArray) ? typeof(string[]) : - optionType == typeof(ImmutableArray) ? typeof(bool[]) : - optionType == typeof(ImmutableArray) ? typeof(int[]) : - optionType == typeof(ImmutableArray) ? typeof(long[]) : - optionType; - private static bool IsDefaultImmutableArray(object array) => (bool)array.GetType().GetMethod("get_IsDefault").Invoke(array, [])!; @@ -229,11 +218,10 @@ public void SettingsManagerReadOptionValue_Error( Type optionType) { var (optionValue, storageValue) = GetSomeOptionValue(optionType); - var storageType = GetStorageType(optionType); var mockManager = new MockSettingsManager() { - GetValueImpl = (_, type) => (type == storageType ? specializedTypeResult : GetValueResult.Success, storageValue) + GetValueImpl = (_, type) => (specializedTypeResult, storageValue) }; var result = VisualStudioSettingsOptionPersister.TryReadOptionValue(mockManager, "key", optionType, optionValue);