From 5ba1e5a63b14cf2ab332076d66aa689222df032f Mon Sep 17 00:00:00 2001 From: "dan_clark@outlook.com" Date: Sun, 27 Mar 2022 19:35:25 +0100 Subject: [PATCH] Added app configuration --- .../AssemblyExtensions.cs | 13 ++ TheXamlGuy.TaskbarGroup.Core/Class1.cs | 1 + .../ConfigurationBuilderExtensions.cs | 12 ++ .../CreateShortcut.cs | 4 + .../CreateShortcutHandler.cs | 18 +++ .../ShortcutConfiguration.cs | 13 ++ .../TheXamlGuy.TaskbarGroup.Core.csproj | 2 + .../WritableJsonConfigurationExtensions.cs | 73 +++++++++ .../WritableJsonConfigurationFile.cs | 138 ++++++++++++++++++ .../WritableJsonConfigurationProvider.cs | 37 +++++ .../WritableJsonConfigurationSource.cs | 32 ++++ .../FileDropTarget.cs | 82 ----------- .../IServiceCollectionExtensions.cs | 3 +- .../TheXamlGuy.TaskbarGroup.Foundation.csproj | 2 - TheXamlGuy.TaskbarGroup/App.xaml.cs | 6 +- TheXamlGuy.TaskbarGroup/Settings.json | 3 + .../TheXamlGuy.TaskbarGroup.csproj | 6 + 17 files changed, 358 insertions(+), 87 deletions(-) create mode 100644 TheXamlGuy.TaskbarGroup.Core/AssemblyExtensions.cs create mode 100644 TheXamlGuy.TaskbarGroup.Core/ConfigurationBuilderExtensions.cs create mode 100644 TheXamlGuy.TaskbarGroup.Core/CreateShortcut.cs create mode 100644 TheXamlGuy.TaskbarGroup.Core/CreateShortcutHandler.cs create mode 100644 TheXamlGuy.TaskbarGroup.Core/ShortcutConfiguration.cs create mode 100644 TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationExtensions.cs create mode 100644 TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationFile.cs create mode 100644 TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationProvider.cs create mode 100644 TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationSource.cs delete mode 100644 TheXamlGuy.TaskbarGroup.Foundation/FileDropTarget.cs create mode 100644 TheXamlGuy.TaskbarGroup/Settings.json diff --git a/TheXamlGuy.TaskbarGroup.Core/AssemblyExtensions.cs b/TheXamlGuy.TaskbarGroup.Core/AssemblyExtensions.cs new file mode 100644 index 0000000..282e842 --- /dev/null +++ b/TheXamlGuy.TaskbarGroup.Core/AssemblyExtensions.cs @@ -0,0 +1,13 @@ +using System.Reflection; + +namespace TheXamlGuy.TaskbarGroup.Core +{ + public static class AssemblyExtensions + { + public static Stream ExtractResource(this Assembly assembly, string filename) + { + var resourceName = $"{assembly.GetName().Name.Replace("-", "_")}.{filename}"; + return assembly.GetManifestResourceStream(resourceName); + } + } +} diff --git a/TheXamlGuy.TaskbarGroup.Core/Class1.cs b/TheXamlGuy.TaskbarGroup.Core/Class1.cs index 7feb395..530473c 100644 --- a/TheXamlGuy.TaskbarGroup.Core/Class1.cs +++ b/TheXamlGuy.TaskbarGroup.Core/Class1.cs @@ -1,5 +1,6 @@ namespace TheXamlGuy.TaskbarGroup.Core { + public static class Class1 { public static void Test(Stream s) diff --git a/TheXamlGuy.TaskbarGroup.Core/ConfigurationBuilderExtensions.cs b/TheXamlGuy.TaskbarGroup.Core/ConfigurationBuilderExtensions.cs new file mode 100644 index 0000000..ef31b8d --- /dev/null +++ b/TheXamlGuy.TaskbarGroup.Core/ConfigurationBuilderExtensions.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Configuration; + +namespace TheXamlGuy.TaskbarGroup.Core +{ + public static class ConfigurationBuilderExtensions + { + public static IConfigurationBuilder AddWritableConfiguration(this IConfigurationBuilder builder) + { + return builder.Add(new WritableJsonConfigurationSource()); + } + } +} diff --git a/TheXamlGuy.TaskbarGroup.Core/CreateShortcut.cs b/TheXamlGuy.TaskbarGroup.Core/CreateShortcut.cs new file mode 100644 index 0000000..bb3f651 --- /dev/null +++ b/TheXamlGuy.TaskbarGroup.Core/CreateShortcut.cs @@ -0,0 +1,4 @@ +namespace TheXamlGuy.TaskbarGroup.Core +{ + public record CreateShortcut(string Path); +} diff --git a/TheXamlGuy.TaskbarGroup.Core/CreateShortcutHandler.cs b/TheXamlGuy.TaskbarGroup.Core/CreateShortcutHandler.cs new file mode 100644 index 0000000..0025e4e --- /dev/null +++ b/TheXamlGuy.TaskbarGroup.Core/CreateShortcutHandler.cs @@ -0,0 +1,18 @@ +using WindowsShortcutFactory; + +namespace TheXamlGuy.TaskbarGroup.Core +{ + public class CreateShortcutHandler : IMessageHandler + { + public void Handle(CreateShortcut message) + { + using var shortcut = new WindowsShortcut + { + Path = message.Path, + }; + + shortcut.Save(@"C:\temp\MyShortcut.lnk"); + } + + } +} diff --git a/TheXamlGuy.TaskbarGroup.Core/ShortcutConfiguration.cs b/TheXamlGuy.TaskbarGroup.Core/ShortcutConfiguration.cs new file mode 100644 index 0000000..829e455 --- /dev/null +++ b/TheXamlGuy.TaskbarGroup.Core/ShortcutConfiguration.cs @@ -0,0 +1,13 @@ +namespace TheXamlGuy.TaskbarGroup.Core +{ + public class ShortcutConfiguration + { + private string? _shortcutDirectory; + + public string? ShortcutDirectory + { + get => !string.IsNullOrEmpty(_shortcutDirectory) ? Environment.ExpandEnvironmentVariables(_shortcutDirectory) : null; + set => _shortcutDirectory = value; + } + } +} diff --git a/TheXamlGuy.TaskbarGroup.Core/TheXamlGuy.TaskbarGroup.Core.csproj b/TheXamlGuy.TaskbarGroup.Core/TheXamlGuy.TaskbarGroup.Core.csproj index 6cfce7d..c4428f5 100644 --- a/TheXamlGuy.TaskbarGroup.Core/TheXamlGuy.TaskbarGroup.Core.csproj +++ b/TheXamlGuy.TaskbarGroup.Core/TheXamlGuy.TaskbarGroup.Core.csproj @@ -17,8 +17,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationExtensions.cs b/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationExtensions.cs new file mode 100644 index 0000000..7bffa80 --- /dev/null +++ b/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationExtensions.cs @@ -0,0 +1,73 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.FileProviders; + +namespace TheXamlGuy.TaskbarGroup.Core +{ + public static class WritableJsonConfigurationExtensions + { + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path) + { + return AddWritableJsonFile(builder, null, path, false, false, null); + } + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + Stream createFromStream) + { + return AddWritableJsonFile(builder, null, path, false, false, createFromStream); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + bool optional) + { + return AddWritableJsonFile(builder, null, path, optional, false, null); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + bool optional, + Stream createFromStream) + { + return AddWritableJsonFile(builder, null, path, optional, false, createFromStream); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + bool optional, + bool reloadOnChange) + { + return AddWritableJsonFile(builder, null, path, optional, reloadOnChange, null); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + string path, + bool optional, + bool reloadOnChange, + Stream createFromStream) + { + return AddWritableJsonFile(builder, null, path, optional, reloadOnChange, createFromStream); + } + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + IFileProvider? provider, + string path, + bool optional, + bool reloadOnChange, Stream? createFromStream) + { + return builder.AddWritableJsonFile(configuration => + { + configuration.FileProvider = provider; + configuration.Path = path; + configuration.Optional = optional; + configuration.ReloadOnChange = reloadOnChange; + configuration.CreateFromSteam = createFromStream; + configuration.ResolveFileProvider(); + }); + } + + + public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder, + Action configureSource) => builder.Add(configureSource); + } +} diff --git a/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationFile.cs b/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationFile.cs new file mode 100644 index 0000000..2024238 --- /dev/null +++ b/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationFile.cs @@ -0,0 +1,138 @@ +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Text.Json; + +namespace TheXamlGuy.TaskbarGroup.Core +{ + internal class WritableJsonConfigurationFile + { + private readonly Dictionary _data = new(StringComparer.OrdinalIgnoreCase); + private readonly Stack _paths = new(); + private JObject _tokenCache = new(); + + public IDictionary Parse(Stream input) + { + return ParseStream(input); + } + + private object ConvertValue(JsonValueKind kind, string value) + { + if (kind is JsonValueKind.True or JsonValueKind.False) + { + return bool.Parse(value); + } + + return value; + } + + public void Write(string key, string value, Stream output) + { + var tokenPath = $"$.{key.Replace(":", ".")}"; + + var token = _tokenCache.SelectToken(tokenPath); + if (token is not null) + { + var (kind, _) = _data[key]; + + var newValue = ConvertValue(kind, value); + _data[key] = new(kind, value); + + token.Replace(JToken.FromObject(newValue)); + + using StreamWriter streamWriter = new(output); + using JsonTextWriter writer = new(streamWriter) { Formatting = Formatting.Indented }; + _tokenCache.WriteTo(writer); + output.SetLength(output.Position); + } + } + + private void EnterContext(string context) + { + _paths.Push(_paths.Count > 0 ? _paths.Peek() + ConfigurationPath.KeyDelimiter + context : context); + } + + private void ExitContext() + { + _paths.Pop(); + } + + private IDictionary ParseStream(Stream input) + { + _data.Clear(); + + var jsonDocumentOptions = new JsonDocumentOptions + { + CommentHandling = JsonCommentHandling.Skip, + AllowTrailingCommas = true, + }; + + using (var reader = new StreamReader(input)) + { + var content = reader.ReadToEnd(); + _tokenCache = JObject.Parse(content); + + using var doc = JsonDocument.Parse(content, jsonDocumentOptions); + VisitElement(doc.RootElement); + } + + return _data.ToDictionary(k => k.Key, v => v.Value.Item2.ToString()); + } + + private void VisitElement(JsonElement element) + { + var isEmpty = true; + + foreach (JsonProperty property in element.EnumerateObject()) + { + isEmpty = false; + + EnterContext(property.Name); + VisitValue(property.Value); + ExitContext(); + } + + if (isEmpty && _paths.Count > 0) + { + _data[_paths.Peek()] = (JsonValueKind.Null, null); + } + } + + private void VisitValue(JsonElement value) + { + switch (value.ValueKind) + { + case JsonValueKind.Object: + VisitElement(value); + break; + + case JsonValueKind.Array: + int index = 0; + foreach (JsonElement arrayElement in value.EnumerateArray()) + { + EnterContext(index.ToString()); + VisitValue(arrayElement); + ExitContext(); + index++; + } + break; + + case JsonValueKind.Number: + case JsonValueKind.String: + case JsonValueKind.True: + case JsonValueKind.False: + case JsonValueKind.Null: + string key = _paths.Peek(); + if (_data.ContainsKey(key)) + { + throw new FormatException(); + } + _data[key] = new(value.ValueKind, value.ToString()); + break; + + default: + throw new FormatException(); + } + } + } +} diff --git a/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationProvider.cs b/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationProvider.cs new file mode 100644 index 0000000..fe2a5c2 --- /dev/null +++ b/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationProvider.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.Configuration.Json; +using Microsoft.Extensions.FileProviders; + +namespace TheXamlGuy.TaskbarGroup.Core +{ + public class WritableJsonConfigurationProvider : JsonConfigurationProvider + { + private readonly WritableJsonConfigurationFile writableJsonConfigurationFile; + + public WritableJsonConfigurationProvider(JsonConfigurationSource source) : base(source) + { + writableJsonConfigurationFile = new WritableJsonConfigurationFile(); + } + + public override void Load(Stream stream) + { + Data = writableJsonConfigurationFile.Parse(stream); + } + + public override void Set(string key, string value) + { + var file = Source.FileProvider?.GetFileInfo(Source.Path ?? string.Empty); + static Stream OpenRead(IFileInfo fileInfo) + { + if (fileInfo.PhysicalPath is not null) + { + return new FileStream(fileInfo.PhysicalPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); + } + + return fileInfo.CreateReadStream(); + } + + using Stream stream = OpenRead(file); + writableJsonConfigurationFile.Write(key, value, stream); + } + } +} diff --git a/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationSource.cs b/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationSource.cs new file mode 100644 index 0000000..b413c17 --- /dev/null +++ b/TheXamlGuy.TaskbarGroup.Core/WritableJsonConfigurationSource.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Json; +using Microsoft.Extensions.FileProviders; + +namespace TheXamlGuy.TaskbarGroup.Core +{ + public class WritableJsonConfigurationSource : JsonConfigurationSource + { + public override IConfigurationProvider Build(IConfigurationBuilder builder) + { + EnsureDefaultsWithSteam(builder); + return new WritableJsonConfigurationProvider(this); + } + + private void EnsureDefaultsWithSteam(IConfigurationBuilder builder) + { + EnsureDefaults(builder); + + if (FileProvider is PhysicalFileProvider physicalFileProvider) + { + var outputFile = System.IO.Path.Combine(physicalFileProvider.Root, Path); + if (!File.Exists(outputFile) && CreateFromSteam is not null) + { + using var fileStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write); + CreateFromSteam.CopyTo(fileStream); + } + } + } + + public Stream? CreateFromSteam { get; set; } + } +} diff --git a/TheXamlGuy.TaskbarGroup.Foundation/FileDropTarget.cs b/TheXamlGuy.TaskbarGroup.Foundation/FileDropTarget.cs deleted file mode 100644 index 3e05a4b..0000000 --- a/TheXamlGuy.TaskbarGroup.Foundation/FileDropTarget.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Diagnostics; -using System.Windows; -using System.Windows.Shell; -using TheXamlGuy.TaskbarGroup.Core; -using Microsoft.WindowsAPICodePack.Shell; - -namespace TheXamlGuy.TaskbarGroup.Foundation -{ - public class FileDropTarget : IDropTarget - { - private UIElement? target; - private readonly IMessenger messenger; - - public FileDropTarget(IMessenger messenger) - { - this.messenger = messenger; - } - - public void Register(UIElement target) - { - if (this.target is not null) - { - target.DragOver -= OnDragOver; - target.DragEnter -= OnDragEnter; - target.Drop -= OnDrop; - } - - this.target = target; - - target.DragOver += OnDragOver; - target.DragEnter += OnDragEnter; - target.Drop += OnDrop; - } - - private void OnDrop(object sender, DragEventArgs args) - { - String[] fileName = (String[])args.Data.GetFormats(); - - var ddd = ShellObjectCollection.FromDataObject((System.Runtime.InteropServices.ComTypes.IDataObject)args.Data); - - //args.Handled = true; - //var fileName = GetFileName(args.Data); - //messenger.Publish(); - - } - - private string GetFileName(IDataObject data) - { - var filenames = (string[])data.GetData(DataFormats.FileDrop); - return filenames[0]; - } - - private bool IsFileDrop(IDataObject data) - { - return data.GetDataPresent(DataFormats.FileDrop); - } - - private void OnDragEnter(object sender, DragEventArgs args) - { - if (IsFileDrop(args.Data)) - { - args.Effects = DragDropEffects.Link; - } - else - { - args.Effects = DragDropEffects.None; - } - } - - private void OnDragOver(object sender, DragEventArgs args) - { - if (IsFileDrop(args.Data)) - { - args.Effects = DragDropEffects.Link; - } - else - { - args.Effects = DragDropEffects.None; - } - } - } -} diff --git a/TheXamlGuy.TaskbarGroup.Foundation/IServiceCollectionExtensions.cs b/TheXamlGuy.TaskbarGroup.Foundation/IServiceCollectionExtensions.cs index 76b01c2..a0c4460 100644 --- a/TheXamlGuy.TaskbarGroup.Foundation/IServiceCollectionExtensions.cs +++ b/TheXamlGuy.TaskbarGroup.Foundation/IServiceCollectionExtensions.cs @@ -11,8 +11,7 @@ namespace TheXamlGuy.TaskbarGroup.Foundation .AddSingleton() .AddSingleton(new DataTemplateCollection(new Dictionary())) .AddSingleton() - .AddSingleton() - .AddTransient(); + .AddSingleton(); } } } diff --git a/TheXamlGuy.TaskbarGroup.Foundation/TheXamlGuy.TaskbarGroup.Foundation.csproj b/TheXamlGuy.TaskbarGroup.Foundation/TheXamlGuy.TaskbarGroup.Foundation.csproj index b8b501a..0ded7c9 100644 --- a/TheXamlGuy.TaskbarGroup.Foundation/TheXamlGuy.TaskbarGroup.Foundation.csproj +++ b/TheXamlGuy.TaskbarGroup.Foundation/TheXamlGuy.TaskbarGroup.Foundation.csproj @@ -8,8 +8,6 @@ x64;x86 - - diff --git a/TheXamlGuy.TaskbarGroup/App.xaml.cs b/TheXamlGuy.TaskbarGroup/App.xaml.cs index 779f845..8cd9ffc 100644 --- a/TheXamlGuy.TaskbarGroup/App.xaml.cs +++ b/TheXamlGuy.TaskbarGroup/App.xaml.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Hosting; using System; using System.IO; +using System.Reflection; using System.Windows; using TheXamlGuy.TaskbarGroup.Core; using TheXamlGuy.TaskbarGroup.Flyout; @@ -19,7 +20,10 @@ namespace TheXamlGuy.TaskbarGroup host = new HostBuilder() .UseContentRoot(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "TheXamlGuy", "TaskbarGroup"), true) - .ConfigureServices(ConfigureServices) + .ConfigureAppConfiguration(configuration => + { + configuration.AddWritableJsonFile("Settings.json", false, true, Assembly.GetExecutingAssembly().ExtractResource("Settings.json")); + }).ConfigureServices(ConfigureServices) .Build(); await host.StartAsync(); diff --git a/TheXamlGuy.TaskbarGroup/Settings.json b/TheXamlGuy.TaskbarGroup/Settings.json new file mode 100644 index 0000000..0e0dcd2 --- /dev/null +++ b/TheXamlGuy.TaskbarGroup/Settings.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/TheXamlGuy.TaskbarGroup/TheXamlGuy.TaskbarGroup.csproj b/TheXamlGuy.TaskbarGroup/TheXamlGuy.TaskbarGroup.csproj index 0f51b4d..493df93 100644 --- a/TheXamlGuy.TaskbarGroup/TheXamlGuy.TaskbarGroup.csproj +++ b/TheXamlGuy.TaskbarGroup/TheXamlGuy.TaskbarGroup.csproj @@ -11,6 +11,9 @@ 10.0 enable + + + @@ -22,6 +25,9 @@ + + +