From 2b855d1d82f3f9a2d2e19394947d6b3400caabba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 05:01:47 +0000 Subject: [PATCH 01/15] Bump Xamarin.Essentials from 1.8.0 to 1.8.1 Bumps [Xamarin.Essentials](https://github.com/xamarin/Essentials) from 1.8.0 to 1.8.1. - [Release notes](https://github.com/xamarin/Essentials/releases) - [Commits](https://github.com/xamarin/Essentials/compare/1.8.0...1.8.1) --- updated-dependencies: - dependency-name: Xamarin.Essentials dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../MvvmScarletToolkit.Xamarin.Forms.Samples.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MvvmScarletToolkit.Xamarin.Forms.Samples/MvvmScarletToolkit.Xamarin.Forms.Samples.csproj b/src/MvvmScarletToolkit.Xamarin.Forms.Samples/MvvmScarletToolkit.Xamarin.Forms.Samples.csproj index 2fb3c23a..b3a0c1f9 100644 --- a/src/MvvmScarletToolkit.Xamarin.Forms.Samples/MvvmScarletToolkit.Xamarin.Forms.Samples.csproj +++ b/src/MvvmScarletToolkit.Xamarin.Forms.Samples/MvvmScarletToolkit.Xamarin.Forms.Samples.csproj @@ -10,7 +10,7 @@ - + From 1a882e4447b83a1834b83c846ea632e03b89e030 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 04:40:52 +0000 Subject: [PATCH 02/15] Bump NUnit.Analyzers from 3.10.0 to 4.0.1 Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 3.10.0 to 4.0.1. - [Release notes](https://github.com/nunit/nunit.analyzers/releases) - [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.txt) - [Commits](https://github.com/nunit/nunit.analyzers/compare/3.10.0...4.0.1) --- updated-dependencies: - dependency-name: NUnit.Analyzers dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .../MvvmScarletToolkit.Observables.Tests.csproj | 2 +- .../MvvmScarletToolkit.Wpf.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj b/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj index e02fec0e..03956645 100644 --- a/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj +++ b/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj @@ -11,7 +11,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj b/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj index 2fef0140..bc337f4a 100644 --- a/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj +++ b/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj @@ -11,7 +11,7 @@ - + all runtime; build; native; contentfiles; analyzers From 9e622af1ca87c58d31bd175c129be314c0a4e007 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 04:28:14 +0000 Subject: [PATCH 03/15] Bump Microsoft.NET.Test.Sdk from 17.8.0 to 17.9.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.8.0 to 17.9.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.8.0...v17.9.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../MvvmScarletToolkit.Observables.Tests.csproj | 2 +- .../MvvmScarletToolkit.Wpf.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj b/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj index e02fec0e..bdea8041 100644 --- a/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj +++ b/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj @@ -9,7 +9,7 @@ - + all diff --git a/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj b/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj index 2fef0140..a73e35df 100644 --- a/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj +++ b/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj @@ -9,7 +9,7 @@ - + all From 7e8a84e9fd7348699382d173823dcda6d93ed3cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 04:29:26 +0000 Subject: [PATCH 04/15] Bump NSubstitute.Analyzers.CSharp from 1.0.16 to 1.0.17 Bumps [NSubstitute.Analyzers.CSharp](https://github.com/nsubstitute/NSubstitute.Analyzers) from 1.0.16 to 1.0.17. - [Changelog](https://github.com/nsubstitute/NSubstitute.Analyzers/blob/1.0.17/ReleaseNotes.md) - [Commits](https://github.com/nsubstitute/NSubstitute.Analyzers/compare/1.0.16...1.0.17) --- updated-dependencies: - dependency-name: NSubstitute.Analyzers.CSharp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../MvvmScarletToolkit.Observables.Tests.csproj | 2 +- .../MvvmScarletToolkit.Wpf.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj b/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj index e02fec0e..5eae2b18 100644 --- a/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj +++ b/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj b/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj index 2fef0140..8bc21a34 100644 --- a/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj +++ b/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj @@ -6,7 +6,7 @@ - + From 72ebd13699d80cd1fec0e5605cabd6b253e00aae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 04:08:45 +0000 Subject: [PATCH 05/15] Bump System.Drawing.Common from 8.0.0 to 8.0.2 Bumps [System.Drawing.Common](https://github.com/dotnet/winforms) from 8.0.0 to 8.0.2. - [Release notes](https://github.com/dotnet/winforms/releases) - [Changelog](https://github.com/dotnet/winforms/blob/main/docs/release-activity.md) - [Commits](https://github.com/dotnet/winforms/compare/v8.0.0...v8.0.2) --- updated-dependencies: - dependency-name: System.Drawing.Common dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../MvvmScarletToolkit.Incubator.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MvvmScarletToolkit.Incubator/MvvmScarletToolkit.Incubator.csproj b/src/MvvmScarletToolkit.Incubator/MvvmScarletToolkit.Incubator.csproj index a555a3e2..7f4af394 100644 --- a/src/MvvmScarletToolkit.Incubator/MvvmScarletToolkit.Incubator.csproj +++ b/src/MvvmScarletToolkit.Incubator/MvvmScarletToolkit.Incubator.csproj @@ -9,7 +9,7 @@ - + From 02c1d963ffd5674050ddc50f902d5230dfe987c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 04:09:54 +0000 Subject: [PATCH 06/15] Bump nunit from 4.0.1 to 4.1.0 Bumps [nunit](https://github.com/nunit/nunit) from 4.0.1 to 4.1.0. - [Release notes](https://github.com/nunit/nunit/releases) - [Changelog](https://github.com/nunit/nunit/blob/master/CHANGES.md) - [Commits](https://github.com/nunit/nunit/compare/v4.0.1...4.1.0) --- updated-dependencies: - dependency-name: nunit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../MvvmScarletToolkit.Observables.Tests.csproj | 2 +- .../MvvmScarletToolkit.Wpf.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj b/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj index e02fec0e..4c4b0cb6 100644 --- a/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj +++ b/src/MvvmScarletToolkit.Observables.Tests/MvvmScarletToolkit.Observables.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj b/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj index 2fef0140..9ea7fa31 100644 --- a/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj +++ b/src/MvvmScarletToolkit.Wpf.Tests/MvvmScarletToolkit.Wpf.Tests.csproj @@ -8,7 +8,7 @@ - + From 67324ff3672fdd4be5f77d33faac8baff6bd9fa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 04:11:29 +0000 Subject: [PATCH 07/15] Bump Roslynator.Analyzers from 4.7.0 to 4.11.0 Bumps [Roslynator.Analyzers](https://github.com/dotnet/roslynator) from 4.7.0 to 4.11.0. - [Release notes](https://github.com/dotnet/roslynator/releases) - [Changelog](https://github.com/dotnet/roslynator/blob/main/ChangeLog.md) - [Commits](https://github.com/dotnet/roslynator/compare/v4.7.0...v4.11.0) --- updated-dependencies: - dependency-name: Roslynator.Analyzers dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- src/Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index b773fb32..2eb3d4ee 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -12,7 +12,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From e97e6fb8066347db2c095f7fb50a9c589c7a4e95 Mon Sep 17 00:00:00 2001 From: Peter Vietense Date: Sun, 28 Apr 2024 12:11:00 +0200 Subject: [PATCH 08/15] showcase autosorting via CollectionViewSource --- global.json | 2 +- .../MainWindow.xaml | 23 +++++++++++++++++-- .../ToastServiceConfiguration.cs | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index d90e20eb..9c202eee 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "8.0.204", "rollForward": "disable" } } diff --git a/src/MvvmScarletToolkit.Wpf.Samples/MainWindow.xaml b/src/MvvmScarletToolkit.Wpf.Samples/MainWindow.xaml index fbe88371..b9d448ff 100644 --- a/src/MvvmScarletToolkit.Wpf.Samples/MainWindow.xaml +++ b/src/MvvmScarletToolkit.Wpf.Samples/MainWindow.xaml @@ -686,7 +686,7 @@ IsLiveSortingRequested="True" Source="{Binding Items}"> - + @@ -800,7 +800,6 @@ + + + + + + + + + + + + - /// how lojg to wait, before closing the notification window + /// how long to wait, before closing the notification window /// public TimeSpan WindowCloseDelay { get; set; } = TimeSpan.FromSeconds(DefaultIsVisibleForInSeconds); From 70ee143bdaa755d316d0ab0931c421187bc10274 Mon Sep 17 00:00:00 2001 From: Peter Vietense Date: Wed, 8 May 2024 22:57:41 +0200 Subject: [PATCH 09/15] added ObservableDictionary --- .../ObservableDictionary.cs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/MvvmScarletToolkit.Observables/ObservableDictionary.cs diff --git a/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs b/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs new file mode 100644 index 00000000..01e1ea64 --- /dev/null +++ b/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs @@ -0,0 +1,110 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace MvvmScarletToolkit.Observables +{ + public sealed class ObservableDictionary : ObservableCollection>, IDictionary, IReadOnlyDictionary + { + private readonly Dictionary _dictionary; + + public ObservableDictionary() + : base() + { + _dictionary = new Dictionary(); + } + + public ObservableDictionary(IDictionary collection) + : base(collection) + { + _dictionary = new Dictionary(collection); + } + + public ObservableDictionary(IEqualityComparer equalityComparer) + : base() + { + _dictionary = new Dictionary(equalityComparer); + } + + public ObservableDictionary(int capacity, IEqualityComparer equalityComparer) + : base() + { + _dictionary = new Dictionary(capacity, equalityComparer); + _dictionary = new Dictionary(capacity, equalityComparer); + } + + public ObservableDictionary(IDictionary collection, IEqualityComparer equalityComparer) + : base(collection) + { + _dictionary = new Dictionary(collection, equalityComparer); + } + + /// + public TValue this[TKey key] + { + get + { + return _dictionary[key]; + } + set + { + _dictionary[key] = value; + for (var i = 0; i < Count; i++) + { + var entry = base[i]; + if (entry.Key?.Equals(key) == true) + { + base[i] = new KeyValuePair(key, value); + break; + } + } + } + } + + /// + public ICollection Keys => _dictionary.Keys; + + /// + public ICollection Values => _dictionary.Values; + + /// + IEnumerable IReadOnlyDictionary.Keys => _dictionary.Keys; + + /// + IEnumerable IReadOnlyDictionary.Values => _dictionary.Values; + + /// + public void Add(TKey key, TValue value) + { + _dictionary.Add(key, value); + base.Add(new KeyValuePair(key, value)); + } + + /// + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + /// + public bool Remove(TKey key) + { + for (var i = 0; i < Count; i++) + { + var entry = base[i]; + if (entry.Key?.Equals(key) == true) + { + base.RemoveAt(i); + return true; + } + } + + return false; + } + + /// + public bool TryGetValue(TKey key, out TValue value) + { + return _dictionary.TryGetValue(key, out value); + } + } +} From fcfb1e5feb0e161d407680ed60b9f2a36fd2373c Mon Sep 17 00:00:00 2001 From: Peter Vietense Date: Wed, 8 May 2024 23:12:52 +0200 Subject: [PATCH 10/15] fid and add comment headers --- .../ObservableDictionary.cs | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs b/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs index 01e1ea64..768e5bda 100644 --- a/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs +++ b/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs @@ -7,31 +7,62 @@ public sealed class ObservableDictionary : ObservableCollection _dictionary; + /// + /// Initializes a new instance of the class. + /// public ObservableDictionary() : base() { _dictionary = new Dictionary(); } + /// + /// Initializes a new instance of the class + /// that contains elements copied from the specified . + /// + /// The whose elements are copied to the new . + /// dictionary is null + /// dictionary contains one or more duplicate keys public ObservableDictionary(IDictionary collection) : base(collection) { _dictionary = new Dictionary(collection); } + /// + /// Initializes a new instance of the class + /// and uses the specified . + /// + /// The implementation to use when comparing keys, or null to use the default for the type of the key. public ObservableDictionary(IEqualityComparer equalityComparer) : base() { _dictionary = new Dictionary(equalityComparer); } + /// + /// Initializes a new instance of the class + /// that is empty, has the specified initial capacity, + /// and uses the specified . + /// + /// The initial number of elements that the can contain.. + /// The implementation to use whencomparing keys, or null to use the default for the type of the key. + /// capacity is less than 0 public ObservableDictionary(int capacity, IEqualityComparer equalityComparer) : base() { _dictionary = new Dictionary(capacity, equalityComparer); - _dictionary = new Dictionary(capacity, equalityComparer); } + /// + /// Initializes a new instance of the class + /// that contains elements copied from the specified + /// and uses the specified . + /// + /// The whose elements are copied to the new . + /// The implementation to use when comparing keys, or null to use the default for the type of the key. + /// dictionary is null + /// dictionary contains one or more duplicate keys public ObservableDictionary(IDictionary collection, IEqualityComparer equalityComparer) : base(collection) { @@ -60,32 +91,32 @@ public TValue this[TKey key] } } - /// + /// public ICollection Keys => _dictionary.Keys; - /// + /// public ICollection Values => _dictionary.Values; - /// + /// IEnumerable IReadOnlyDictionary.Keys => _dictionary.Keys; - /// + /// IEnumerable IReadOnlyDictionary.Values => _dictionary.Values; - /// + /// public void Add(TKey key, TValue value) { _dictionary.Add(key, value); base.Add(new KeyValuePair(key, value)); } - /// + /// public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); } - /// + /// public bool Remove(TKey key) { for (var i = 0; i < Count; i++) @@ -101,7 +132,7 @@ public bool Remove(TKey key) return false; } - /// + /// public bool TryGetValue(TKey key, out TValue value) { return _dictionary.TryGetValue(key, out value); From c19e2e58b339084737a305187a1496e2f3961692 Mon Sep 17 00:00:00 2001 From: Peter Vietense Date: Thu, 9 May 2024 09:15:43 +0200 Subject: [PATCH 11/15] fix bugs and add tests for ObservableDictionary --- .../ObservableDictionaryTests.cs | 401 ++++++++++++++++++ .../ObservableDictionary.cs | 52 ++- 2 files changed, 449 insertions(+), 4 deletions(-) create mode 100644 src/MvvmScarletToolkit.Observables.Tests/ObservableDictionaryTests.cs diff --git a/src/MvvmScarletToolkit.Observables.Tests/ObservableDictionaryTests.cs b/src/MvvmScarletToolkit.Observables.Tests/ObservableDictionaryTests.cs new file mode 100644 index 00000000..e196d3e9 --- /dev/null +++ b/src/MvvmScarletToolkit.Observables.Tests/ObservableDictionaryTests.cs @@ -0,0 +1,401 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MvvmScarletToolkit.Observables.Tests +{ + internal sealed class ObservableDictionaryTests + { + [Test] + public void Ctor_DoesNotThrow_1() + { + new ObservableDictionary(); + } + + [Test] + public void Ctor_DoesNotThrow_2() + { + new ObservableDictionary(new Dictionary()); + } + + [Test] + public void Ctor_DoesNotThrow_3() + { + new ObservableDictionary(EqualityComparer.Default); + } + + [Test] + public void Ctor_DoesNotThrow_4() + { + new ObservableDictionary(0, EqualityComparer.Default); + } + + [Test] + public void Ctor_DoesNotThrow_5() + { + new ObservableDictionary(new Dictionary(), EqualityComparer.Default); + } + + [Test] + public void Ctor_DoesThrow_1() + { + Assert.Throws(() => new ObservableDictionary(default(Dictionary))); + } + + [Test] + public void Ctor_DoesNotThrow_6() + { + new ObservableDictionary(default(IEqualityComparer)); + } + + [Test] + public void Ctor_DoesThrow_2() + { + Assert.Throws(() => new ObservableDictionary(-1, default(IEqualityComparer))); + } + + [Test] + public void Ctor_DoesThrow_3() + { + Assert.Throws(() => new ObservableDictionary(default(Dictionary), default(IEqualityComparer))); + } + + [Test] + public void Ctor_Does_Initialize_With_Expected_Data_1() + { + var sut = new ObservableDictionary() + { + [1] = new object(), + [2] = new object(), + }; + + Assert.Multiple(() => + { + Assert.That(sut, Has.Count.EqualTo(2)); + Assert.That(sut.Keys, Has.Count.EqualTo(2)); + Assert.That(sut.Values, Has.Count.EqualTo(2)); + }); + + IReadOnlyDictionary castedSut = sut; + + Assert.Multiple(() => + { + Assert.That(castedSut.Keys.Count(), Is.EqualTo(2)); + Assert.That(castedSut.Values.Count(), Is.EqualTo(2)); + }); + } + + [Test] + public void Ctor_Does_Initialize_With_Expected_Data_2() + { + var sut = new ObservableDictionary(new Dictionary() + { + [1] = new object(), + [2] = new object(), + }); + + Assert.Multiple(() => + { + Assert.That(sut, Has.Count.EqualTo(2)); + Assert.That(sut.Keys, Has.Count.EqualTo(2)); + Assert.That(sut.Values, Has.Count.EqualTo(2)); + }); + + IReadOnlyDictionary castedSut = sut; + + Assert.Multiple(() => + { + Assert.That(castedSut.Keys.Count(), Is.EqualTo(2)); + Assert.That(castedSut.Values.Count(), Is.EqualTo(2)); + }); + } + + [Test] + public void Indexer_DoesThrow_Get() + { + var sut = new ObservableDictionary() + { + [1] = new object(), + }; + + Assert.Throws(() => _ = sut[2]); + } + + [Test] + public void Indexer_DoesNotThrow_Get() + { + var data = new object(); + var sut = new ObservableDictionary() + { + [1] = data, + }; + + Assert.That(data, Is.EqualTo(sut[1])); + } + + [Test] + public void Indexer_DoesNotThrow_Set_1() + { + var sut = new ObservableDictionary() + { + [1] = new object(), + }; + + sut[2] = new object(); + } + + [Test] + public void Indexer_DoesNotThrow_Set_2() + { + var sut = new ObservableDictionary() + { + [1] = new object(), + [2] = new object(), + }; + + sut[1] = new object(); + } + + [Test] + public void Add_DoesNotThrow_1() + { + var sut = new ObservableDictionary() + { + [1] = new object(), + [2] = new object(), + }; + + sut.Add(3, new object()); + } + + [Test] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0028:Simplify collection initialization", Justification = "Be explicit about calling Add")] + public void Add_DoesNotThrow_2() + { + var sut = new ObservableDictionary(); + + sut.Add(1, new object()); + } + + [Test] + public void Add_DoesThrow_1() + { + var sut = new ObservableDictionary() + { + [1] = new object(), + }; + + Assert.Throws(() => sut.Add(1, new object())); + } + + [Test] + public void Add_DoesThrow_2() + { + var sut = new ObservableDictionary() + { + [new object()] = new object(), + }; + + Assert.Throws(() => sut.Add(null, new object())); + } + + [Test] + public void ContainsKey_DoesNotThrow_1() + { + var sut = new ObservableDictionary() + { + [1] = new object(), + [2] = new object(), + }; + + Assert.That(sut.ContainsKey(3), Is.EqualTo(false)); + } + + [Test] + public void ContainsKey_DoesNotThrow_2() + { + var sut = new ObservableDictionary() + { + [1] = new object(), + [2] = new object(), + }; + + Assert.That(sut.ContainsKey(2), Is.EqualTo(true)); + } + + [Test] + public void Clear_Does_Empty_Collection() + { + var sut = new ObservableDictionary(new Dictionary() + { + [1] = new object(), + [2] = new object(), + }); + + sut.Clear(); + + Assert.Multiple(() => + { + Assert.That(sut, Is.Empty); + Assert.That(sut.Keys, Is.Empty); + Assert.That(sut.Values, Is.Empty); + }); + + IReadOnlyDictionary castedSut = sut; + + Assert.Multiple(() => + { + Assert.That(castedSut.Keys.Count(), Is.EqualTo(0)); + Assert.That(castedSut.Values.Count(), Is.EqualTo(0)); + }); + } + + [Test] + public void Clear_Does_Work_On_Empty_Collection() + { + var sut = new ObservableDictionary(); + + sut.Clear(); + + Assert.Multiple(() => + { + Assert.That(sut, Is.Empty); + Assert.That(sut.Keys, Is.Empty); + Assert.That(sut.Values, Is.Empty); + }); + + IReadOnlyDictionary castedSut = sut; + + Assert.Multiple(() => + { + Assert.That(castedSut.Keys.Count(), Is.EqualTo(0)); + Assert.That(castedSut.Values.Count(), Is.EqualTo(0)); + }); + } + + [Test] + public void Remove_Does_Work() + { + var sut = new ObservableDictionary(new Dictionary() + { + [1] = new object(), + [2] = new object(), + }); + + Assert.Multiple(() => + { + Assert.That(sut.Remove(2), Is.EqualTo(true)); + Assert.That(sut, Has.Count.EqualTo(1)); + Assert.That(sut.Keys, Has.Count.EqualTo(1)); + Assert.That(sut.Values, Has.Count.EqualTo(1)); + }); + + IReadOnlyDictionary castedSut = sut; + + Assert.Multiple(() => + { + Assert.That(castedSut.Keys.Count(), Is.EqualTo(1)); + Assert.That(castedSut.Values.Count(), Is.EqualTo(1)); + + Assert.That(sut.Remove(2), Is.EqualTo(false)); + }); + } + + [Test] + public void Remove_Does_Not_Throw_1() + { + var sut = new ObservableDictionary(new Dictionary() + { + [1] = new object(), + }); + + Assert.Multiple(() => + { + Assert.That(sut.Remove(2), Is.EqualTo(false)); + Assert.That(sut, Has.Count.EqualTo(1)); + Assert.That(sut.Keys, Has.Count.EqualTo(1)); + Assert.That(sut.Values, Has.Count.EqualTo(1)); + }); + + IReadOnlyDictionary castedSut = sut; + + Assert.Multiple(() => + { + Assert.That(castedSut.Keys.Count(), Is.EqualTo(1)); + Assert.That(castedSut.Values.Count(), Is.EqualTo(1)); + + Assert.That(sut.Remove(2), Is.EqualTo(false)); + }); + } + + [Test] + public void Remove_Does_Not_Throw_2() + { + var sut = new ObservableDictionary(new Dictionary() + { + [1] = new object(), + }); + + Assert.Multiple(() => + { + Assert.That(sut.Remove(1), Is.EqualTo(true)); + Assert.That(sut, Is.Empty); + Assert.That(sut.Keys, Is.Empty); + Assert.That(sut.Values, Is.Empty); + }); + + IReadOnlyDictionary castedSut = sut; + + Assert.Multiple(() => + { + Assert.That(castedSut.Keys.Count(), Is.EqualTo(0)); + Assert.That(castedSut.Values.Count(), Is.EqualTo(0)); + + Assert.That(sut.Remove(1), Is.EqualTo(false)); + }); + } + + [Test] + public void Remove_Does_Not_Throw_3() + { + var sut = new ObservableDictionary(); + + Assert.That(sut.Remove(2), Is.EqualTo(false)); + } + + [Test] + public void TryGetValue_Does_Work_1() + { + var expectedResult = new object(); + var sut = new ObservableDictionary(new Dictionary() + { + [1] = new object(), + [2] = expectedResult, + }); + + Assert.Multiple(() => + { + Assert.That(sut.TryGetValue(2, out var result), Is.EqualTo(true)); + Assert.That(result, Is.EqualTo(expectedResult)); + }); + } + + [Test] + public void TryGetValue_Does_Work_2() + { + var expectedResult = new object(); + var sut = new ObservableDictionary(new Dictionary() + { + [1] = new object(), + [2] = expectedResult, + }); + + Assert.Multiple(() => + { + Assert.That(sut.TryGetValue(3, out var result), Is.EqualTo(false)); + Assert.That(result, Is.EqualTo(null)); + }); + } + } +} diff --git a/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs b/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs index 768e5bda..00e5ca9b 100644 --- a/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs +++ b/src/MvvmScarletToolkit.Observables/ObservableDictionary.cs @@ -34,7 +34,7 @@ public ObservableDictionary(IDictionary collection) /// and uses the specified . /// /// The implementation to use when comparing keys, or null to use the default for the type of the key. - public ObservableDictionary(IEqualityComparer equalityComparer) + public ObservableDictionary(IEqualityComparer? equalityComparer) : base() { _dictionary = new Dictionary(equalityComparer); @@ -48,7 +48,7 @@ public ObservableDictionary(IEqualityComparer equalityComparer) /// The initial number of elements that the can contain.. /// The implementation to use whencomparing keys, or null to use the default for the type of the key. /// capacity is less than 0 - public ObservableDictionary(int capacity, IEqualityComparer equalityComparer) + public ObservableDictionary(int capacity, IEqualityComparer? equalityComparer) : base() { _dictionary = new Dictionary(capacity, equalityComparer); @@ -63,7 +63,7 @@ public ObservableDictionary(int capacity, IEqualityComparer equalityCompar /// The implementation to use when comparing keys, or null to use the default for the type of the key. /// dictionary is null /// dictionary contains one or more duplicate keys - public ObservableDictionary(IDictionary collection, IEqualityComparer equalityComparer) + public ObservableDictionary(IDictionary collection, IEqualityComparer? equalityComparer) : base(collection) { _dictionary = new Dictionary(collection, equalityComparer); @@ -79,15 +79,22 @@ public TValue this[TKey key] set { _dictionary[key] = value; + var requiresAdd = true; for (var i = 0; i < Count; i++) { var entry = base[i]; if (entry.Key?.Equals(key) == true) { base[i] = new KeyValuePair(key, value); + requiresAdd = false; break; } } + + if (requiresAdd) + { + base.Add(new KeyValuePair(key, value)); + } } } @@ -119,12 +126,17 @@ public bool ContainsKey(TKey key) /// public bool Remove(TKey key) { + if (!_dictionary.ContainsKey(key)) + { + return false; + } + for (var i = 0; i < Count; i++) { var entry = base[i]; if (entry.Key?.Equals(key) == true) { - base.RemoveAt(i); + base.Remove(entry); return true; } } @@ -137,5 +149,37 @@ public bool TryGetValue(TKey key, out TValue value) { return _dictionary.TryGetValue(key, out value); } + + /// + protected override void ClearItems() + { + _dictionary.Clear(); + base.ClearItems(); + } + + /// + protected override void InsertItem(int index, KeyValuePair item) + { + _dictionary[item.Key] = item.Value; + base.InsertItem(index, item); + } + + /// + protected override void RemoveItem(int index) + { + var entry = base[index]; + + if (_dictionary.Remove(entry.Key)) + { + base.RemoveItem(index); + } + } + + /// + protected override void SetItem(int index, KeyValuePair item) + { + _dictionary[item.Key] = item.Value; + base.SetItem(index, item); + } } } From 5e270bb9a3fe32290d759945b3e7dbb67d65e532 Mon Sep 17 00:00:00 2001 From: Peter Vietense Date: Thu, 9 May 2024 09:32:33 +0200 Subject: [PATCH 12/15] added binding sample for ObservableDictionary --- .../Features/NavigationViewModel.cs | 1 + .../Features/ObservableDictionaryViewModel.cs | 55 +++++++++++++++++++ .../MainWindow.xaml | 23 ++++++++ 3 files changed, 79 insertions(+) create mode 100644 src/MvvmScarletToolkit.Wpf.Samples/Features/ObservableDictionaryViewModel.cs diff --git a/src/MvvmScarletToolkit.Wpf.Samples/Features/NavigationViewModel.cs b/src/MvvmScarletToolkit.Wpf.Samples/Features/NavigationViewModel.cs index 3aadbedd..51e59fa9 100644 --- a/src/MvvmScarletToolkit.Wpf.Samples/Features/NavigationViewModel.cs +++ b/src/MvvmScarletToolkit.Wpf.Samples/Features/NavigationViewModel.cs @@ -36,6 +36,7 @@ public NavigationViewModel(SynchronizationContext synchronizationContext, IScarl Add("Binding Enum values", new EnumViewModel()); Add("MVVM Toast-Notification", new ToastsViewModel(commandBuilder)); Add("Input Prevention", new FormViewModel()); + Add("ObservableDictionary", new ObservableDictionaryViewModel()); } } } diff --git a/src/MvvmScarletToolkit.Wpf.Samples/Features/ObservableDictionaryViewModel.cs b/src/MvvmScarletToolkit.Wpf.Samples/Features/ObservableDictionaryViewModel.cs new file mode 100644 index 00000000..5165fb76 --- /dev/null +++ b/src/MvvmScarletToolkit.Wpf.Samples/Features/ObservableDictionaryViewModel.cs @@ -0,0 +1,55 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using MvvmScarletToolkit.Observables; +using System; +using System.Linq; + +namespace MvvmScarletToolkit.Wpf.Samples +{ + internal sealed partial class ObservableDictionaryViewModel : ObservableObject + { + public ObservableDictionary Items { get; } + + public ObservableDictionaryViewModel() + { + Items = new ObservableDictionary() + { + [0] = Guid.NewGuid(), + [1] = Guid.NewGuid(), + [2] = Guid.NewGuid(), + [3] = Guid.NewGuid(), + [4] = Guid.NewGuid(), + }; + } + + [RelayCommand] + private void Add() + { + if (Items.Count > 0) + { + Items.Add(Items.MaxBy(p => p.Key).Key + 1, Guid.NewGuid()); + } + else + { + Items.Add(0, Guid.NewGuid()); + } + } + + [RelayCommand(CanExecute = nameof(CanRemove))] + private void Remove() + { + Items.Remove(Items.First()); + } + + private bool CanRemove() + { + return Items.Count > 0; + } + + [RelayCommand] + private void Clear() + { + Items.Clear(); + } + } +} diff --git a/src/MvvmScarletToolkit.Wpf.Samples/MainWindow.xaml b/src/MvvmScarletToolkit.Wpf.Samples/MainWindow.xaml index b9d448ff..6edb5449 100644 --- a/src/MvvmScarletToolkit.Wpf.Samples/MainWindow.xaml +++ b/src/MvvmScarletToolkit.Wpf.Samples/MainWindow.xaml @@ -1558,6 +1558,29 @@ + + + + + +