diff --git a/Application/Feeds/Category.cs b/Application/Feeds/Category.cs index b2de669..1fe0bf2 100644 --- a/Application/Feeds/Category.cs +++ b/Application/Feeds/Category.cs @@ -1,9 +1,10 @@ -using Realms; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using JetBrains.Annotations; +using Realms; namespace FeedCenter { @@ -11,13 +12,21 @@ namespace FeedCenter { public const string DefaultName = "< default >"; - private readonly Dictionary> _errorsByPropertyName = new(); + private readonly DataErrorDictionary _dataErrorDictionary; + + public Category() + { + _dataErrorDictionary = new DataErrorDictionary(); + _dataErrorDictionary.ErrorsChanged += DataErrorDictionaryErrorsChanged; + } + + [Ignored] + public ICollection Feeds { get; set; } [PrimaryKey] public Guid Id { get; set; } = Guid.NewGuid(); - [MapTo("Name")] - private string RawName { get; set; } = string.Empty; + public bool IsDefault { get; internal set; } public string Name { @@ -31,60 +40,37 @@ namespace FeedCenter } } - [Ignored] - public ICollection Feeds { get; set; } + [MapTo("Name")] + private string RawName { get; set; } = string.Empty; + + [UsedImplicitly] + public int SortKey => IsDefault ? 0 : 1; + + public bool HasErrors => _dataErrorDictionary.Any(); + + public IEnumerable GetErrors(string propertyName) + { + return _dataErrorDictionary.GetErrors(propertyName); + } + + public event EventHandler ErrorsChanged; + + private void DataErrorDictionaryErrorsChanged(object sender, DataErrorsChangedEventArgs e) + { + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(e.PropertyName)); + } public static Category CreateDefault() { return new Category { Name = DefaultName, IsDefault = true }; } - public bool IsDefault { get; internal set; } - - // ReSharper disable once UnusedMember.Global - public int SortKey => IsDefault ? 0 : 1; - - public bool HasErrors => _errorsByPropertyName.Any(); - - public event EventHandler ErrorsChanged; - - public IEnumerable GetErrors(string propertyName) - { - return _errorsByPropertyName.TryGetValue(propertyName, out var value) ? value : null; - } - - private void OnErrorsChanged(string propertyName) - { - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); - } - private void ValidateName() { - ClearErrors(nameof(Name)); + _dataErrorDictionary.ClearErrors(nameof(Name)); if (string.IsNullOrWhiteSpace(Name)) - AddError(nameof(Name), "Name cannot be empty"); - } - - private void AddError(string propertyName, string error) - { - if (!_errorsByPropertyName.ContainsKey(propertyName)) - _errorsByPropertyName[propertyName] = new List(); - - if (_errorsByPropertyName[propertyName].Contains(error)) - return; - - _errorsByPropertyName[propertyName].Add(error); - OnErrorsChanged(propertyName); - } - - private void ClearErrors(string propertyName) - { - if (!_errorsByPropertyName.ContainsKey(propertyName)) - return; - - _errorsByPropertyName.Remove(propertyName); - OnErrorsChanged(propertyName); + _dataErrorDictionary.AddError(nameof(Name), "Name cannot be empty"); } } } \ No newline at end of file diff --git a/Application/Feeds/DataErrorDictionary.cs b/Application/Feeds/DataErrorDictionary.cs new file mode 100644 index 0000000..fa3f63d --- /dev/null +++ b/Application/Feeds/DataErrorDictionary.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; + +namespace FeedCenter +{ + internal class DataErrorDictionary : Dictionary> + { + public event EventHandler ErrorsChanged; + + private void OnErrorsChanged(string propertyName) + { + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); + } + + public IEnumerable GetErrors(string propertyName) + { + return TryGetValue(propertyName, out var value) ? value : null; + } + + public void AddError(string propertyName, string error) + { + if (!ContainsKey(propertyName)) + this[propertyName] = new List(); + + if (this[propertyName].Contains(error)) + return; + + this[propertyName].Add(error); + OnErrorsChanged(propertyName); + } + + public void ClearErrors(string propertyName) + { + if (!ContainsKey(propertyName)) + return; + + Remove(propertyName); + OnErrorsChanged(propertyName); + } + } +} \ No newline at end of file diff --git a/Application/Feeds/Feed.cs b/Application/Feeds/Feed.cs index 8d4cc7d..f845ac9 100644 --- a/Application/Feeds/Feed.cs +++ b/Application/Feeds/Feed.cs @@ -1,5 +1,15 @@ -using System; +using ChrisKaczor.ApplicationUpdate; +using FeedCenter.Data; +using FeedCenter.FeedParsers; +using FeedCenter.Properties; +using FeedCenter.Xml; +using JetBrains.Annotations; +using Realms; +using Serilog; +using System; +using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; @@ -9,14 +19,6 @@ using System.Net.Http.Headers; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; -using ChrisKaczor.ApplicationUpdate; -using FeedCenter.Data; -using FeedCenter.FeedParsers; -using FeedCenter.Properties; -using FeedCenter.Xml; -using JetBrains.Annotations; -using Realms; -using Serilog; namespace FeedCenter { @@ -55,9 +57,18 @@ namespace FeedCenter #endregion - public partial class Feed : RealmObject + public partial class Feed : RealmObject, INotifyDataErrorInfo { private static HttpClient _httpClient; + + private readonly DataErrorDictionary _dataErrorDictionary; + + public Feed() + { + _dataErrorDictionary = new DataErrorDictionary(); + _dataErrorDictionary.ErrorsChanged += DataErrorDictionaryErrorsChanged; + } + public bool Authenticate { get; set; } public Guid CategoryId { get; set; } @@ -112,17 +123,68 @@ namespace FeedCenter private string MultipleOpenActionRaw { get; set; } - public string Name { get; set; } + public string Name + { + get => RawName; + set + { + RawName = value; + + ValidateString(nameof(Name), RawName); + RaisePropertyChanged(); + } + } + public string Password { get; set; } - public string Source { get; set; } + + [MapTo("Name")] + private string RawName { get; set; } = string.Empty; + + [MapTo("Source")] + private string RawSource { get; set; } = string.Empty; + + public string Source + { + get => RawSource; + set + { + RawSource = value; + + ValidateString(nameof(Source), RawSource); + RaisePropertyChanged(); + } + } + public string Title { get; set; } public string Username { get; set; } + public bool HasErrors => _dataErrorDictionary.Any(); + + public IEnumerable GetErrors(string propertyName) + { + return _dataErrorDictionary.GetErrors(propertyName); + } + + public event EventHandler ErrorsChanged; + public static Feed Create() { return new Feed { Id = Guid.NewGuid(), CategoryId = Database.Entities.DefaultCategory.Id }; } + private void DataErrorDictionaryErrorsChanged(object sender, DataErrorsChangedEventArgs e) + { + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(e.PropertyName)); + } + + private void ValidateString(string propertyName, string value) + { + _dataErrorDictionary.ClearErrors(propertyName); + + if (string.IsNullOrWhiteSpace(value)) + _dataErrorDictionary.AddError(propertyName, $"{propertyName} cannot be empty"); + } + #region Reading public FeedReadResult Read(bool forceRead = false) diff --git a/Application/Options/CategoryWindow.xaml b/Application/Options/CategoryWindow.xaml index e3a71e4..5500ef5 100644 --- a/Application/Options/CategoryWindow.xaml +++ b/Application/Options/CategoryWindow.xaml @@ -34,7 +34,7 @@ Target="{Binding ElementName=NameTextBox}" /> + Text="{Binding Path=Name, UpdateSourceTrigger=Explicit, ValidatesOnExceptions=True}">