diff --git a/ReasonCodeExample.XPathTools.Tests/Properties/AssemblyInfo.cs b/ReasonCodeExample.XPathTools.Tests/Properties/AssemblyInfo.cs index 29a5d30..57141a3 100644 --- a/ReasonCodeExample.XPathTools.Tests/Properties/AssemblyInfo.cs +++ b/ReasonCodeExample.XPathTools.Tests/Properties/AssemblyInfo.cs @@ -5,4 +5,4 @@ [assembly: AssemblyCompany("Reason→Code→Example (http://reasoncodeexample.com)")] [assembly: AssemblyProduct("ReasonCodeExample.XPathTools.Tests")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("6.0.1.*")] +[assembly: AssemblyVersion("6.0.2.*")] diff --git a/ReasonCodeExample.XPathTools/Configuration/XPathToolsDialogPage.cs b/ReasonCodeExample.XPathTools/Configuration/XPathToolsDialogPage.cs index 95543f6..9af407b 100644 --- a/ReasonCodeExample.XPathTools/Configuration/XPathToolsDialogPage.cs +++ b/ReasonCodeExample.XPathTools/Configuration/XPathToolsDialogPage.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing.Design; @@ -21,11 +22,11 @@ public XPathToolsDialogPage() [Category("Statusbar")] [DisplayName("Statusbar XPath format")] [Description("Select the XPath format used in the statusbar.")] - public XPathFormat? StatusbarXPathFormatSetting + public XPathFormat StatusbarXPathFormatSetting { get; set; - } + } = XPathFormat.Generic; [Category("Generic XPath")] [DisplayName("Always displayed attributes")] @@ -88,6 +89,8 @@ public override void LoadSettingsFromStorage() PreferredAttributeCandidatesSetting.ListChanged += SavePreferredAttributeCandidatesSettingToStorage; } + #region Load and initialize settings + private void LoadCollectionsFromStorage() { var package = (Package)GetService(typeof(Package)); @@ -95,28 +98,30 @@ private void LoadCollectionsFromStorage() { return; } + using(var userRegistryRoot = package.UserRegistryRoot) { - LoadCollectionsFromRegistry(userRegistryRoot); + LoadSettingsFromRegistry(userRegistryRoot); } } - private void LoadCollectionsFromRegistry(RegistryKey userRegistryRoot) + private void LoadSettingsFromRegistry(RegistryKey userRegistryRoot) { var settingsRegistryPath = SettingsRegistryPath; var automationObject = AutomationObject; - var registryKey = userRegistryRoot.OpenSubKey(settingsRegistryPath, false); + var registryKey = userRegistryRoot.OpenSubKey(settingsRegistryPath, true); if(registryKey == null) { LoadDefaultSettings(); return; } + using(registryKey) { var propertyNames = registryKey.GetValueNames(); foreach(var propertyName in propertyNames) { - SetPropertyValue(automationObject, propertyName, registryKey); + LoadSettingValue(automationObject, propertyName, registryKey); } } } @@ -127,60 +132,109 @@ private void LoadDefaultSettings() { return; } + PreferredAttributeCandidates.Add(new XPathSetting {AttributeName = "id"}); PreferredAttributeCandidates.Add(new XPathSetting {AttributeName = "name"}); PreferredAttributeCandidates.Add(new XPathSetting {AttributeName = "type"}); } - private void SetPropertyValue(object automationObject, string propertyName, RegistryKey registryKey) + private void LoadSettingValue(object automationObject, string propertyName, RegistryKey registryKey) { var property = automationObject.GetType().GetProperty(propertyName); if(property == null) { return; } - if(property.GetCustomAttribute(typeof(TypeConverterAttribute)) == null) + + try { - return; + var storedValue = registryKey.GetValue(propertyName).ToString(); + var converter = GetTypeConverter(propertyName); + var convertedValue = converter.ConvertFrom(storedValue); + property.SetValue(automationObject, convertedValue); + } + catch(Exception e) + { + Console.Error.WriteLine(e); + // Delete the value if it can't be deserialized, + // to prevent old serialization formats from + // interfering with storing new values. + registryKey.DeleteValue(propertyName); + var defaultValue = GetDefaultValue(property); + property.SetValue(automationObject, defaultValue); + } + } + + private TypeConverter GetTypeConverter(string propertyName) + { + var property = GetType().GetProperty(propertyName); + if(property == null) + { + throw new InvalidOperationException($"Property '{propertyName}' not found on type '{GetType()}.'"); } - var storedValue = registryKey.GetValue(propertyName).ToString(); - var converter = new SerializableConverter>(); - property.SetValue(automationObject, converter.ConvertFrom(storedValue)); + + var typeConverterAttribute = property.GetCustomAttribute(); + if(typeConverterAttribute == null) + { + return TypeDescriptor.GetConverter(property.PropertyType); + } + + var converterType = Type.GetType(typeConverterAttribute.ConverterTypeName); + return (TypeConverter)Activator.CreateInstance(converterType); } + private object GetDefaultValue(PropertyInfo property) + { + return property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null; + } + + #endregion + + #region Save settings + private void SaveAlwaysDisplayedAttributesSettingToStorage(object sender, ListChangedEventArgs e) { - SaveCollectionToStorage(nameof(AlwaysDisplayedAttributesSetting), (BindingList)sender); + SaveSettingToStorage(nameof(AlwaysDisplayedAttributesSetting), sender); } private void SavePreferredAttributeCandidatesSettingToStorage(object sender, ListChangedEventArgs e) { - SaveCollectionToStorage(nameof(PreferredAttributeCandidatesSetting), (BindingList)sender); + SaveSettingToStorage(nameof(PreferredAttributeCandidatesSetting), sender); } - private void SaveCollectionToStorage(string propertyName, BindingList settings) + private void SaveSettingToStorage(string propertyName, object propertyValue) { var package = (Package)GetService(typeof(Package)); if(package == null) { return; } + using(var userRegistryRoot = package.UserRegistryRoot) { - SaveCollectionToRegistry(userRegistryRoot, propertyName, settings); + SaveSettingToRegistry(userRegistryRoot, propertyName, propertyValue); } } - private void SaveCollectionToRegistry(RegistryKey userRegistryRoot, string propertyName, BindingList settings) + private void SaveSettingToRegistry(RegistryKey userRegistryRoot, string propertyName, object propertyValue) { var settingsRegistryPath = SettingsRegistryPath; var registryKey = userRegistryRoot.OpenSubKey(settingsRegistryPath, true) ?? userRegistryRoot.CreateSubKey(settingsRegistryPath); using(registryKey) { - var converter = new SerializableConverter>(); - var convertedValue = converter.ConvertTo(settings, typeof(string)); - registryKey.SetValue(propertyName, convertedValue); + try + { + var converter = GetTypeConverter(propertyName); + var convertedValue = converter.ConvertTo(propertyValue, typeof(string)); + registryKey.SetValue(propertyName, convertedValue); + } + catch(Exception e) + { + Console.Error.WriteLine(e); + } } } + + #endregion } -} \ No newline at end of file +} diff --git a/ReasonCodeExample.XPathTools/Properties/AssemblyInfo.cs b/ReasonCodeExample.XPathTools/Properties/AssemblyInfo.cs index b61728a..12ca79a 100644 --- a/ReasonCodeExample.XPathTools/Properties/AssemblyInfo.cs +++ b/ReasonCodeExample.XPathTools/Properties/AssemblyInfo.cs @@ -7,7 +7,7 @@ [assembly: AssemblyCompany("Reason→Code→Example (http://reasoncodeexample.com)")] [assembly: AssemblyProduct("ReasonCodeExample.XPathTools")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("6.0.1.*")] +[assembly: AssemblyVersion("6.0.2.*")] [assembly: InternalsVisibleTo(InternalsVisibleTo.ReasonCodeExampleXPathToolsTests)] [assembly: InternalsVisibleTo(InternalsVisibleTo.DynamicProxyGenAssembly2)] [assembly: InternalsVisibleTo(InternalsVisibleTo.CastleCore)] diff --git a/ReasonCodeExample.XPathTools/VisualStudioIntegration/source.extension.vsixmanifest b/ReasonCodeExample.XPathTools/VisualStudioIntegration/source.extension.vsixmanifest index 39786ca..ad702f1 100644 --- a/ReasonCodeExample.XPathTools/VisualStudioIntegration/source.extension.vsixmanifest +++ b/ReasonCodeExample.XPathTools/VisualStudioIntegration/source.extension.vsixmanifest @@ -1,7 +1,7 @@ - + XPath Tools Run XPaths and XPath functions. Browse through results at the click of a button. Track and copy XPaths incl. XML namespaces in various formats, taking the hassle out of complex documents. diff --git a/ReasonCodeExample.XPathTools/release-notes.txt b/ReasonCodeExample.XPathTools/release-notes.txt index da3b19f..c0dad53 100644 --- a/ReasonCodeExample.XPathTools/release-notes.txt +++ b/ReasonCodeExample.XPathTools/release-notes.txt @@ -1,4 +1,5 @@ -v6.0.1: XPath Runner query results now change focus to their source document when clicked. See issue #38 for details (https://github.com/uli-weltersbach/XPathTools/issues/38). +v6.0.2: The XPath format used in the statusbar is now persisted correctly. See issue #42 for details (https://github.com/uli-weltersbach/XPathTools/issues/42). +v6.0.1: XPath Runner query results now change focus to their source document when clicked. See issue #38 for details (https://github.com/uli-weltersbach/XPathTools/issues/38). v6.0.0: Async package load implemented as required by Visual Studio 2019.1 and later. Support for Visual Studio 2013 and earlier has been dropped as a consequence. v5.3.0: Supported Visual Studio version range updated to include VS 2019 Preview. See issue #36 for details (https://github.com/uli-weltersbach/XPathTools/issues/36). v5.2.2: Blank text nodes are now stripped from the output of the "Copy XML structure" command. See issue #34 for details (https://github.com/uli-weltersbach/XPathTools/issues/34). diff --git a/appveyor.yml b/appveyor.yml index 1e1ae7d..59eb82b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '6.0.1-{build}' +version: '6.0.2-{build}' image: Visual Studio 2017 configuration: Release platform: Any CPU