Added app configuration

This commit is contained in:
dan_clark@outlook.com
2022-03-27 19:35:25 +01:00
parent 40d8caf1e0
commit 5ba1e5a63b
17 changed files with 358 additions and 87 deletions
@@ -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);
}
}
}
+1
View File
@@ -1,5 +1,6 @@
namespace TheXamlGuy.TaskbarGroup.Core namespace TheXamlGuy.TaskbarGroup.Core
{ {
public static class Class1 public static class Class1
{ {
public static void Test(Stream s) public static void Test(Stream s)
@@ -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());
}
}
}
@@ -0,0 +1,4 @@
namespace TheXamlGuy.TaskbarGroup.Core
{
public record CreateShortcut(string Path);
}
@@ -0,0 +1,18 @@
using WindowsShortcutFactory;
namespace TheXamlGuy.TaskbarGroup.Core
{
public class CreateShortcutHandler : IMessageHandler<CreateShortcut>
{
public void Handle(CreateShortcut message)
{
using var shortcut = new WindowsShortcut
{
Path = message.Path,
};
shortcut.Save(@"C:\temp\MyShortcut.lnk");
}
}
}
@@ -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;
}
}
}
@@ -17,8 +17,10 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.Reactive" Version="5.0.0" /> <PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="System.Runtime.WindowsRuntime" Version="5.0.0-preview.5.20278.1" /> <PackageReference Include="System.Runtime.WindowsRuntime" Version="5.0.0-preview.5.20278.1" />
<PackageReference Include="WindowsShortcutFactory" Version="1.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<COMReference Include="UIA"> <COMReference Include="UIA">
@@ -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<WritableJsonConfigurationSource> configureSource) => builder.Add(configureSource);
}
}
@@ -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<string, (JsonValueKind, string)> _data = new(StringComparer.OrdinalIgnoreCase);
private readonly Stack<string> _paths = new();
private JObject _tokenCache = new();
public IDictionary<string, string> 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<string, string> 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();
}
}
}
}
@@ -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);
}
}
}
@@ -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; }
}
}
@@ -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<UIElement>
{
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<FileDropped>();
}
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;
}
}
}
}
@@ -11,8 +11,7 @@ namespace TheXamlGuy.TaskbarGroup.Foundation
.AddSingleton<TemplateSelector>() .AddSingleton<TemplateSelector>()
.AddSingleton<IDataTemplateCollection>(new DataTemplateCollection(new Dictionary<Type, Type>())) .AddSingleton<IDataTemplateCollection>(new DataTemplateCollection(new Dictionary<Type, Type>()))
.AddSingleton<DataTemplateFactory>() .AddSingleton<DataTemplateFactory>()
.AddSingleton<IDispatcherTimerFactory, DispatcherTimerFactory>() .AddSingleton<IDispatcherTimerFactory, DispatcherTimerFactory>();
.AddTransient<FileDropTarget>();
} }
} }
} }
@@ -8,8 +8,6 @@
<Platforms>x64;x86</Platforms> <Platforms>x64;x86</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft-WindowsAPICodePack-Core" Version="1.1.4" />
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
</ItemGroup> </ItemGroup>
+5 -1
View File
@@ -2,6 +2,7 @@
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using System; using System;
using System.IO; using System.IO;
using System.Reflection;
using System.Windows; using System.Windows;
using TheXamlGuy.TaskbarGroup.Core; using TheXamlGuy.TaskbarGroup.Core;
using TheXamlGuy.TaskbarGroup.Flyout; using TheXamlGuy.TaskbarGroup.Flyout;
@@ -19,7 +20,10 @@ namespace TheXamlGuy.TaskbarGroup
host = new HostBuilder() host = new HostBuilder()
.UseContentRoot(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), .UseContentRoot(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"TheXamlGuy", "TaskbarGroup"), true) "TheXamlGuy", "TaskbarGroup"), true)
.ConfigureServices(ConfigureServices) .ConfigureAppConfiguration(configuration =>
{
configuration.AddWritableJsonFile("Settings.json", false, true, Assembly.GetExecutingAssembly().ExtractResource("Settings.json"));
}).ConfigureServices(ConfigureServices)
.Build(); .Build();
await host.StartAsync(); await host.StartAsync();
+3
View File
@@ -0,0 +1,3 @@
{
}
@@ -11,6 +11,9 @@
<LangVersion>10.0</LangVersion> <LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="Settings.json" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
@@ -22,6 +25,9 @@
<ProjectReference Include="..\TheXamlGuy.TaskbarGroup.Flyout\TheXamlGuy.TaskbarGroup.Flyout.csproj" /> <ProjectReference Include="..\TheXamlGuy.TaskbarGroup.Flyout\TheXamlGuy.TaskbarGroup.Flyout.csproj" />
<ProjectReference Include="..\TheXamlGuy.TaskbarGroup.Foundation\TheXamlGuy.TaskbarGroup.Foundation.csproj" /> <ProjectReference Include="..\TheXamlGuy.TaskbarGroup.Foundation\TheXamlGuy.TaskbarGroup.Foundation.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Settings.json" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Update="Microsoft.VCRTForwarders.140" Version="1.0.7" /> <PackageReference Update="Microsoft.VCRTForwarders.140" Version="1.0.7" />
</ItemGroup> </ItemGroup>