diff --git a/Directory.Build.targets b/Directory.Build.targets index 7a9fdf0..12edcbc 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -8,11 +8,16 @@ + + + + - - - - + + + + + @@ -23,12 +28,12 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/PriceChecker.Core.Tests/PriceChecker.Core.Tests.csproj b/PriceChecker.Core.Tests/PriceChecker.Core.Tests.csproj index 75df198..ffe3f36 100644 --- a/PriceChecker.Core.Tests/PriceChecker.Core.Tests.csproj +++ b/PriceChecker.Core.Tests/PriceChecker.Core.Tests.csproj @@ -8,7 +8,10 @@ - + + + + diff --git a/PriceChecker.Core.Tests/Repositories/AgentRepositoryTests.cs b/PriceChecker.Core.Tests/Repositories/AgentRepositoryTests.cs index 2ce99b1..4d9e387 100644 --- a/PriceChecker.Core.Tests/Repositories/AgentRepositoryTests.cs +++ b/PriceChecker.Core.Tests/Repositories/AgentRepositoryTests.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Linq; using AutoFixture; +using Genius.Atom.Infrastructure.Events; using Genius.PriceChecker.Core.Messages; using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Repositories; using Genius.PriceChecker.Core.Services; -using Genius.PriceChecker.Infrastructure.Events; using Microsoft.Extensions.Logging; using Moq; using Xunit; @@ -19,9 +19,7 @@ public class AgentRepositoryTests private readonly Fixture _fixture = new(); private readonly Mock _eventBusMock = new(); private readonly Mock _persisterMock = new(); - private readonly Mock _agentRepoMock = new(); - private readonly List _products = new(); private readonly List _agents = new(); public AgentRepositoryTests() diff --git a/PriceChecker.Core.Tests/Repositories/ProductRepositoryTests.cs b/PriceChecker.Core.Tests/Repositories/ProductRepositoryTests.cs index 756b561..072d004 100644 --- a/PriceChecker.Core.Tests/Repositories/ProductRepositoryTests.cs +++ b/PriceChecker.Core.Tests/Repositories/ProductRepositoryTests.cs @@ -6,7 +6,7 @@ using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Repositories; using Genius.PriceChecker.Core.Services; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; using Microsoft.Extensions.Logging; using Moq; using Xunit; diff --git a/PriceChecker.Core.Tests/Repositories/SettingsRepositoryTests.cs b/PriceChecker.Core.Tests/Repositories/SettingsRepositoryTests.cs index be767a7..94004fc 100644 --- a/PriceChecker.Core.Tests/Repositories/SettingsRepositoryTests.cs +++ b/PriceChecker.Core.Tests/Repositories/SettingsRepositoryTests.cs @@ -4,7 +4,7 @@ using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Repositories; using Genius.PriceChecker.Core.Services; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; using Microsoft.Extensions.Logging; using Moq; using Xunit; diff --git a/PriceChecker.Core/Messages/AgentDeletedEvent.cs b/PriceChecker.Core/Messages/AgentDeletedEvent.cs index 6854a78..00351a1 100644 --- a/PriceChecker.Core/Messages/AgentDeletedEvent.cs +++ b/PriceChecker.Core/Messages/AgentDeletedEvent.cs @@ -1,5 +1,5 @@ using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.Core.Messages { diff --git a/PriceChecker.Core/Messages/AgentsUpdatedEvent.cs b/PriceChecker.Core/Messages/AgentsUpdatedEvent.cs index b317b04..32d2371 100644 --- a/PriceChecker.Core/Messages/AgentsUpdatedEvent.cs +++ b/PriceChecker.Core/Messages/AgentsUpdatedEvent.cs @@ -1,4 +1,4 @@ -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.Core.Messages { diff --git a/PriceChecker.Core/Messages/ProductAddedEvent.cs b/PriceChecker.Core/Messages/ProductAddedEvent.cs index 7f620ba..73ba299 100644 --- a/PriceChecker.Core/Messages/ProductAddedEvent.cs +++ b/PriceChecker.Core/Messages/ProductAddedEvent.cs @@ -1,5 +1,5 @@ using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.Core.Messages { diff --git a/PriceChecker.Core/Messages/ProductAutoScanStartedEvent.cs b/PriceChecker.Core/Messages/ProductAutoScanStartedEvent.cs index b1f03c9..dbdf4df 100644 --- a/PriceChecker.Core/Messages/ProductAutoScanStartedEvent.cs +++ b/PriceChecker.Core/Messages/ProductAutoScanStartedEvent.cs @@ -1,4 +1,4 @@ -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.Core.Messages { diff --git a/PriceChecker.Core/Messages/ProductScanFailedEvent.cs b/PriceChecker.Core/Messages/ProductScanFailedEvent.cs index 717dd6f..82b084c 100644 --- a/PriceChecker.Core/Messages/ProductScanFailedEvent.cs +++ b/PriceChecker.Core/Messages/ProductScanFailedEvent.cs @@ -1,5 +1,5 @@ using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.Core.Messages { diff --git a/PriceChecker.Core/Messages/ProductScanStartedEvent.cs b/PriceChecker.Core/Messages/ProductScanStartedEvent.cs index 43c6dff..114bbfd 100644 --- a/PriceChecker.Core/Messages/ProductScanStartedEvent.cs +++ b/PriceChecker.Core/Messages/ProductScanStartedEvent.cs @@ -1,5 +1,5 @@ using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.Core.Messages { diff --git a/PriceChecker.Core/Messages/ProductScannedEvent.cs b/PriceChecker.Core/Messages/ProductScannedEvent.cs index a664b7a..bd020c8 100644 --- a/PriceChecker.Core/Messages/ProductScannedEvent.cs +++ b/PriceChecker.Core/Messages/ProductScannedEvent.cs @@ -1,5 +1,5 @@ using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.Core.Messages { diff --git a/PriceChecker.Core/Messages/ProductUpdatedEvent.cs b/PriceChecker.Core/Messages/ProductUpdatedEvent.cs index 801b9d5..a4d92d9 100644 --- a/PriceChecker.Core/Messages/ProductUpdatedEvent.cs +++ b/PriceChecker.Core/Messages/ProductUpdatedEvent.cs @@ -1,5 +1,5 @@ using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.Core.Messages { diff --git a/PriceChecker.Core/Messages/SettingsUpdatedEvent.cs b/PriceChecker.Core/Messages/SettingsUpdatedEvent.cs index f86e340..4f04150 100644 --- a/PriceChecker.Core/Messages/SettingsUpdatedEvent.cs +++ b/PriceChecker.Core/Messages/SettingsUpdatedEvent.cs @@ -1,5 +1,5 @@ using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.Core.Messages { diff --git a/PriceChecker.Core/PriceChecker.Core.csproj b/PriceChecker.Core/PriceChecker.Core.csproj index e657d96..a6d6923 100644 --- a/PriceChecker.Core/PriceChecker.Core.csproj +++ b/PriceChecker.Core/PriceChecker.Core.csproj @@ -15,12 +15,9 @@ - - - - + diff --git a/PriceChecker.Core/Repositories/AgentRepository.cs b/PriceChecker.Core/Repositories/AgentRepository.cs index 3ab128a..636f0da 100644 --- a/PriceChecker.Core/Repositories/AgentRepository.cs +++ b/PriceChecker.Core/Repositories/AgentRepository.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using Genius.Atom.Infrastructure.Events; using Genius.PriceChecker.Core.Messages; using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Services; -using Genius.PriceChecker.Infrastructure.Events; using Microsoft.Extensions.Logging; namespace Genius.PriceChecker.Core.Repositories diff --git a/PriceChecker.Core/Repositories/ProductRepository.cs b/PriceChecker.Core/Repositories/ProductRepository.cs index d138e78..fccb17f 100644 --- a/PriceChecker.Core/Repositories/ProductRepository.cs +++ b/PriceChecker.Core/Repositories/ProductRepository.cs @@ -4,7 +4,7 @@ using Genius.PriceChecker.Core.Messages; using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Services; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; using Microsoft.Extensions.Logging; namespace Genius.PriceChecker.Core.Repositories diff --git a/PriceChecker.Core/Repositories/SettingsRepository.cs b/PriceChecker.Core/Repositories/SettingsRepository.cs index c5c5ce1..aae55f9 100644 --- a/PriceChecker.Core/Repositories/SettingsRepository.cs +++ b/PriceChecker.Core/Repositories/SettingsRepository.cs @@ -2,7 +2,7 @@ using Genius.PriceChecker.Core.Messages; using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Services; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; using Microsoft.Extensions.Logging; namespace Genius.PriceChecker.Core.Repositories diff --git a/PriceChecker.Core/Services/ProductPriceManager.cs b/PriceChecker.Core/Services/ProductPriceManager.cs index 1dc17fb..32d282f 100644 --- a/PriceChecker.Core/Services/ProductPriceManager.cs +++ b/PriceChecker.Core/Services/ProductPriceManager.cs @@ -7,7 +7,7 @@ using Genius.PriceChecker.Core.Messages; using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Repositories; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; using Microsoft.Extensions.Logging; namespace Genius.PriceChecker.Core.Services diff --git a/PriceChecker.Core/Services/TrickyHttpClient.cs b/PriceChecker.Core/Services/TrickyHttpClient.cs index 2694ea8..f7af825 100644 --- a/PriceChecker.Core/Services/TrickyHttpClient.cs +++ b/PriceChecker.Core/Services/TrickyHttpClient.cs @@ -82,14 +82,14 @@ private async Task DownloadInternal(string url, CancellationToken cancel return null; } - private string CreateRandomUserAgent() + private static string CreateRandomUserAgent() { var platform = new [] { "Machintosh", "Windows", "X11" }.TakeRandom(); var os = (platform switch { "Machintosh" => new [] { "68K", "PPC" }, "Windows" => new [] { "Win3.11", "WinNT3.51", "WinNT4.0", "Windows NT 5.0", "Windows NT 5.1", "Windows NT 5.2", "Windows NT 6.0", "Windows NT 6.1", "Windows NT 6.2", "Win95", "Win98", "Win 9x 4.90", "WindowsCE" }, "X11" => new [] { "Linux i686", "Linux x86_64" }, - _ => new string[] {} + _ => Array.Empty() }).TakeRandom(); var browser = new [] { "Chrome", "Firefox", "IE" }.TakeRandom(); diff --git a/PriceChecker.Infrastructure/CompositeDisposable.cs b/PriceChecker.Infrastructure/CompositeDisposable.cs deleted file mode 100644 index 42e7580..0000000 --- a/PriceChecker.Infrastructure/CompositeDisposable.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Genius.PriceChecker.Infrastructure -{ - public class CompositeDisposable : IDisposable - { - private readonly IDisposable[] _disposables; - - public CompositeDisposable(params IDisposable[] disposables) - { - _disposables = disposables; - } - - public void Dispose() - { - foreach (var disposable in _disposables) - { - disposable.Dispose(); - } - } - } -} diff --git a/PriceChecker.Infrastructure/DisposableAction.cs b/PriceChecker.Infrastructure/DisposableAction.cs deleted file mode 100644 index d486703..0000000 --- a/PriceChecker.Infrastructure/DisposableAction.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Genius.PriceChecker.Infrastructure -{ - public class DisposableAction : IDisposable - { - private readonly Action _disposeAction; - - public DisposableAction(Action disposeAction) - { - _disposeAction = disposeAction; - } - - public void Dispose() - { - _disposeAction(); - } - } -} diff --git a/PriceChecker.Infrastructure/Events/EventBus.cs b/PriceChecker.Infrastructure/Events/EventBus.cs deleted file mode 100644 index ff3b5aa..0000000 --- a/PriceChecker.Infrastructure/Events/EventBus.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Reactive; -using System.Reactive.Linq; - -namespace Genius.PriceChecker.Infrastructure.Events -{ - public interface IEventBus - { - void Publish(IEventMessage @event); - IObservable WhenFired() - where T : IEventMessage; - IObservable WhenFired() - where T1 : IEventMessage - where T2 : IEventMessage; - IObservable WhenFired() - where T1 : IEventMessage - where T2 : IEventMessage - where T3 : IEventMessage; - } - - internal sealed class EventBus : IEventBus - { - private event EventHandler _eventAdded; - private readonly IObservable _mainObservable; - - public EventBus() - { - _mainObservable = Observable.FromEventPattern( - x => this._eventAdded += x, - x => this._eventAdded -= x) - .Select(x => x.EventArgs); - } - - public void Publish(IEventMessage message) - { - _eventAdded?.Invoke(this, new EventPublishedArgs(message)); - } - - public IObservable WhenFired() - where T : IEventMessage - { - return _mainObservable - .Where(x => x.Event is T) - .Select(x => (T)x.Event); - } - - public IObservable WhenFired() - where T1 : IEventMessage - where T2 : IEventMessage - { - return WhenFired().Select(x => Unit.Default) - .Merge(WhenFired().Select(x => Unit.Default)); - } - - public IObservable WhenFired() - where T1 : IEventMessage - where T2 : IEventMessage - where T3 : IEventMessage - { - return WhenFired() - .Merge(WhenFired().Select(x => Unit.Default)); - } - } -} diff --git a/PriceChecker.Infrastructure/Events/EventPublishedArgs.cs b/PriceChecker.Infrastructure/Events/EventPublishedArgs.cs deleted file mode 100644 index 3bdf8b1..0000000 --- a/PriceChecker.Infrastructure/Events/EventPublishedArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Genius.PriceChecker.Infrastructure.Events -{ - internal sealed class EventPublishedArgs : EventArgs - { - public EventPublishedArgs(IEventMessage @event) - { - Event = @event; - } - - public IEventMessage Event { get; } - } -} \ No newline at end of file diff --git a/PriceChecker.Infrastructure/Events/IEventMessage.cs b/PriceChecker.Infrastructure/Events/IEventMessage.cs deleted file mode 100644 index ce788a5..0000000 --- a/PriceChecker.Infrastructure/Events/IEventMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Genius.PriceChecker.Infrastructure.Events -{ - public interface IEventMessage - { - } -} diff --git a/PriceChecker.Infrastructure/ExpressionHelpers.cs b/PriceChecker.Infrastructure/ExpressionHelpers.cs deleted file mode 100644 index b51991d..0000000 --- a/PriceChecker.Infrastructure/ExpressionHelpers.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Linq.Expressions; - -namespace Genius.PriceChecker.Infrastructure -{ - public static class ExpressionHelpers - { - public static string GetPropertyName(Expression> propertyLambda) - { - MemberExpression body = propertyLambda.Body as MemberExpression; - - if (body == null) { - UnaryExpression ubody = (UnaryExpression)propertyLambda.Body; - body = ubody.Operand as MemberExpression; - } - - return body.Member.Name; - } - } -} diff --git a/PriceChecker.Infrastructure/Logging/EventBasedLogger.cs b/PriceChecker.Infrastructure/Logging/EventBasedLogger.cs deleted file mode 100644 index 7970ad4..0000000 --- a/PriceChecker.Infrastructure/Logging/EventBasedLogger.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Linq; -using Genius.PriceChecker.Infrastructure.Events; -using Microsoft.Extensions.Logging; - -namespace Genius.PriceChecker.Infrastructure.Logging -{ - internal sealed class EventBasedLogger : ILogger - { - private readonly IEventBus _eventBus; - private readonly string _name; - private readonly string _shortName; - - public EventBasedLogger(string name, IEventBus eventBus) - { - _name = name; - _shortName = CreateShortNameFrom(name); - _eventBus = eventBus; - } - - public IDisposable BeginScope(TState state) => default; - - public bool IsEnabled(LogLevel logLevel) - // Ignore all log events below Information - => logLevel >= LogLevel.Information; - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - if (!IsEnabled(logLevel)) - { - return; - } - - var message = formatter(state, exception); - - _eventBus.Publish(new LogEvent(logLevel, _shortName, message)); - } - - private string CreateShortNameFrom(string name) - { - if (name.StartsWith("Genius.")) - { - return string.Join('.', name.Split('.').Skip(3)); - } - - return name; - } - } -} diff --git a/PriceChecker.Infrastructure/Logging/EventBasedLoggerProvider.cs b/PriceChecker.Infrastructure/Logging/EventBasedLoggerProvider.cs deleted file mode 100644 index cede43f..0000000 --- a/PriceChecker.Infrastructure/Logging/EventBasedLoggerProvider.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Concurrent; -using Genius.PriceChecker.Infrastructure.Events; -using Microsoft.Extensions.Logging; - -namespace Genius.PriceChecker.Infrastructure.Logging -{ - public sealed class EventBasedLoggerProvider : ILoggerProvider - { - private readonly IEventBus _eventBus; - - private readonly ConcurrentDictionary _loggers = - new ConcurrentDictionary(); - - public EventBasedLoggerProvider(IEventBus eventBus) - { - _eventBus = eventBus; - } - - public ILogger CreateLogger(string categoryName) - { - return _loggers.GetOrAdd(categoryName, name => new EventBasedLogger(name, _eventBus)); - } - - public void Dispose() - { - _loggers.Clear(); - } - } -} diff --git a/PriceChecker.Infrastructure/Logging/LogEvent.cs b/PriceChecker.Infrastructure/Logging/LogEvent.cs deleted file mode 100644 index 1b46c5b..0000000 --- a/PriceChecker.Infrastructure/Logging/LogEvent.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Genius.PriceChecker.Infrastructure.Events; -using Microsoft.Extensions.Logging; - -namespace Genius.PriceChecker.Infrastructure.Logging -{ - public sealed class LogEvent : IEventMessage - { - public LogEvent(LogLevel severity, string logger, string message) - { - Severity = severity; - Logger = logger; - Message = message; - } - - public LogLevel Severity { get; } - public string Logger { get; } - public string Message { get; } - } -} diff --git a/PriceChecker.Infrastructure/Module.cs b/PriceChecker.Infrastructure/Module.cs deleted file mode 100644 index 980bfbe..0000000 --- a/PriceChecker.Infrastructure/Module.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Genius.PriceChecker.Infrastructure.Events; -using Microsoft.Extensions.DependencyInjection; - -namespace Genius.PriceChecker.Infrastructure -{ - [ExcludeFromCodeCoverage] - public static class Module - { - public static void Configure(IServiceCollection services) - { - services.AddSingleton(); - } - } -} diff --git a/PriceChecker.Infrastructure/PriceChecker.Infrastructure.csproj b/PriceChecker.Infrastructure/PriceChecker.Infrastructure.csproj deleted file mode 100644 index ab99415..0000000 --- a/PriceChecker.Infrastructure/PriceChecker.Infrastructure.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net5.0 - Genius.PriceChecker.Infrastructure - - - - - - - - - diff --git a/PriceChecker.UI.Forms.Tests/PriceChecker.UI.Forms.Tests.csproj b/PriceChecker.UI.Forms.Tests/PriceChecker.UI.Forms.Tests.csproj deleted file mode 100644 index 36f317f..0000000 --- a/PriceChecker.UI.Forms.Tests/PriceChecker.UI.Forms.Tests.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - net5.0-windows - Genius.PriceChecker.UI.Forms.Tests - false - - - - - - - diff --git a/PriceChecker.UI.Forms/ActionCommand.cs b/PriceChecker.UI.Forms/ActionCommand.cs deleted file mode 100644 index 6de3449..0000000 --- a/PriceChecker.UI.Forms/ActionCommand.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Reactive; -using System.Reactive.Subjects; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Input; - -namespace Genius.PriceChecker.UI.Forms -{ - public interface IActionCommand : ICommand - { - IObservable Executed { get; } - } - - public class ActionCommand : IActionCommand - { - private Func _asyncAction; - private Predicate _canExecute; - private Subject _executed = new(); - - public event EventHandler CanExecuteChanged - { - add { CommandManager.RequerySuggested += value; } - remove { CommandManager.RequerySuggested -= value; } - } - - public IObservable Executed => _executed; - - public ActionCommand() - : this (_ => { }, null) - { - } - - public ActionCommand(Func asyncAction) - : this (asyncAction, null) - { - } - - public ActionCommand(Action action) - : this (action, null) - { - } - - public ActionCommand(Action action, Predicate canExecute) - : this ((o) => { action(o); return Task.CompletedTask; }, canExecute) - { - } - - public ActionCommand(Func asyncAction, Predicate canExecute) - { - if (asyncAction == null) - { - throw new ArgumentNullException(nameof(asyncAction)); - } - - _asyncAction = asyncAction; - _canExecute = canExecute; - } - - public bool CanExecute(object parameter) - { - if (_canExecute != null) - { - return _canExecute.Invoke(parameter); - } - - return true; - } - - public async void Execute(object parameter) - { - try - { - await _asyncAction.Invoke(parameter); - _executed.OnNext(Unit.Default); - } - catch (Exception ex) - { - MessageBox.Show(ex.Message, "Action failed", MessageBoxButton.OK, MessageBoxImage.Warning); - } - } - } -} diff --git a/PriceChecker.UI.Forms/Attributes/DisplayIndexAttribute.cs b/PriceChecker.UI.Forms/Attributes/DisplayIndexAttribute.cs deleted file mode 100644 index ad74c53..0000000 --- a/PriceChecker.UI.Forms/Attributes/DisplayIndexAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] - public sealed class DisplayIndexAttribute : Attribute - { - public DisplayIndexAttribute(int index) - { - Index = index; - } - - public int Index { get; } - } -} diff --git a/PriceChecker.UI.Forms/Attributes/FilterByAttribute.cs b/PriceChecker.UI.Forms/Attributes/FilterByAttribute.cs deleted file mode 100644 index 06a386c..0000000 --- a/PriceChecker.UI.Forms/Attributes/FilterByAttribute.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] - public sealed class FilterByAttribute : Attribute - { } -} diff --git a/PriceChecker.UI.Forms/Attributes/FilterContextAttribute.cs b/PriceChecker.UI.Forms/Attributes/FilterContextAttribute.cs deleted file mode 100644 index 744234e..0000000 --- a/PriceChecker.UI.Forms/Attributes/FilterContextAttribute.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] - public sealed class FilterContextAttribute : Attribute - { } -} diff --git a/PriceChecker.UI.Forms/Attributes/GroupByAttribute.cs b/PriceChecker.UI.Forms/Attributes/GroupByAttribute.cs deleted file mode 100644 index 519cda4..0000000 --- a/PriceChecker.UI.Forms/Attributes/GroupByAttribute.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] - public sealed class GroupByAttribute : Attribute - { } -} diff --git a/PriceChecker.UI.Forms/Attributes/IconAttribute.cs b/PriceChecker.UI.Forms/Attributes/IconAttribute.cs deleted file mode 100644 index e77f2fe..0000000 --- a/PriceChecker.UI.Forms/Attributes/IconAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] - public sealed class IconAttribute : Attribute - { - public IconAttribute(string name) - { - Name = name; - } - - public string Name { get; } - } -} diff --git a/PriceChecker.UI.Forms/Attributes/IconSourceAttribute.cs b/PriceChecker.UI.Forms/Attributes/IconSourceAttribute.cs deleted file mode 100644 index 86e517c..0000000 --- a/PriceChecker.UI.Forms/Attributes/IconSourceAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - public sealed class IconSourceAttribute : Attribute - { - public IconSourceAttribute(string iconPropertyPath) - { - IconPropertyPath = iconPropertyPath; - } - - public IconSourceAttribute(string iconPropertyPath, double fixedSize) - : this (iconPropertyPath) - { - FixedSize = fixedSize; - } - - public IconSourceAttribute(string iconPropertyPath, double fixedSize, bool hideText) - : this (iconPropertyPath, fixedSize) - { - HideText = hideText; - } - - public string IconPropertyPath { get; } - public double? FixedSize { get; } - public bool HideText { get; } = false; - } -} diff --git a/PriceChecker.UI.Forms/Attributes/SelectFromListAttribute.cs b/PriceChecker.UI.Forms/Attributes/SelectFromListAttribute.cs deleted file mode 100644 index fd4b1e5..0000000 --- a/PriceChecker.UI.Forms/Attributes/SelectFromListAttribute.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] - public sealed class SelectFromListAttribute : Attribute - { - public SelectFromListAttribute(string collectionPropertyName, bool fromOwnerContext = false) - { - CollectionPropertyName = collectionPropertyName; - FromOwnerContext = fromOwnerContext; - } - - public string CollectionPropertyName { get; } - public bool FromOwnerContext { get; } - } -} diff --git a/PriceChecker.UI.Forms/Attributes/ShowOnlyBrowsableAttribute.cs b/PriceChecker.UI.Forms/Attributes/ShowOnlyBrowsableAttribute.cs deleted file mode 100644 index 52510c3..0000000 --- a/PriceChecker.UI.Forms/Attributes/ShowOnlyBrowsableAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public sealed class ShowOnlyBrowsableAttribute : Attribute - { - public ShowOnlyBrowsableAttribute(bool onlyBrowsable) - { - OnlyBrowsable = onlyBrowsable; - } - - public bool OnlyBrowsable { get; } - } -} diff --git a/PriceChecker.UI.Forms/Attributes/StyleAttribute.cs b/PriceChecker.UI.Forms/Attributes/StyleAttribute.cs deleted file mode 100644 index 7063259..0000000 --- a/PriceChecker.UI.Forms/Attributes/StyleAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Windows; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property)] - public sealed class StyleAttribute : Attribute - { - public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Left; - } -} diff --git a/PriceChecker.UI.Forms/Attributes/TooltipSourceAttribute.cs b/PriceChecker.UI.Forms/Attributes/TooltipSourceAttribute.cs deleted file mode 100644 index cf9ee91..0000000 --- a/PriceChecker.UI.Forms/Attributes/TooltipSourceAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] - public sealed class TooltipSourceAttribute : Attribute - { - public TooltipSourceAttribute(string path) - { - Path = path; - } - - public string Path { get; } - } -} diff --git a/PriceChecker.UI.Forms/Attributes/ValidationRuleAttribute.cs b/PriceChecker.UI.Forms/Attributes/ValidationRuleAttribute.cs deleted file mode 100644 index 2dd184c..0000000 --- a/PriceChecker.UI.Forms/Attributes/ValidationRuleAttribute.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] - public sealed class ValidationRuleAttribute : Attribute - { - public ValidationRuleAttribute(Type validationRuleType, params object[] parameters) - { - ValidationRuleType = validationRuleType; - Parameters = parameters; - } - - public Type ValidationRuleType { get; } - public object[] Parameters { get; } - } -} diff --git a/PriceChecker.UI.Forms/Attributes/ValueConverterAttribute.cs b/PriceChecker.UI.Forms/Attributes/ValueConverterAttribute.cs deleted file mode 100644 index 0324af6..0000000 --- a/PriceChecker.UI.Forms/Attributes/ValueConverterAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Genius.PriceChecker.UI.Forms.Attributes -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] - public sealed class ValueConverterAttribute : Attribute - { - public ValueConverterAttribute(Type valueConverterType) - { - ValueConverterType = valueConverterType; - } - - public Type ValueConverterType { get; } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/AttachingBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/AttachingBehavior.cs deleted file mode 100644 index 7d81e86..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/AttachingBehavior.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections; -using System.ComponentModel; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using Genius.PriceChecker.UI.Forms.Attributes; -using Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors; -using Genius.PriceChecker.UI.Forms.ViewModels; -using Microsoft.Xaml.Behaviors; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid -{ - public class AttachingBehavior : Behavior - { - private bool? _showOnlyBrowsable; - - private static IAutoGridColumnBehavior[] _columnBehaviors; - - static AttachingBehavior() - { - _columnBehaviors = new IAutoGridColumnBehavior[] { - new ColumnReadOnlyBehavior(), - new ColumnDisplayIndexBehavior(), - new ColumnButtonBehavior(), - new ColumnWithImageBehavior(), - new ColumnComboboxBehavior(), - new ColumnValidationBehavior(), - new ColumnConverterBehavior(), - new ColumnFormattingBehavior(), - new ColumnTooltipBehavior(), - new ColumnStylingBehavior(), - new ColumnHeaderNameBehavior() - }; - } - - protected override void OnAttached() - { - AssociatedObject.AutoGenerateColumns = true; - AssociatedObject.AutoGeneratingColumn += OnAutoGeneratingColumn; - - var dpd = DependencyPropertyDescriptor.FromProperty(DataGrid.ItemsSourceProperty, typeof(DataGrid)); - if (dpd != null) - { - dpd.AddValueChanged(AssociatedObject, OnItemsSourceChanged); - } - - base.OnAttached(); - } - - private void OnItemsSourceChanged(object sender, EventArgs e) - { - if (AssociatedObject.ItemsSource == null) - { - return; - } - - if (AssociatedObject.SelectionMode == DataGridSelectionMode.Extended && - typeof(ISelectable).IsAssignableFrom(Helpers.GetListItemType(AssociatedObject.ItemsSource))) - { - BindIsSelected(); - } - } - - private void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) - { - var context = new AutoGridColumnContext(AssociatedObject, e, (PropertyDescriptor) e.PropertyDescriptor); - - if (!IsBrowsable(context)) - { - e.Cancel = true; - return; - } - - var ignoreProperties = new [] { - nameof(IHasDirtyFlag.IsDirty), - nameof(ISelectable.IsSelected), - nameof(INotifyDataErrorInfo.HasErrors) - }; - - if (ignoreProperties.Contains(e.PropertyName) - || context.GetAttribute() != null - || typeof(ICollection).IsAssignableFrom(context.Property.PropertyType)) - { - e.Cancel = true; - return; - } - - foreach (var columnBehavior in _columnBehaviors) - { - columnBehavior.Attach(context); - } - } - - private bool IsBrowsable(AutoGridColumnContext context) - { - _showOnlyBrowsable = _showOnlyBrowsable ?? context.Property.ComponentType.GetCustomAttributes(false) - .FirstOrDefault(x => x is ShowOnlyBrowsableAttribute b && b.OnlyBrowsable) != null; - - var browsable = context.GetAttribute(); - if (_showOnlyBrowsable.Value && browsable?.Browsable != true - || !_showOnlyBrowsable.Value && browsable?.Browsable == false) - { - return false; - } - - return true; - } - - private void BindIsSelected() - { - var binding = new Binding(nameof(ISelectable.IsSelected)); - var rowStyle = new Style { - TargetType = typeof(DataGridRow), - BasedOn = (Style) AssociatedObject.FindResource("MahApps.Styles.DataGridRow") - }; - rowStyle.Setters.Add(new Setter(DataGrid.IsSelectedProperty, binding)); - AssociatedObject.RowStyle = rowStyle; - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/AutoGridColumnContext.cs b/PriceChecker.UI.Forms/AutoGrid/AutoGridColumnContext.cs deleted file mode 100644 index 8756e65..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/AutoGridColumnContext.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.ComponentModel; -using System.Linq; -using System.Windows.Controls; -using System.Windows.Data; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid -{ - public class AutoGridColumnContext - { - public AutoGridColumnContext(DataGrid dataGrid, DataGridAutoGeneratingColumnEventArgs args, - PropertyDescriptor property) - { - DataGrid = dataGrid; - Args = args; - Property = property; - } - - public DataGrid DataGrid { get; } - public DataGridAutoGeneratingColumnEventArgs Args { get; } - public PropertyDescriptor Property { get; } - - public T GetAttribute() where T: Attribute - => Property.Attributes.OfType().FirstOrDefault(); - - public Binding GetBinding() - => (Args.Column as DataGridBoundColumn)?.Binding as Binding; - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnButtonBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnButtonBehavior.cs deleted file mode 100644 index 5d0d5f9..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnButtonBehavior.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Windows.Input; -using Genius.PriceChecker.UI.Forms.Attributes; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnButtonBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - if (!typeof(ICommand).IsAssignableFrom(context.Property.PropertyType)) - { - return; - } - - var icon = context.GetAttribute()?.Name; - - context.Args.Column = WpfHelpers.CreateButtonColumn(context.Property.Name, icon); - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnComboboxBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnComboboxBehavior.cs deleted file mode 100644 index 0a2c99e..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnComboboxBehavior.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections; -using Genius.PriceChecker.UI.Forms.Attributes; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnComboboxBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - if (context.DataGrid.IsReadOnly || context.Args.Column.IsReadOnly) - { - return; - } - - var selectFromListAttr = context.GetAttribute(); - if (selectFromListAttr == null) - { - return; - } - - if (selectFromListAttr.FromOwnerContext) - { - var prop = context.DataGrid.DataContext.GetType().GetProperty(selectFromListAttr.CollectionPropertyName); - var value = prop.GetValue(context.DataGrid.DataContext) as IEnumerable; - context.Args.Column = WpfHelpers.CreateComboboxColumnWithStaticItemsSource( - value, context.Property.Name); - } - else - { - context.Args.Column = WpfHelpers.CreateComboboxColumnWithItemsSourcePerRow( - selectFromListAttr.CollectionPropertyName, context.Property.Name); - } - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnConverterBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnConverterBehavior.cs deleted file mode 100644 index 1dcad0a..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnConverterBehavior.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Windows.Data; -using Genius.PriceChecker.UI.Forms.Attributes; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnConverterBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - var converterAttr = context.GetAttribute(); - if (converterAttr == null) - { - return; - } - - var binding = context.GetBinding(); - binding.Converter = (IValueConverter)Activator.CreateInstance(converterAttr.ValueConverterType); - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnDisplayIndexBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnDisplayIndexBehavior.cs deleted file mode 100644 index a84c72d..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnDisplayIndexBehavior.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Windows.Input; -using Genius.PriceChecker.UI.Forms.Attributes; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnDisplayIndexBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - var index = context.GetAttribute()?.Index; - - if (index.HasValue) - { - context.Args.Column.DisplayIndex = index.Value; - } - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnFormattingBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnFormattingBehavior.cs deleted file mode 100644 index 1e489c2..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnFormattingBehavior.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Windows; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnFormattingBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - var format = context.GetAttribute(); - if (format == null) - { - return; - } - - context.GetBinding().StringFormat = format.DataFormatString; - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnHeaderNameBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnHeaderNameBehavior.cs deleted file mode 100644 index 75b80f0..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnHeaderNameBehavior.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.RegularExpressions; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnHeaderNameBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - if (context.Args.Column.Header is not string headerText) - { - return; - } - - context.Args.Column.Header = Regex.Replace(headerText, "[A-Z]", " $0"); - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnReadOnlyBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnReadOnlyBehavior.cs deleted file mode 100644 index f4468e8..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnReadOnlyBehavior.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnReadOnlyBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - var isReadOnly = context.GetAttribute()?.IsReadOnly; - - if (isReadOnly == true) - { - context.Args.Column.IsReadOnly = true; - } - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnStylingBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnStylingBehavior.cs deleted file mode 100644 index 442b58a..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnStylingBehavior.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Genius.PriceChecker.UI.Forms.Attributes; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnStylingBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - var style = context.GetAttribute(); - if (style == null) - { - return; - } - - WpfHelpers.SetCellHorizontalAlignment(context.Args.Column, style.HorizontalAlignment); - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnTooltipBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnTooltipBehavior.cs deleted file mode 100644 index 87ccd09..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnTooltipBehavior.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using Genius.PriceChecker.UI.Forms.Attributes; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnTooltipBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - var path = context.GetAttribute()?.Path; - if (path == null) - { - return; - } - - var style = WpfHelpers.EnsureDefaultCellStyle(context.Args.Column); - var binding = new Binding(path); - style.Setters.Add(new Setter(ToolTipService.ToolTipProperty, binding)); - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnValidationBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnValidationBehavior.cs deleted file mode 100644 index a4399bc..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnValidationBehavior.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Windows; -using System.Windows.Controls; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnValidationBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - if (context.DataGrid.IsReadOnly || context.Args.Column.IsReadOnly) - { - return; - } - - var columnBinding = context.GetBinding(); - if (columnBinding == null) - { - return; - } - - //columnBinding.ValidatesOnDataErrors = true; - columnBinding.ValidatesOnNotifyDataErrors = true; - columnBinding.NotifyOnValidationError = true; - ((DataGridBoundColumn)context.Args.Column).ElementStyle - = (Style)Application.Current.FindResource("ValidatableCellElementStyle"); - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnWithImageBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnWithImageBehavior.cs deleted file mode 100644 index 1db211e..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Behaviors/ColumnWithImageBehavior.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Media.Imaging; -using Genius.PriceChecker.UI.Forms.Attributes; -using WpfAnimatedGif; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid.Behaviors -{ - public class ColumnWithImageBehavior : IAutoGridColumnBehavior - { - public void Attach(AutoGridColumnContext context) - { - var iconAttr = context.GetAttribute(); - if (iconAttr == null) - { - return; - } - - context.Args.Column = CreateColumnWithImage(context.Property.Name, - iconAttr.IconPropertyPath, fixedSize: iconAttr.FixedSize, - imageIsPath: true, hideText: iconAttr.HideText); - } - - private static DataGridTemplateColumn CreateColumnWithImage(string valuePath, - string imageName, string imageTooltip = null, - string imageVisibilityFlagPath = null, - double? fixedSize = null, bool imageIsPath = false, - bool hideText = false) - { - var bindToValue = new Binding(valuePath); - var column = new DataGridTemplateColumn { - Header = valuePath, - SortMemberPath = valuePath - }; - - var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel)); - stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal); - - var imageFactory = new FrameworkElementFactory(typeof(Image)); - - if (imageIsPath) - imageFactory.SetBinding(ImageBehavior.AnimatedSourceProperty, new Binding(imageName)); - else - imageFactory.SetValue(ImageBehavior.AnimatedSourceProperty, (BitmapImage)Application.Current.FindResource(imageName)); - - if (imageTooltip != null) - imageFactory.SetValue(Image.ToolTipProperty, imageTooltip); - - if (imageVisibilityFlagPath != null) - imageFactory.SetValue(Image.VisibilityProperty, new Binding(imageVisibilityFlagPath) { Converter = new BooleanToVisibilityConverter() }); - - if (fixedSize != null) - { - imageFactory.SetValue(Image.HeightProperty, fixedSize.Value); - imageFactory.SetValue(Image.WidthProperty, fixedSize.Value); - } - - stackPanelFactory.AppendChild(imageFactory); - - if (!hideText) - { - var textFactory = new FrameworkElementFactory(typeof(TextBlock)); - textFactory.SetBinding(TextBlock.TextProperty, bindToValue); - textFactory.SetValue(Image.MarginProperty, new Thickness(5, 0, 0, 0)); - stackPanelFactory.AppendChild(textFactory); - } - - column.CellTemplate = new DataTemplate { VisualTree = stackPanelFactory }; - - return column; - } - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/IAutoGridColumnBehavior.cs b/PriceChecker.UI.Forms/AutoGrid/IAutoGridColumnBehavior.cs deleted file mode 100644 index 5787fff..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/IAutoGridColumnBehavior.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Genius.PriceChecker.UI.Forms.AutoGrid -{ - public interface IAutoGridColumnBehavior - { - void Attach(AutoGridColumnContext context); - } -} diff --git a/PriceChecker.UI.Forms/AutoGrid/Properties.cs b/PriceChecker.UI.Forms/AutoGrid/Properties.cs deleted file mode 100644 index 03c9254..0000000 --- a/PriceChecker.UI.Forms/AutoGrid/Properties.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using Genius.PriceChecker.UI.Forms.Attributes; -using Genius.PriceChecker.UI.Forms.ViewModels; - -namespace Genius.PriceChecker.UI.Forms.AutoGrid -{ - public static class Properties - { - public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.RegisterAttached( - "ItemsSource", - typeof(IEnumerable), - typeof(Properties), - new PropertyMetadata(ItemsSourceChanged) - ); - - public static void SetItemsSource(DependencyObject element, IEnumerable value) - { - element.SetValue(ItemsSourceProperty, value); - } - - public static IEnumerable GetItemsSource(DependencyObject element) - { - return (IEnumerable) element.GetValue(ItemsSourceProperty); - } - - private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var itemType = Helpers.GetListItemType(e.NewValue); - var properties = itemType.GetProperties(); - var groupByProps = properties - .Where(x => x.GetCustomAttributes(false).OfType().Any()) - .ToList(); - var filterByProps = properties - .Where(x => x.GetCustomAttributes(false).OfType().Any()) - .ToList(); - - if (!groupByProps.Any() && !filterByProps.Any()) - { - d.SetValue(DataGrid.ItemsSourceProperty, e.NewValue); - } - else - { - var collectionViewSource = new CollectionViewSource(); - collectionViewSource.Source = e.NewValue; - - SetupGrouping(groupByProps, collectionViewSource); - SetupFiltering(d, filterByProps, collectionViewSource); - - d.SetValue(DataGrid.ItemsSourceProperty, collectionViewSource.View); - } - } - - private static void SetupGrouping(List groupByProps, CollectionViewSource collectionViewSource) - { - foreach (var groupByProp in groupByProps) - { - collectionViewSource.GroupDescriptions.Add(new PropertyGroupDescription(groupByProp.Name)); - } - } - - private static void SetupFiltering(DependencyObject d, List filterByProps, - CollectionViewSource collectionViewSource) - { - var vm = ((FrameworkElement)d).DataContext as ViewModelBase; - if (vm == null) - throw new InvalidCastException($"Cannot cast DataContext to {nameof(ViewModelBase)}"); - - var filterContext = vm.GetType().GetProperties() - .FirstOrDefault(x => x.GetCustomAttributes(false).OfType().Any()); - - if (filterContext == null || !filterByProps.Any()) - return; - - string filter = string.Empty; - vm.WhenChanged(filterContext.Name, (string s) => { - filter = s; - collectionViewSource.View.Refresh(); - }); - - collectionViewSource.Filter += (object sender, FilterEventArgs e) => - { - if (string.IsNullOrEmpty(filter)) - { - e.Accepted = true; - return; - } - - foreach (var filterProp in filterByProps) - { - var value = filterProp.GetValue(e.Item); - if (value is string stringValue) - { - if (stringValue.Contains(filter, StringComparison.InvariantCultureIgnoreCase)) - { - e.Accepted = true; - return; - } - } - else - { - throw new NotSupportedException($"Type {value.GetType().Name} is not suppoerted yet for AutoGrid filtering"); - } - } - - e.Accepted = false; - }; - } - } -} diff --git a/PriceChecker.UI.Forms/DropDownMenuItem.cs b/PriceChecker.UI.Forms/DropDownMenuItem.cs deleted file mode 100644 index 4f0412d..0000000 --- a/PriceChecker.UI.Forms/DropDownMenuItem.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Windows.Input; - -namespace Genius.PriceChecker.UI.Forms -{ - public class DropDownMenuItem - { - public DropDownMenuItem(string name, ICommand command) - { - Name = name; - Command = command; - } - - public string Name { get; set; } - public ICommand Command { get; set; } - } -} diff --git a/PriceChecker.UI.Forms/Helpers.cs b/PriceChecker.UI.Forms/Helpers.cs deleted file mode 100644 index 28ab6c2..0000000 --- a/PriceChecker.UI.Forms/Helpers.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using System.Windows.Data; - -namespace Genius.PriceChecker.UI.Forms -{ - internal class Helpers - { - public static Type GetListItemType(object value) - { - if (value is ListCollectionView listCollectionView) - value = listCollectionView.SourceCollection; - - if (value is ITypedObservableList typedObservableList) - return typedObservableList.ItemType; - - return value.GetType().GetGenericArguments().Single(); - } - - public static string MakeCaptionFromPropertyName(string propertyName) - { - return Regex.Replace(propertyName, @"(?<=[^$])([A-Z])", " $1"); - } - } -} diff --git a/PriceChecker.UI.Forms/PriceChecker.UI.Forms.csproj b/PriceChecker.UI.Forms/PriceChecker.UI.Forms.csproj deleted file mode 100644 index 7286c27..0000000 --- a/PriceChecker.UI.Forms/PriceChecker.UI.Forms.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - net5.0-windows - true - Genius.PriceChecker.UI.Forms - - - - - - - - - - - - diff --git a/PriceChecker.UI.Forms/TypedObservableList.cs b/PriceChecker.UI.Forms/TypedObservableList.cs deleted file mode 100644 index bae92d7..0000000 --- a/PriceChecker.UI.Forms/TypedObservableList.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.ComponentModel; - -namespace Genius.PriceChecker.UI.Forms -{ - public interface ITypedObservableList - { - Type ItemType { get; } - } - - public class TypedObservableList : ObservableCollection, ITypedObservableList, ITypedList - { - public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) - { - return TypeDescriptor.GetProperties(typeof(TType)); - } - - public string GetListName(PropertyDescriptor[] listAccessors) - { - return null; - } - - public Type ItemType => typeof(TType); - } -} diff --git a/PriceChecker.UI.Forms/ViewModels/IHasDirtyFlag.cs b/PriceChecker.UI.Forms/ViewModels/IHasDirtyFlag.cs deleted file mode 100644 index e06c97d..0000000 --- a/PriceChecker.UI.Forms/ViewModels/IHasDirtyFlag.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Genius.PriceChecker.UI.Forms.ViewModels -{ - public interface IHasDirtyFlag - { - bool IsDirty { get; set; } - } -} diff --git a/PriceChecker.UI.Forms/ViewModels/ISelectable.cs b/PriceChecker.UI.Forms/ViewModels/ISelectable.cs deleted file mode 100644 index 5b6eae1..0000000 --- a/PriceChecker.UI.Forms/ViewModels/ISelectable.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Genius.PriceChecker.UI.Forms.ViewModels -{ - public interface ISelectable - { - bool IsSelected { get; set; } - } -} diff --git a/PriceChecker.UI.Forms/ViewModels/TabViewModelBase.cs b/PriceChecker.UI.Forms/ViewModels/TabViewModelBase.cs deleted file mode 100644 index 02a4c01..0000000 --- a/PriceChecker.UI.Forms/ViewModels/TabViewModelBase.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel; - -namespace Genius.PriceChecker.UI.Forms.ViewModels -{ - public interface ITabViewModel : IViewModel - { - IActionCommand Activated { get; } - IActionCommand Deactivated { get; } - } - - public abstract class TabViewModelBase : ViewModelBase, ITabViewModel - { - [Browsable(false)] - public IActionCommand Activated { get; } = new ActionCommand(); - [Browsable(false)] - public IActionCommand Deactivated { get; } = new ActionCommand(); - } -} diff --git a/PriceChecker.UI.Forms/ViewModels/ViewModelBase.cs b/PriceChecker.UI.Forms/ViewModels/ViewModelBase.cs deleted file mode 100644 index 6d57d12..0000000 --- a/PriceChecker.UI.Forms/ViewModels/ViewModelBase.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Windows.Controls; -using Genius.PriceChecker.UI.Forms.Attributes; - -namespace Genius.PriceChecker.UI.Forms.ViewModels -{ - public interface IViewModel : INotifyPropertyChanged, INotifyDataErrorInfo - { - bool TryGetPropertyValue(string propertyName, out object value); - } - - public abstract class ViewModelBase : IViewModel - { - protected readonly ConcurrentDictionary _propertyBag = new(); - private readonly Dictionary> _validationRules = new(); - private readonly Dictionary> _errors = new(); - private bool _suspendDirtySet = false; - - public ViewModelBase() - { - DetectValidationRules(); - } - - public IEnumerable GetErrors(string propertyName) - { - return string.IsNullOrWhiteSpace(propertyName) ? - _errors.SelectMany(entry => entry.Value) : - _errors.TryGetValue(propertyName, out List errors) ? - errors : - new List(); - } - - public bool TryGetPropertyValue(string propertyName, out object value) - { - return _propertyBag.TryGetValue(propertyName, out value); - } - - protected void InitializeProperties(Action action) - { - _suspendDirtySet = true; - try - { - action(); - - if (this is IHasDirtyFlag hasDirtyFlag) - { - hasDirtyFlag.IsDirty = false; - } - } - finally - { - _suspendDirtySet = false; - } - } - - protected T GetOrDefault(T defaultValue = default(T), [CallerMemberName] string name = null) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - var result = _propertyBag.GetOrAdd(name, _ => defaultValue); - - return (T) result; - } - - protected void RaiseAndSetIfChanged(T value, Action valueChangedHandler = null, [CallerMemberName] string name = null) - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - object oldValue; - var isInitial = !_propertyBag.TryGetValue(name, out oldValue); - - if (Equals(oldValue, value)) - { - if (isInitial) - { - // Initial validation - ValidateProperty(name, value); - } - return; - } - - _propertyBag.AddOrUpdate(name, _ => value, (_, __) => value); - OnPropertyChanged(name); - - if (!_suspendDirtySet && - this is IHasDirtyFlag hasDirtyFlag && - name != nameof(IHasDirtyFlag.IsDirty) && - (this is not ISelectable || name != nameof(ISelectable.IsSelected))) - { - hasDirtyFlag.IsDirty = true; - } - - ValidateProperty(name, value); - - if (valueChangedHandler != null) - { - valueChangedHandler(isInitial ? value : (T)oldValue, value); - } - } - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - private void OnErrorsChanged(string propertyName) - { - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); - } - - private void ValidateProperty(string propertyName, object value) - { - // Clear previous errors of the current property to be validated - _errors.Remove(propertyName); - OnErrorsChanged(propertyName); - - if (!_validationRules.TryGetValue(propertyName, out var rules)) - { - return; - } - - foreach (var rule in rules) - { - AddError(propertyName, rule.Validate(value, CultureInfo.CurrentCulture)); - } - } - - private void AddError(string propertyName, ValidationResult validationResult) - { - if (validationResult.IsValid) - { - return; - } - - if (!_errors.TryGetValue(propertyName, out var errors)) - { - errors = new List(); - _errors.Add(propertyName, errors); - } - - errors.Add(validationResult.ErrorContent.ToString()); - OnErrorsChanged(propertyName); - } - - private void DetectValidationRules() - { - var allProperties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); - foreach (var prop in allProperties) - { - _validationRules.Add(prop.Name, new List()); - foreach (var attr in prop.GetCustomAttributes()) - { - var hasParams = attr.Parameters?.Any() == true; - var parameters = hasParams - ? new object[] { this }.Concat(attr.Parameters).ToArray() - : null; - var validationRule = (ValidationRule) (hasParams - ? Activator.CreateInstance(attr.ValidationRuleType, parameters) - : Activator.CreateInstance(attr.ValidationRuleType)); - _validationRules[prop.Name].Add(validationRule); - } - } - } - - public event PropertyChangedEventHandler PropertyChanged; - public event EventHandler ErrorsChanged; - - public virtual bool HasErrors => _errors.Any(); - } -} diff --git a/PriceChecker.UI.Forms/ViewModels/ViewModelExtensions.cs b/PriceChecker.UI.Forms/ViewModels/ViewModelExtensions.cs deleted file mode 100644 index 72d4833..0000000 --- a/PriceChecker.UI.Forms/ViewModels/ViewModelExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.ComponentModel; -using System.Linq.Expressions; -using Genius.PriceChecker.Infrastructure; - -namespace Genius.PriceChecker.UI.Forms.ViewModels -{ - public static class ViewModelExtensions - { - public static IDisposable WhenChanged(this TViewModel viewModel, Expression> propertyAccessor, Action handler) - where TViewModel : IViewModel - { - var propName = ExpressionHelpers.GetPropertyName(propertyAccessor); - - return WhenChanged(viewModel, propName, handler); - } - - public static IDisposable WhenChanged(this IViewModel viewModel, string propertyName, Action handler) - { - PropertyChangedEventHandler fn = (_, args) => - { - if (args.PropertyName != propertyName) - return; - - if (!viewModel.TryGetPropertyValue(propertyName, out var value)) - return; - - handler((TProperty) value); - }; - - viewModel.PropertyChanged += fn; - - return new DisposableAction(() => viewModel.PropertyChanged -= fn); - } - } -} diff --git a/PriceChecker.UI.Forms/WpfHelpers.cs b/PriceChecker.UI.Forms/WpfHelpers.cs deleted file mode 100644 index 5a56cea..0000000 --- a/PriceChecker.UI.Forms/WpfHelpers.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Markup; -using MahApps.Metro.Controls; - -namespace Genius.PriceChecker.UI.Forms -{ - [ExcludeFromCodeCoverage] - public static class WpfHelpers - { - public static void AddFlyout(FrameworkElement owner, string isOpenBindingPath, string sourcePath = null) - where T: Flyout, new() - { - var parentWindow = Window.GetWindow(owner); - object obj = parentWindow.FindName("flyoutsControl"); - var flyout = (FlyoutsControl) obj; - var child = new T(); - if (sourcePath == null) - { - child.DataContext = owner.DataContext; - } - else - { - BindingOperations.SetBinding(child, Flyout.DataContextProperty, - new Binding(sourcePath) { Source = owner.DataContext }); - //: TypeDescriptor.GetProperties(owner.DataContext).Find(sourcePath, false).GetValue(owner.DataContext); - } - BindingOperations.SetBinding(child, Flyout.IsOpenProperty, new Binding(isOpenBindingPath) { Source = owner.DataContext }); - (flyout as IAddChild).AddChild(child); - } - - public static DataGridTemplateColumn CreateButtonColumn(string commandPath, string iconName) - { - var caption = Helpers.MakeCaptionFromPropertyName(commandPath.Replace("Command", "")); - - var buttonFactory = new FrameworkElementFactory(typeof(Button)); - buttonFactory.SetBinding(Button.CommandProperty, new Binding(commandPath)); - buttonFactory.SetValue(Button.ToolTipProperty, caption); - buttonFactory.SetValue(Button.BorderThicknessProperty, new Thickness(0)); - if (iconName != null) - { - var imageFactory = new FrameworkElementFactory(typeof(Image)); - imageFactory.SetValue(Image.SourceProperty, Application.Current.FindResource(iconName)); - buttonFactory.AppendChild(imageFactory); - } - else - { - buttonFactory.SetValue(Button.ContentProperty, caption); - } - - var column = new DataGridTemplateColumn(); - column.CellTemplate = new DataTemplate { VisualTree = buttonFactory }; - return column; - } - - public static DataGridComboBoxColumn CreateComboboxColumnWithStaticItemsSource(IEnumerable itemsSource, string valuePath) - { - var column = new DataGridComboBoxColumn(); - column.Header = valuePath; - column.ItemsSource = itemsSource; - column.SelectedValueBinding = new Binding(valuePath); - return column; - } - - public static DataGridTemplateColumn CreateComboboxColumnWithItemsSourcePerRow(string itemsSourcePath, string valuePath) - { - var column = new DataGridTemplateColumn(); - column.Header = valuePath; - - var bindToValue = new Binding(valuePath); - var bindToItemsSource = new Binding(itemsSourcePath); - - var textFactory = new FrameworkElementFactory(typeof(TextBlock)); - textFactory.SetBinding(TextBlock.TextProperty, bindToValue); - var textTemplate = new DataTemplate(); - textTemplate.VisualTree = textFactory; - - var comboFactory = new FrameworkElementFactory(typeof(ComboBox)); - comboFactory.SetValue(ComboBox.IsTextSearchEnabledProperty, true); - comboFactory.SetBinding(ComboBox.SelectedItemProperty, bindToValue); - comboFactory.SetBinding(ComboBox.ItemsSourceProperty, bindToItemsSource); - - var comboTemplate = new DataTemplate { VisualTree = comboFactory }; - - column.CellTemplate = textTemplate; - column.CellEditingTemplate = comboTemplate; - - return column; - } - - public static Style EnsureDefaultCellStyle(DataGridColumn column) - { - if (column.CellStyle == null) - { - column.CellStyle = new Style { - TargetType = typeof(DataGridCell), - BasedOn = (Style) Application.Current.FindResource("MahApps.Styles.DataGridCell") - }; - } - - return column.CellStyle; - } - - public static void SetCellHorizontalAlignment(DataGridColumn column, HorizontalAlignment alignment) - { - EnsureDefaultCellStyle(column); - - column.CellStyle.Setters.Add(new Setter(DataGridCell.HorizontalAlignmentProperty, alignment)); - } - } -} diff --git a/PriceChecker.UI.Tests/TestBase.cs b/PriceChecker.UI.Tests/TestBase.cs index 98325c6..d6fd92b 100644 --- a/PriceChecker.UI.Tests/TestBase.cs +++ b/PriceChecker.UI.Tests/TestBase.cs @@ -4,9 +4,9 @@ using System.Reactive.Subjects; using System.Windows.Threading; using AutoFixture; -using Genius.PriceChecker.Infrastructure; -using Genius.PriceChecker.Infrastructure.Events; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.Infrastructure; +using Genius.Atom.Infrastructure.Events; +using Genius.Atom.UI.Forms.ViewModels; using Moq; namespace Genius.PriceChecker.UI.Tests diff --git a/PriceChecker.UI.Tests/Validation/MustBeUniqueValidationRuleTests.cs b/PriceChecker.UI.Tests/Validation/MustBeUniqueValidationRuleTests.cs index 4ce18e6..94f34b4 100644 --- a/PriceChecker.UI.Tests/Validation/MustBeUniqueValidationRuleTests.cs +++ b/PriceChecker.UI.Tests/Validation/MustBeUniqueValidationRuleTests.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Linq; using AutoFixture; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.UI.Forms.ViewModels; using Genius.PriceChecker.UI.Validation; using Xunit; diff --git a/PriceChecker.UI.Tests/ViewModels/MainViewModelTests.cs b/PriceChecker.UI.Tests/ViewModels/MainViewModelTests.cs index a281abf..531c07f 100644 --- a/PriceChecker.UI.Tests/ViewModels/MainViewModelTests.cs +++ b/PriceChecker.UI.Tests/ViewModels/MainViewModelTests.cs @@ -1,8 +1,8 @@ using System.Reactive.Subjects; using System.Windows.Shell; using AutoFixture; -using Genius.PriceChecker.UI.Forms; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.UI.Forms; +using Genius.Atom.UI.Forms.ViewModels; using Genius.PriceChecker.UI.Helpers; using Genius.PriceChecker.UI.ViewModels; using Moq; diff --git a/PriceChecker.UI.Tests/ViewModels/TrackerViewModelTests.cs b/PriceChecker.UI.Tests/ViewModels/TrackerViewModelTests.cs index 7d59db3..8d105a9 100644 --- a/PriceChecker.UI.Tests/ViewModels/TrackerViewModelTests.cs +++ b/PriceChecker.UI.Tests/ViewModels/TrackerViewModelTests.cs @@ -7,7 +7,7 @@ using Genius.PriceChecker.Core.Messages; using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Repositories; -using Genius.PriceChecker.UI.Forms; +using Genius.Atom.UI.Forms; using Genius.PriceChecker.UI.Helpers; using Genius.PriceChecker.UI.ViewModels; using Moq; @@ -99,7 +99,7 @@ public void RefreshAllCommand__Enqueues_selected_products_for_scan() public void OpenAddProductFlyoutCommand__When_flyout_is_closed__Flyout_shows_up() { // Arrange - var products = SampleProducts(); + SampleProducts(); var sut = CreateSystemUnderTest(); sut.IsAddEditProductVisible = false; sut.EditingProduct = null; @@ -117,7 +117,7 @@ public void OpenAddProductFlyoutCommand__When_flyout_is_closed__Flyout_shows_up( public void OpenAddProductFlyoutCommand__When_flyout_is_opened__Flyout_closed() { // Arrange - var products = SampleProducts(); + SampleProducts(); var sut = CreateSystemUnderTest(); sut.IsAddEditProductVisible = true; @@ -167,7 +167,7 @@ public void OpenEditProductFlyoutCommand__When_flyout_is_closed__Flyout_shows_up public void OpenEditProductFlyoutCommand__When_flyout_is_opened__Flyout_closed() { // Arrange - var products = SampleProducts(); + SampleProducts(); var sut = CreateSystemUnderTest(); sut.IsAddEditProductVisible = true; @@ -182,7 +182,7 @@ public void OpenEditProductFlyoutCommand__When_flyout_is_opened__Flyout_closed() public void OpenEditProductFlyoutCommand__Product_committed__Flyout_closed() { // Arrange - var products = SampleProducts(); + SampleProducts(); var sut = CreateSystemUnderTest(); sut.Products[0].IsSelected = true; sut.OpenEditProductFlyoutCommand.Execute(null); // trigger to open flyout @@ -260,7 +260,7 @@ public void ProductScanStartedEvent_fired__Appropriate_product_changed_status() // Arrange var products = SampleProducts(); var sut = CreateSystemUnderTest(); - var productScanningIndex = 1; + const int productScanningIndex = 1; // Act _productScanStartedEventSubject.OnNext(new ProductScanStartedEvent(products.ElementAt(productScanningIndex))); @@ -276,7 +276,7 @@ public void ProductScannedEvent_fired__Appropriate_product_changed_status() var products = SampleProducts(); var sut = CreateSystemUnderTest(); var lowestUpdated = Fixture.Create(); - var productScannedIndex = 1; + const int productScannedIndex = 1; // Act _productScannedEventSubject.OnNext(new ProductScannedEvent(products.ElementAt(productScannedIndex), lowestUpdated)); @@ -291,7 +291,7 @@ public void ProductScanFailedEvent_fired__Appropriate_product_set_to_failed() // Arrange var products = SampleProducts(); var sut = CreateSystemUnderTest(); - var productFailedIndex = 1; + const int productFailedIndex = 1; var errorMessage = Fixture.Create(); // Act @@ -305,7 +305,7 @@ public void ProductScanFailedEvent_fired__Appropriate_product_set_to_failed() public void Deactivated__Flyout_closed() { // Arrange - var products = SampleProducts(); + SampleProducts(); var sut = CreateSystemUnderTest(); sut.IsAddEditProductVisible = true; diff --git a/PriceChecker.UI/App.xaml b/PriceChecker.UI/App.xaml index 09857dd..85afb9c 100644 --- a/PriceChecker.UI/App.xaml +++ b/PriceChecker.UI/App.xaml @@ -13,6 +13,8 @@ + + @@ -32,44 +34,6 @@ - - - - - - - - diff --git a/PriceChecker.UI/App.xaml.cs b/PriceChecker.UI/App.xaml.cs index 4bcec13..e5e3d7e 100644 --- a/PriceChecker.UI/App.xaml.cs +++ b/PriceChecker.UI/App.xaml.cs @@ -2,8 +2,8 @@ using System.Diagnostics.CodeAnalysis; using System.Windows; using Genius.PriceChecker.Core.Services; -using Genius.PriceChecker.Infrastructure.Events; -using Genius.PriceChecker.Infrastructure.Logging; +using Genius.Atom.Infrastructure.Events; +using Genius.Atom.Infrastructure.Logging; using Genius.PriceChecker.UI.Helpers; using Genius.PriceChecker.UI.ViewModels; using Genius.PriceChecker.UI.Views; @@ -31,11 +31,8 @@ protected override void OnStartup(StartupEventArgs e) serviceCollection.AddSingleton((NotifyIconViewModel)_notifyIcon.DataContext); ServiceProvider = serviceCollection.BuildServiceProvider(); - - ServiceProvider.GetService() - .AddProvider(new EventBasedLoggerProvider(ServiceProvider.GetService())); - Core.Module.Initialize(ServiceProvider); + Atom.UI.Forms.Module.Initialize(ServiceProvider); var mainWindow = ServiceProvider.GetRequiredService(); mainWindow.Show(); @@ -51,9 +48,9 @@ protected override void OnExit(ExitEventArgs e) _notifyIcon.Dispose(); } - private void ConfigureServices(IServiceCollection services) + private static void ConfigureServices(IServiceCollection services) { - Infrastructure.Module.Configure(services); + Atom.Infrastructure.Module.Configure(services); Core.Module.Configure(services); // Framework: diff --git a/PriceChecker.UI/Helpers/TrackerScanContext.cs b/PriceChecker.UI/Helpers/TrackerScanContext.cs index 08c5f43..abce746 100644 --- a/PriceChecker.UI/Helpers/TrackerScanContext.cs +++ b/PriceChecker.UI/Helpers/TrackerScanContext.cs @@ -2,7 +2,7 @@ using System.Reactive.Subjects; using Genius.PriceChecker.Core.Messages; using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; namespace Genius.PriceChecker.UI.Helpers { @@ -27,9 +27,8 @@ internal sealed class TrackerScanContext : ITrackerScanContext public TrackerScanContext(IEventBus eventBus) { eventBus.WhenFired() - .Subscribe(ev => { - NotifyStarted(ev.ProductsCount); - }); + .Subscribe(ev => + NotifyStarted(ev.ProductsCount)); } public void NotifyStarted(int count) @@ -63,14 +62,16 @@ public void NotifyProgressChange(ProductScanStatus productScanStatus) double progress = CalculateProgress(); - TrackerScanStatus status = Helpers.TrackerScanStatus.InProgress; + var status = TrackerScanStatus.InProgress; if (_finished == _count) { _started = false; - status = Helpers.TrackerScanStatus.Finished; + status = TrackerScanStatus.Finished; } else if (HasErrors) - status = Helpers.TrackerScanStatus.InProgressWithErrors; + { + status = TrackerScanStatus.InProgressWithErrors; + } _scanProgress.OnNext((status, progress)); } diff --git a/PriceChecker.UI/NotifyIconResources.xaml.cs b/PriceChecker.UI/NotifyIconResources.xaml.cs index cadcdb7..57de972 100644 --- a/PriceChecker.UI/NotifyIconResources.xaml.cs +++ b/PriceChecker.UI/NotifyIconResources.xaml.cs @@ -15,9 +15,8 @@ public NotifyIconResources() var notifyIcon = (TaskbarIcon)this["NotifyIcon"]; var viewModel = (NotifyIconViewModel)notifyIcon.DataContext; - viewModel.ShowBalloonTipTriggered += (_, args) => { + viewModel.ShowBalloonTipTriggered += (_, args) => notifyIcon.ShowBalloonTip(args.Title, args.Message, args.Icon); - }; } } } diff --git a/PriceChecker.UI/PriceChecker.UI.csproj b/PriceChecker.UI/PriceChecker.UI.csproj index d7a46da..2ce636b 100644 --- a/PriceChecker.UI/PriceChecker.UI.csproj +++ b/PriceChecker.UI/PriceChecker.UI.csproj @@ -27,17 +27,17 @@ - - - + - + + + diff --git a/PriceChecker.UI/Validation/MustBeUniqueValidationRule.cs b/PriceChecker.UI/Validation/MustBeUniqueValidationRule.cs index 886eb47..986cb81 100644 --- a/PriceChecker.UI/Validation/MustBeUniqueValidationRule.cs +++ b/PriceChecker.UI/Validation/MustBeUniqueValidationRule.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Linq; using System.Windows.Controls; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.UI.Forms.ViewModels; namespace Genius.PriceChecker.UI.Validation { diff --git a/PriceChecker.UI/ViewModels/AgentViewModel.cs b/PriceChecker.UI/ViewModels/AgentViewModel.cs index e2ea5c3..917b6f0 100644 --- a/PriceChecker.UI/ViewModels/AgentViewModel.cs +++ b/PriceChecker.UI/ViewModels/AgentViewModel.cs @@ -3,8 +3,8 @@ using System.ComponentModel; using System.Linq; using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.UI.Forms.Attributes; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.UI.Forms.Attributes; +using Genius.Atom.UI.Forms.ViewModels; using Genius.PriceChecker.UI.Validation; namespace Genius.PriceChecker.UI.ViewModels @@ -52,17 +52,18 @@ private void ResetForm(bool firstTimeInit) return; } - Action init = () => { + if (firstTimeInit) + InitializeProperties(init); + else + init(); + + void init() + { Id = _agent?.Id; Url = _agent?.Url; PricePattern = _agent?.PricePattern; DecimalDelimiter = _agent?.DecimalDelimiter ?? '.'; - }; - - if (firstTimeInit) - this.InitializeProperties(init); - else - init(); + } } [Browsable(false)] diff --git a/PriceChecker.UI/ViewModels/AgentsViewModel.cs b/PriceChecker.UI/ViewModels/AgentsViewModel.cs index 34ddc1f..40de723 100644 --- a/PriceChecker.UI/ViewModels/AgentsViewModel.cs +++ b/PriceChecker.UI/ViewModels/AgentsViewModel.cs @@ -2,8 +2,8 @@ using System.Linq; using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Repositories; -using Genius.PriceChecker.UI.Forms; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.UI.Forms; +using Genius.Atom.UI.Forms.ViewModels; using Genius.PriceChecker.UI.Helpers; namespace Genius.PriceChecker.UI.ViewModels diff --git a/PriceChecker.UI/ViewModels/LogItemViewModel.cs b/PriceChecker.UI/ViewModels/LogItemViewModel.cs index cbf74ca..813520c 100644 --- a/PriceChecker.UI/ViewModels/LogItemViewModel.cs +++ b/PriceChecker.UI/ViewModels/LogItemViewModel.cs @@ -1,9 +1,9 @@ using System.ComponentModel; using System.Windows.Forms; using System.Windows.Media.Imaging; -using Genius.PriceChecker.UI.Forms; -using Genius.PriceChecker.UI.Forms.ViewModels; -using Genius.PriceChecker.UI.Forms.Attributes; +using Genius.Atom.UI.Forms; +using Genius.Atom.UI.Forms.ViewModels; +using Genius.Atom.UI.Forms.Attributes; using Microsoft.Extensions.Logging; namespace Genius.PriceChecker.UI.ViewModels @@ -17,9 +17,8 @@ internal sealed class LogItemViewModel : ViewModelBase, ILogItemViewModel { public LogItemViewModel() { - CopyToClipboardCommand = new ActionCommand(_ => { - Clipboard.SetText(Message); - }); + CopyToClipboardCommand = new ActionCommand(_ => + Clipboard.SetText(Message)); } [IconSource(nameof(SeverityIcon), 16d)] diff --git a/PriceChecker.UI/ViewModels/LogsViewModel.cs b/PriceChecker.UI/ViewModels/LogsViewModel.cs index ffb290f..454f42f 100644 --- a/PriceChecker.UI/ViewModels/LogsViewModel.cs +++ b/PriceChecker.UI/ViewModels/LogsViewModel.cs @@ -2,10 +2,10 @@ using System.Collections.ObjectModel; using System.Linq; using System.Windows; -using Genius.PriceChecker.Infrastructure.Events; -using Genius.PriceChecker.Infrastructure.Logging; -using Genius.PriceChecker.UI.Forms; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.Infrastructure.Events; +using Genius.Atom.Infrastructure.Logging; +using Genius.Atom.UI.Forms; +using Genius.Atom.UI.Forms.ViewModels; using Microsoft.Extensions.Logging; namespace Genius.PriceChecker.UI.ViewModels @@ -20,14 +20,12 @@ public LogsViewModel(IEventBus eventBus) { eventBus.WhenFired() .Subscribe(x => { - Application.Current.Dispatcher.Invoke(() => { - LogItems.Add(new LogItemViewModel { Severity = x.Severity, Logger = x.Logger, Message = x.Message }); - }); + Application.Current.Dispatcher.Invoke(() => + LogItems.Add(new LogItemViewModel { Severity = x.Severity, Logger = x.Logger, Message = x.Message }) + ); }); - CleanLogCommand = new ActionCommand(_ => { - LogItems.Clear(); - }); + CleanLogCommand = new ActionCommand(_ => LogItems.Clear()); LogItems.CollectionChanged += (_, args) => { if (HasNewErrors) diff --git a/PriceChecker.UI/ViewModels/MainViewModel.cs b/PriceChecker.UI/ViewModels/MainViewModel.cs index 8108459..62b76e9 100644 --- a/PriceChecker.UI/ViewModels/MainViewModel.cs +++ b/PriceChecker.UI/ViewModels/MainViewModel.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Windows.Shell; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.UI.Forms.ViewModels; using Genius.PriceChecker.UI.Helpers; using Hardcodet.Wpf.TaskbarNotification; diff --git a/PriceChecker.UI/ViewModels/NotifyIconViewModel.cs b/PriceChecker.UI/ViewModels/NotifyIconViewModel.cs index 7fccb42..542f4e7 100644 --- a/PriceChecker.UI/ViewModels/NotifyIconViewModel.cs +++ b/PriceChecker.UI/ViewModels/NotifyIconViewModel.cs @@ -1,7 +1,7 @@ using System; using System.Windows; using System.Windows.Input; -using Genius.PriceChecker.UI.Forms; +using Genius.Atom.UI.Forms; using Genius.PriceChecker.UI.Helpers; using Hardcodet.Wpf.TaskbarNotification; diff --git a/PriceChecker.UI/ViewModels/SettingsViewModel.cs b/PriceChecker.UI/ViewModels/SettingsViewModel.cs index 8bd6a88..de8a397 100644 --- a/PriceChecker.UI/ViewModels/SettingsViewModel.cs +++ b/PriceChecker.UI/ViewModels/SettingsViewModel.cs @@ -1,6 +1,6 @@ using System.Linq; using Genius.PriceChecker.Core.Repositories; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.UI.Forms.ViewModels; namespace Genius.PriceChecker.UI.ViewModels { diff --git a/PriceChecker.UI/ViewModels/TrackerProductSourceViewModel.cs b/PriceChecker.UI/ViewModels/TrackerProductSourceViewModel.cs index 586bea4..5ee759e 100644 --- a/PriceChecker.UI/ViewModels/TrackerProductSourceViewModel.cs +++ b/PriceChecker.UI/ViewModels/TrackerProductSourceViewModel.cs @@ -3,9 +3,9 @@ using System.ComponentModel.DataAnnotations; using System.Windows; using Genius.PriceChecker.Core.Models; -using Genius.PriceChecker.UI.Forms; -using Genius.PriceChecker.UI.Forms.Attributes; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.UI.Forms; +using Genius.Atom.UI.Forms.Attributes; +using Genius.Atom.UI.Forms.ViewModels; using Genius.PriceChecker.UI.Helpers; namespace Genius.PriceChecker.UI.ViewModels @@ -21,9 +21,8 @@ public TrackerProductSourceViewModel(IUserInteraction ui, ProductSource productS LastPrice = lastPrice; }); - ShowInBrowserCommand = new ActionCommand(_ => { - ui.ShowProductInBrowser(productSource); - }); + ShowInBrowserCommand = new ActionCommand(_ => + ui.ShowProductInBrowser(productSource)); } [Browsable(false)] diff --git a/PriceChecker.UI/ViewModels/TrackerProductViewModel.cs b/PriceChecker.UI/ViewModels/TrackerProductViewModel.cs index bb9323a..775f9f1 100644 --- a/PriceChecker.UI/ViewModels/TrackerProductViewModel.cs +++ b/PriceChecker.UI/ViewModels/TrackerProductViewModel.cs @@ -7,14 +7,14 @@ using System.Reactive.Linq; using System.Windows; using System.Windows.Media.Imaging; +using Genius.Atom.Infrastructure.Events; +using Genius.Atom.UI.Forms; +using Genius.Atom.UI.Forms.Attributes; +using Genius.Atom.UI.Forms.ViewModels; using Genius.PriceChecker.Core.Messages; using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Repositories; using Genius.PriceChecker.Core.Services; -using Genius.PriceChecker.Infrastructure.Events; -using Genius.PriceChecker.UI.Forms; -using Genius.PriceChecker.UI.Forms.Attributes; -using Genius.PriceChecker.UI.Forms.ViewModels; using Genius.PriceChecker.UI.Helpers; using Genius.PriceChecker.UI.ValueConverters; using ReactiveUI; @@ -56,7 +56,8 @@ public TrackerProductViewModel(Product product, IEventBus eventBus, _product = product; _ui = ui; - InitializeProperties(() => { + InitializeProperties(() => + { RefreshAgents(); RefreshCategories(); @@ -75,18 +76,18 @@ public TrackerProductViewModel(Product product, IEventBus eventBus, AddSourceCommand = new ActionCommand(_ => Sources.Add(CreateSourceViewModel(null))); - ResetCommand = new ActionCommand(_ => { - ResetForm(); - }, _ => _product != null); + ResetCommand = new ActionCommand(_ => ResetForm(), _ => _product != null); - DropPricesCommand = new ActionCommand(_ => { + DropPricesCommand = new ActionCommand(_ => + { if (!_ui.AskForConfirmation("Are you sure?", "Prices drop confirmation")) return; _productRepo.DropPrices(_product); Reconcile(true); }); - RefreshPriceCommand = new ActionCommand(_ => { + RefreshPriceCommand = new ActionCommand(_ => + { if (Status == ProductScanStatus.Scanning) return; _productMng.EnqueueScan(product.Id); @@ -109,20 +110,27 @@ public void Reconcile(bool lowestPriceUpdated) var previousLowestPrice = LowestPrice; LowestPrice = _product.Lowest?.Price; LowestFoundOn = _product.Lowest?.FoundDate; - Status = lowestPriceUpdated && _product.Lowest != null - ? ProductScanStatus.ScannedNewLowest - : _statusProvider.DetermineStatus(_product); + RecentPrice = _product.Recent.Any() + ? _product.Recent.Min(x => x.Price) + : null; + Status = lowestPriceUpdated && _product.Lowest != null ? + ProductScanStatus.ScannedNewLowest : + _statusProvider.DetermineStatus(_product); LastUpdated = null; if (lowestPriceUpdated && LowestPrice.HasValue && previousLowestPrice.HasValue) { - var x = (1 - LowestPrice.Value/previousLowestPrice) * 100; + var x = (1 - LowestPrice.Value / previousLowestPrice) * 100; StatusText = $"The new price is by {x:0}% less than by previous scan ({LowestPrice.Value:#,##0.00} vs {previousLowestPrice.Value:#,##0.00})"; } else if (Status == ProductScanStatus.ScannedWithErrors) + { StatusText = "One or more source hasn't updated its price. Check the logs."; + } else + { StatusText = string.Empty; + } if (_product.Recent.Any()) { @@ -131,9 +139,9 @@ public void Reconcile(bool lowestPriceUpdated) var pricesDict = _product.Recent.ToDictionary(x => x.ProductSourceId, x => x.Price); foreach (var source in Sources) { - source.LastPrice = pricesDict.ContainsKey(source.Id) - ? pricesDict[source.Id] - : null; + source.LastPrice = pricesDict.ContainsKey(source.Id) ? + pricesDict[source.Id] : + null; } } else @@ -159,15 +167,16 @@ private void CommitProduct() return; } - _product = _product ?? new Product(); + _product ??= new Product(); _product.Name = Name; _product.Category = Category; _product.Description = Description; - _product.Sources = Sources.Select(x => new ProductSource { + _product.Sources = Sources.Select(x => new ProductSource + { Id = x.Id, - AgentId = x.Agent, - AgentArgument = x.Argument + AgentId = x.Agent, + AgentArgument = x.Argument }).ToArray(); _productRepo.Store(_product); @@ -175,9 +184,9 @@ private void CommitProduct() private TrackerProductSourceViewModel CreateSourceViewModel(ProductSource productSource) { - var lastPrice = productSource == null || _product?.Recent == null - ? null - : _product.Recent.FirstOrDefault(x => x.ProductSourceId == productSource.Id)?.Price; + var lastPrice = productSource == null || _product?.Recent == null ? + null : + _product.Recent.FirstOrDefault(x => x.ProductSourceId == productSource.Id)?.Price; var vm = new TrackerProductSourceViewModel(_ui, productSource, lastPrice); vm.DeleteCommand.Executed.Subscribe(_ => Sources.Remove(vm)); @@ -213,15 +222,14 @@ private void ResetForm() public ObservableCollection Categories { get; } = new ObservableCollection(); [Browsable(true)] - [IconSource(nameof(StatusIcon), fixedSize: 16d, hideText: true)] + [IconSource(nameof(StatusIcon), fixedSize : 16d, hideText : true)] [TooltipSource(nameof(StatusText))] [Style(HorizontalAlignment = HorizontalAlignment.Right)] public ProductScanStatus Status { get => GetOrDefault(); - set => RaiseAndSetIfChanged(value, (@old, @new) => { - OnPropertyChanged(nameof(StatusIcon)); - }); + set => RaiseAndSetIfChanged(value, (_, __) => + OnPropertyChanged(nameof(StatusIcon))); } public BitmapImage StatusIcon => ResourcesHelper.GetStatusIcon(Status); @@ -262,6 +270,15 @@ public decimal? LowestPrice set => RaiseAndSetIfChanged(value); } + [Browsable(true)] + [DisplayFormat(DataFormatString = "€ #,##0.00")] + [Style(HorizontalAlignment = HorizontalAlignment.Right)] + public decimal? RecentPrice + { + get => GetOrDefault(); + set => RaiseAndSetIfChanged(value); + } + [Browsable(true)] [ValueConverter(typeof(DateTimeToHumanizedConverter))] public DateTime? LowestFoundOn diff --git a/PriceChecker.UI/ViewModels/TrackerViewModel.cs b/PriceChecker.UI/ViewModels/TrackerViewModel.cs index 3bd8b9c..d7b726a 100644 --- a/PriceChecker.UI/ViewModels/TrackerViewModel.cs +++ b/PriceChecker.UI/ViewModels/TrackerViewModel.cs @@ -6,10 +6,10 @@ using System.Windows.Input; using Genius.PriceChecker.Core.Messages; using Genius.PriceChecker.Core.Repositories; -using Genius.PriceChecker.Infrastructure.Events; -using Genius.PriceChecker.UI.Forms; -using Genius.PriceChecker.UI.Forms.Attributes; -using Genius.PriceChecker.UI.Forms.ViewModels; +using Genius.Atom.Infrastructure.Events; +using Genius.Atom.UI.Forms; +using Genius.Atom.UI.Forms.Attributes; +using Genius.Atom.UI.Forms.ViewModels; using Genius.PriceChecker.UI.Helpers; using ReactiveUI; diff --git a/PriceChecker.UI/ViewModels/ViewModelFactory.cs b/PriceChecker.UI/ViewModels/ViewModelFactory.cs index 86611f0..565a701 100644 --- a/PriceChecker.UI/ViewModels/ViewModelFactory.cs +++ b/PriceChecker.UI/ViewModels/ViewModelFactory.cs @@ -2,7 +2,7 @@ using Genius.PriceChecker.Core.Models; using Genius.PriceChecker.Core.Repositories; using Genius.PriceChecker.Core.Services; -using Genius.PriceChecker.Infrastructure.Events; +using Genius.Atom.Infrastructure.Events; using Genius.PriceChecker.UI.Helpers; namespace Genius.PriceChecker.UI.ViewModels diff --git a/PriceChecker.UI/Views/AddEditProductFlyout.xaml b/PriceChecker.UI/Views/AddEditProductFlyout.xaml index 8940434..beefd6f 100644 --- a/PriceChecker.UI/Views/AddEditProductFlyout.xaml +++ b/PriceChecker.UI/Views/AddEditProductFlyout.xaml @@ -5,7 +5,7 @@ xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" - xmlns:autogrid="clr-namespace:Genius.PriceChecker.UI.Forms.AutoGrid;assembly=PriceChecker.UI.Forms" + xmlns:autogrid="clr-namespace:Genius.Atom.UI.Forms.Controls.AutoGrid;assembly=Genius.Atom.UI.Forms" mc:Ignorable="d" Header="Add/Edit product" Position="Right" diff --git a/PriceChecker.UI/Views/Agents.xaml b/PriceChecker.UI/Views/Agents.xaml index ff277c5..40811dd 100644 --- a/PriceChecker.UI/Views/Agents.xaml +++ b/PriceChecker.UI/Views/Agents.xaml @@ -4,7 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" - xmlns:autogrid="clr-namespace:Genius.PriceChecker.UI.Forms.AutoGrid;assembly=PriceChecker.UI.Forms" + xmlns:autogrid="clr-namespace:Genius.Atom.UI.Forms.Controls.AutoGrid;assembly=Genius.Atom.UI.Forms" mc:Ignorable="d"> diff --git a/PriceChecker.UI/Views/Logs.xaml b/PriceChecker.UI/Views/Logs.xaml index 01c7595..75af4ce 100644 --- a/PriceChecker.UI/Views/Logs.xaml +++ b/PriceChecker.UI/Views/Logs.xaml @@ -4,7 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" - xmlns:autogrid="clr-namespace:Genius.PriceChecker.UI.Forms.AutoGrid;assembly=PriceChecker.UI.Forms" + xmlns:autogrid="clr-namespace:Genius.Atom.UI.Forms.Controls.AutoGrid;assembly=Genius.Atom.UI.Forms" mc:Ignorable="d"> diff --git a/PriceChecker.UI/Views/Tracker.xaml b/PriceChecker.UI/Views/Tracker.xaml index 8b477a1..a8f5c95 100644 --- a/PriceChecker.UI/Views/Tracker.xaml +++ b/PriceChecker.UI/Views/Tracker.xaml @@ -5,9 +5,8 @@ xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" - xmlns:autogrid="clr-namespace:Genius.PriceChecker.UI.Forms.AutoGrid;assembly=PriceChecker.UI.Forms" + xmlns:autogrid="clr-namespace:Genius.Atom.UI.Forms.Controls.AutoGrid;assembly=Genius.Atom.UI.Forms" xmlns:vm="clr-namespace:Genius.PriceChecker.UI.ViewModels" - xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks" mc:Ignorable="d"> diff --git a/PriceChecker.UI/Views/Tracker.xaml.cs b/PriceChecker.UI/Views/Tracker.xaml.cs index 80b36d3..28ab96a 100644 --- a/PriceChecker.UI/Views/Tracker.xaml.cs +++ b/PriceChecker.UI/Views/Tracker.xaml.cs @@ -2,7 +2,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; -using Genius.PriceChecker.UI.Forms; +using Genius.Atom.UI.Forms; using Genius.PriceChecker.UI.ViewModels; namespace Genius.PriceChecker.UI.Views diff --git a/PriceChecker.sln b/PriceChecker.sln index c2fb0c2..8c6a36c 100644 --- a/PriceChecker.sln +++ b/PriceChecker.sln @@ -7,16 +7,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriceChecker.UI", "PriceChe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriceChecker.Core", "PriceChecker.Core\PriceChecker.Core.csproj", "{F5EA6238-DEEA-4EBB-8B91-E34EECA8987A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriceChecker.Infrastructure", "PriceChecker.Infrastructure\PriceChecker.Infrastructure.csproj", "{98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriceChecker.UI.Forms", "PriceChecker.UI.Forms\PriceChecker.UI.Forms.csproj", "{C0573C09-C8A4-4740-BD4F-8E7963D721F3}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriceChecker.Core.Tests", "PriceChecker.Core.Tests\PriceChecker.Core.Tests.csproj", "{00DFA49D-E5D8-4102-B887-9E154EEABA9C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriceChecker.UI.Tests", "PriceChecker.UI.Tests\PriceChecker.UI.Tests.csproj", "{51D8DB71-C58D-45A0-B733-6BBEE2A64C44}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PriceChecker.UI.Forms.Tests", "PriceChecker.UI.Forms.Tests\PriceChecker.UI.Forms.Tests.csproj", "{1AFF9770-2023-4DB1-B9DC-257948B55FD5}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -54,30 +48,6 @@ Global {F5EA6238-DEEA-4EBB-8B91-E34EECA8987A}.Release|x64.Build.0 = Release|Any CPU {F5EA6238-DEEA-4EBB-8B91-E34EECA8987A}.Release|x86.ActiveCfg = Release|Any CPU {F5EA6238-DEEA-4EBB-8B91-E34EECA8987A}.Release|x86.Build.0 = Release|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Debug|x64.ActiveCfg = Debug|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Debug|x64.Build.0 = Debug|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Debug|x86.ActiveCfg = Debug|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Debug|x86.Build.0 = Debug|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Release|Any CPU.Build.0 = Release|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Release|x64.ActiveCfg = Release|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Release|x64.Build.0 = Release|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Release|x86.ActiveCfg = Release|Any CPU - {98AEA0B8-5C36-46C4-870F-67FF4E14AA8F}.Release|x86.Build.0 = Release|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Debug|x64.ActiveCfg = Debug|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Debug|x64.Build.0 = Debug|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Debug|x86.ActiveCfg = Debug|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Debug|x86.Build.0 = Debug|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Release|Any CPU.Build.0 = Release|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Release|x64.ActiveCfg = Release|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Release|x64.Build.0 = Release|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Release|x86.ActiveCfg = Release|Any CPU - {C0573C09-C8A4-4740-BD4F-8E7963D721F3}.Release|x86.Build.0 = Release|Any CPU {00DFA49D-E5D8-4102-B887-9E154EEABA9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {00DFA49D-E5D8-4102-B887-9E154EEABA9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {00DFA49D-E5D8-4102-B887-9E154EEABA9C}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -102,17 +72,5 @@ Global {51D8DB71-C58D-45A0-B733-6BBEE2A64C44}.Release|x64.Build.0 = Release|Any CPU {51D8DB71-C58D-45A0-B733-6BBEE2A64C44}.Release|x86.ActiveCfg = Release|Any CPU {51D8DB71-C58D-45A0-B733-6BBEE2A64C44}.Release|x86.Build.0 = Release|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Debug|x64.ActiveCfg = Debug|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Debug|x64.Build.0 = Debug|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Debug|x86.ActiveCfg = Debug|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Debug|x86.Build.0 = Debug|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Release|Any CPU.Build.0 = Release|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Release|x64.ActiveCfg = Release|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Release|x64.Build.0 = Release|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Release|x86.ActiveCfg = Release|Any CPU - {1AFF9770-2023-4DB1-B9DC-257948B55FD5}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/products.json b/products.json index 09902a9..4e2138c 100644 --- a/products.json +++ b/products.json @@ -19,8 +19,8 @@ "Recent": [ { "ProductSourceId": "60b9ceed-b34c-45ad-a142-af0ec5491d91", - "Price": 20.15, - "FoundDate": "2021-06-20T19:59:10.0234789+02:00" + "Price": 20.99, + "FoundDate": "2021-08-13T22:17:06.1706+02:00" } ] }, @@ -49,58 +49,13 @@ "Recent": [ { "ProductSourceId": "4753f887-5b20-41de-a06b-3c159262a273", - "Price": 759, - "FoundDate": "2021-06-20T19:58:55.3228205+02:00" + "Price": 739, + "FoundDate": "2021-08-13T22:16:48.6621087+02:00" }, { "ProductSourceId": "ebeaa98d-aedb-4bb8-b539-1de7e1ee25cc", - "Price": 698.99, - "FoundDate": "2021-06-20T19:58:55.322826+02:00" - } - ] - }, - { - "Id": "9ea2448b-736c-4d24-8634-8bfc978866f4", - "Category": "Photo", - "Name": "Sony 10-18mm/4 (SEL1018.AE)", - "Description": null, - "Sources": [ - { - "Id": "59b2149f-9148-40eb-88c3-16061247487f", - "AgentId": "amazon.de", - "AgentArgument": "B009AU9CQO" - }, - { - "Id": "190b93f8-d428-4b9b-aa70-127c513d352f", - "AgentId": "tweakers.net", - "AgentArgument": "324102/sony-10-18mm-f-40-oss.html" - }, - { - "Id": "7abd51a9-7ba1-4b72-9ff3-3fb89fce9045", - "AgentId": "hafo.nl", - "AgentArgument": "sony-sel-10-18-nex" - } - ], - "Lowest": { - "ProductSourceId": "59b2149f-9148-40eb-88c3-16061247487f", - "Price": 659.99, - "FoundDate": "2021-06-20T19:59:54.7048389+02:00" - }, - "Recent": [ - { - "ProductSourceId": "59b2149f-9148-40eb-88c3-16061247487f", - "Price": 659.99, - "FoundDate": "2021-06-20T19:59:54.7048389+02:00" - }, - { - "ProductSourceId": "190b93f8-d428-4b9b-aa70-127c513d352f", "Price": 699, - "FoundDate": "2021-06-20T19:59:54.7048437+02:00" - }, - { - "ProductSourceId": "7abd51a9-7ba1-4b72-9ff3-3fb89fce9045", - "Price": 729.00, - "FoundDate": "2021-06-20T19:59:54.7048439+02:00" + "FoundDate": "2021-08-13T22:16:48.6621219+02:00" } ] }, @@ -134,18 +89,18 @@ "Recent": [ { "ProductSourceId": "3b27b7fc-369d-4f90-ba4c-f209c1a0153b", - "Price": 168, - "FoundDate": "2021-06-20T19:59:04.0369221+02:00" + "Price": 177, + "FoundDate": "2021-08-13T22:16:48.5836644+02:00" }, { "ProductSourceId": "d786a796-b93c-468b-8fda-a2892d72802a", - "Price": 179.0, - "FoundDate": "2021-06-20T19:59:04.0369276+02:00" + "Price": 167.99, + "FoundDate": "2021-08-13T22:16:48.583672+02:00" }, { "ProductSourceId": "1778e5f5-b73d-418b-b3b8-06412828f54f", - "Price": 168.00, - "FoundDate": "2021-06-20T19:59:04.0369281+02:00" + "Price": 177.00, + "FoundDate": "2021-08-13T22:16:48.5836728+02:00" } ] }, @@ -173,44 +128,14 @@ }, "Recent": [ { - "ProductSourceId": "923b83ed-3fcb-43ca-80b0-0e9e717ec9c6", - "Price": 429.0, - "FoundDate": "2021-06-20T20:55:36.6045305+02:00" - } - ] - }, - { - "Id": "ba55c3e7-10f1-47c9-8c0e-41dd7c4834f8", - "Category": "Car", - "Name": "Viofo A129 Duo Pro Dashcam", - "Description": null, - "Sources": [ - { - "Id": "76ba5029-6cf6-4231-bc99-ca7d65cde50d", - "AgentId": "tweakers.net", - "AgentArgument": "1496006/viofo-a129-duo-pro-dashcam.html" + "ProductSourceId": "5f5a72a4-d6ad-4cab-9fb9-9d5839fa2a03", + "Price": 435.99, + "FoundDate": "2021-08-13T22:17:03.4602629+02:00" }, { - "Id": "62dcd317-1805-4b07-bb30-81045d4a6517", - "AgentId": "viofo.nl", - "AgentArgument": "viofo-a129-duo-pro-dashcam" - } - ], - "Lowest": { - "ProductSourceId": "76ba5029-6cf6-4231-bc99-ca7d65cde50d", - "Price": 179.95, - "FoundDate": "2021-06-20T19:58:54.0449809+02:00" - }, - "Recent": [ - { - "ProductSourceId": "76ba5029-6cf6-4231-bc99-ca7d65cde50d", - "Price": 179.95, - "FoundDate": "2021-06-20T19:58:54.0449809+02:00" - }, - { - "ProductSourceId": "62dcd317-1805-4b07-bb30-81045d4a6517", - "Price": 179.95, - "FoundDate": "2021-06-20T19:58:54.044988+02:00" + "ProductSourceId": "923b83ed-3fcb-43ca-80b0-0e9e717ec9c6", + "Price": 449.0, + "FoundDate": "2021-08-13T22:17:03.4602718+02:00" } ] }, @@ -239,13 +164,8 @@ "Recent": [ { "ProductSourceId": "cbb43572-35b9-4408-9e52-09f934b97336", - "Price": 589.55, - "FoundDate": "2021-06-20T19:59:05.9221498+02:00" - }, - { - "ProductSourceId": "ab80a6f9-c175-4c10-8f69-aee23e7efc64", - "Price": 607.95, - "FoundDate": "2021-06-20T19:59:05.9221536+02:00" + "Price": 611.78, + "FoundDate": "2021-08-13T22:17:53.0693481+02:00" } ] }, @@ -269,18 +189,18 @@ "Lowest": { "ProductSourceId": "aa039437-a86f-4cf7-9283-90c63a95e142", "Price": 389.95, - "FoundDate": "2021-06-20T21:06:04.357447+02:00" + "FoundDate": "2021-08-13T22:17:26.5477921+02:00" }, "Recent": [ { "ProductSourceId": "b129f9f2-416b-493e-af3c-ff0f255a66e9", "Price": 499.99, - "FoundDate": "2021-06-20T21:06:04.3574362+02:00" + "FoundDate": "2021-08-13T22:17:26.5477789+02:00" }, { "ProductSourceId": "aa039437-a86f-4cf7-9283-90c63a95e142", "Price": 389.95, - "FoundDate": "2021-06-20T21:06:04.357447+02:00" + "FoundDate": "2021-08-13T22:17:26.5477921+02:00" } ] }, @@ -324,13 +244,13 @@ "Lowest": { "ProductSourceId": "65f0f16f-a64c-4244-bd3b-ea58c27740ed", "Price": 349.99, - "FoundDate": "2021-06-20T20:00:12.515681+02:00" + "FoundDate": "2021-08-13T22:18:13.1946227+02:00" }, "Recent": [ { "ProductSourceId": "65f0f16f-a64c-4244-bd3b-ea58c27740ed", "Price": 349.99, - "FoundDate": "2021-06-20T20:00:12.515681+02:00" + "FoundDate": "2021-08-13T22:18:13.1946227+02:00" } ] }, @@ -349,13 +269,13 @@ "Lowest": { "ProductSourceId": "c33f08c8-854e-4a60-8eff-bd900cffe134", "Price": 279.99, - "FoundDate": "2021-06-04T18:18:42.7704614+02:00" + "FoundDate": "2021-08-13T22:17:08.7208269+02:00" }, "Recent": [ { "ProductSourceId": "c33f08c8-854e-4a60-8eff-bd900cffe134", - "Price": 349.0, - "FoundDate": "2021-06-20T19:58:59.6732845+02:00" + "Price": 279.99, + "FoundDate": "2021-08-13T22:17:08.7208269+02:00" } ] }, @@ -379,8 +299,8 @@ "Recent": [ { "ProductSourceId": "64f76755-0839-4c6c-9a7c-e28e4cf1e670", - "Price": 309.99, - "FoundDate": "2021-06-20T19:59:02.1066094+02:00" + "Price": 299.87, + "FoundDate": "2021-08-13T22:16:53.799147+02:00" } ] }, @@ -404,18 +324,18 @@ "Lowest": { "ProductSourceId": "e4b4c4d2-d318-4304-af16-3b97a18509f7", "Price": 399.99, - "FoundDate": "2021-06-20T21:06:55.5968457+02:00" + "FoundDate": "2021-08-13T22:17:45.4498742+02:00" }, "Recent": [ { "ProductSourceId": "e4b4c4d2-d318-4304-af16-3b97a18509f7", "Price": 399.99, - "FoundDate": "2021-06-20T21:06:55.5968457+02:00" + "FoundDate": "2021-08-13T22:17:45.4498742+02:00" }, { "ProductSourceId": "05b5f419-0ace-4ba7-b35a-4eb571c5ecc6", - "Price": 409, - "FoundDate": "2021-06-20T21:06:55.5968494+02:00" + "Price": 399.99, + "FoundDate": "2021-08-13T22:17:45.4498837+02:00" } ] }, @@ -439,8 +359,63 @@ "Recent": [ { "ProductSourceId": "e593c859-0dff-491d-a9ad-1a2f74a7f6f6", - "Price": 229.0, - "FoundDate": "2021-06-20T19:58:57.2717981+02:00" + "Price": 239.0, + "FoundDate": "2021-08-13T22:16:58.5578261+02:00" + } + ] + }, + { + "Id": "19ebfdd9-7870-4169-a357-876430934f53", + "Category": "Gadgets", + "Name": "Sony WF-1000XM4", + "Description": "", + "Sources": [ + { + "Id": "66a75039-0fcf-4407-8dc6-1065ad4ae925", + "AgentId": "tweakers.net", + "AgentArgument": "1712698/sony-wf-1000xm4-zwart.html" + }, + { + "Id": "f29d4d3a-e932-48d5-b979-3d9f91689137", + "AgentId": "amazon.de", + "AgentArgument": "B095DNPH4R" + }, + { + "Id": "af8a851b-a524-4849-857c-0af66bb8f404", + "AgentId": "amazon.nl", + "AgentArgument": "B095DNPH4R" + }, + { + "Id": "9417f567-36ee-421c-acd3-d2428d60076f", + "AgentId": "bol.com", + "AgentArgument": "sony-wf-1000xm4-volledig-draadloze-oordopjes-met-noise-cancelling-zwart/9300000039263860" + } + ], + "Lowest": { + "ProductSourceId": "66a75039-0fcf-4407-8dc6-1065ad4ae925", + "Price": 279, + "FoundDate": "2021-08-13T22:16:51.2820615+02:00" + }, + "Recent": [ + { + "ProductSourceId": "66a75039-0fcf-4407-8dc6-1065ad4ae925", + "Price": 279, + "FoundDate": "2021-08-13T22:16:51.2820615+02:00" + }, + { + "ProductSourceId": "f29d4d3a-e932-48d5-b979-3d9f91689137", + "Price": 279.0, + "FoundDate": "2021-08-13T22:16:51.2820754+02:00" + }, + { + "ProductSourceId": "af8a851b-a524-4849-857c-0af66bb8f404", + "Price": 279.00, + "FoundDate": "2021-08-13T22:16:51.2820785+02:00" + }, + { + "ProductSourceId": "9417f567-36ee-421c-acd3-d2428d60076f", + "Price": 279.00, + "FoundDate": "2021-08-13T22:16:51.2820812+02:00" } ] }