mirror of
https://github.com/mRemoteNG/mRemoteNG.git
synced 2026-02-17 14:07:46 +08:00
created a class and event type for handling collections that need to raise events for collection changes and child updates
this is for cases where you would like to have INotifyCollectionChanged and INotifyPropertyChanged implemented, but dont need the level of detail that those types provide.
This commit is contained in:
114
mRemoteNGTests/Tools/FullyObservableCollectionTests.cs
Normal file
114
mRemoteNGTests/Tools/FullyObservableCollectionTests.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using mRemoteNG.Tools.CustomCollections;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace mRemoteNGTests.Tools
|
||||
{
|
||||
public class FullyObservableCollectionTests
|
||||
{
|
||||
[Test]
|
||||
public void CollectionBeginsEmpty()
|
||||
{
|
||||
var list = new FullyObservableCollection<INotifyPropertyChanged>();
|
||||
Assert.That(list, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanCreateWithExistingList()
|
||||
{
|
||||
var existingList = new List<INotifyPropertyChanged>
|
||||
{
|
||||
Substitute.For<INotifyPropertyChanged>(),
|
||||
Substitute.For<INotifyPropertyChanged>(),
|
||||
Substitute.For<INotifyPropertyChanged>()
|
||||
};
|
||||
var list = new FullyObservableCollection<INotifyPropertyChanged>(existingList);
|
||||
Assert.That(list, Has.Count.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ItemAdded()
|
||||
{
|
||||
var list = new FullyObservableCollection<INotifyPropertyChanged>();
|
||||
var item = Substitute.For<INotifyPropertyChanged>();
|
||||
list.Add(item);
|
||||
Assert.That(list, Has.Member(item));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ItemInserted()
|
||||
{
|
||||
var list = new FullyObservableCollection<INotifyPropertyChanged>();
|
||||
var item = Substitute.For<INotifyPropertyChanged>();
|
||||
list.Insert(0, item);
|
||||
Assert.That(list[0], Is.EqualTo(item));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ItemRemoved()
|
||||
{
|
||||
var item = Substitute.For<INotifyPropertyChanged>();
|
||||
var list = new FullyObservableCollection<INotifyPropertyChanged>
|
||||
{
|
||||
item
|
||||
};
|
||||
list.Remove(item);
|
||||
Assert.That(list, Does.Not.Contains(item));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ItemRemovedAtIndex()
|
||||
{
|
||||
var item = Substitute.For<INotifyPropertyChanged>();
|
||||
var list = new FullyObservableCollection<INotifyPropertyChanged>
|
||||
{
|
||||
item
|
||||
};
|
||||
list.RemoveAt(0);
|
||||
Assert.That(list, Does.Not.Contains(item));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearRemovesAllItems()
|
||||
{
|
||||
var list = new FullyObservableCollection<INotifyPropertyChanged>
|
||||
{
|
||||
Substitute.For<INotifyPropertyChanged>(),
|
||||
Substitute.For<INotifyPropertyChanged>(),
|
||||
Substitute.For<INotifyPropertyChanged>()
|
||||
};
|
||||
list.Clear();
|
||||
Assert.That(list, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChildItemEventsTriggerListEvents()
|
||||
{
|
||||
var wasCalled = false;
|
||||
var item = Substitute.For<INotifyPropertyChanged>();
|
||||
var list = new FullyObservableCollection<INotifyPropertyChanged> {item};
|
||||
list.CollectionUpdated += (sender, args) => wasCalled = true;
|
||||
RaiseEvent(item);
|
||||
Assert.That(wasCalled, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ListUnsubscribesFromRemovedItems()
|
||||
{
|
||||
var wasCalled = false;
|
||||
var item = Substitute.For<INotifyPropertyChanged>();
|
||||
var list = new FullyObservableCollection<INotifyPropertyChanged> { item };
|
||||
list.Remove(item);
|
||||
list.CollectionUpdated += (sender, args) => wasCalled = true;
|
||||
RaiseEvent(item);
|
||||
Assert.That(wasCalled, Is.False);
|
||||
}
|
||||
|
||||
private void RaiseEvent(INotifyPropertyChanged item)
|
||||
{
|
||||
item.PropertyChanged += Raise.Event<PropertyChangedEventHandler>(item, new PropertyChangedEventArgs("test"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,7 @@
|
||||
<Compile Include="Security\SecureStringExtensionsTests.cs" />
|
||||
<Compile Include="Security\XmlCryptoProviderBuilderTests.cs" />
|
||||
<Compile Include="Tools\ExternalToolsArgumentParserTests.cs" />
|
||||
<Compile Include="Tools\FullyObservableCollectionTests.cs" />
|
||||
<Compile Include="Tree\ClickHandlers\TreeNodeCompositeClickHandlerTests.cs" />
|
||||
<Compile Include="Tree\ConnectionTreeDragAndDropHandlerTests.cs" />
|
||||
<Compile Include="Tree\ConnectionTreeModelTests.cs" />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace mRemoteNG.Credential
|
||||
{
|
||||
public class CredentialChangedEventArgs
|
||||
public class CredentialChangedEventArgs : EventArgs
|
||||
{
|
||||
public ICredentialRecord CredentialRecord { get; }
|
||||
public ICredentialRepository Repository { get; }
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
|
||||
namespace mRemoteNG.Credential
|
||||
{
|
||||
public class CredentialList : CollectionBase
|
||||
{
|
||||
#region Public Properties
|
||||
public CredentialInfo this[object Index]
|
||||
{
|
||||
get
|
||||
{
|
||||
var info = Index as CredentialInfo;
|
||||
if (info != null)
|
||||
{
|
||||
return info;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((CredentialInfo) (List[Convert.ToInt32(Index)]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new int Count => List.Count;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
public CredentialInfo Add(CredentialInfo credentialInfo)
|
||||
{
|
||||
List.Add(credentialInfo);
|
||||
return credentialInfo;
|
||||
}
|
||||
|
||||
public void AddRange(CredentialInfo[] cInfo)
|
||||
{
|
||||
foreach (CredentialInfo cI in cInfo)
|
||||
{
|
||||
List.Add(cI);
|
||||
}
|
||||
}
|
||||
|
||||
public CredentialList Copy()
|
||||
{
|
||||
try
|
||||
{
|
||||
return (CredentialList)this.MemberwiseClone();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public new void Clear()
|
||||
{
|
||||
List.Clear();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace mRemoteNG.Credential
|
||||
{
|
||||
public class CredentialRepositoryChangedArgs
|
||||
public class CredentialRepositoryChangedArgs : EventArgs
|
||||
{
|
||||
public ICredentialRepository Repository { get; }
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mRemoteNG.Tools.CustomCollections
|
||||
{
|
||||
public class CollectionUpdatedEventArgs<T> : EventArgs
|
||||
{
|
||||
public IEnumerable<T> ChangedItems { get; }
|
||||
public ActionType Action { get; }
|
||||
|
||||
public CollectionUpdatedEventArgs(ActionType action, IEnumerable<T> changedItems)
|
||||
{
|
||||
if (changedItems == null)
|
||||
throw new ArgumentNullException(nameof(changedItems));
|
||||
|
||||
Action = action;
|
||||
ChangedItems = changedItems;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ActionType
|
||||
{
|
||||
Added,
|
||||
Removed,
|
||||
Updated
|
||||
}
|
||||
}
|
||||
104
mRemoteV1/Tools/CustomCollections/FullyObservableCollection.cs
Normal file
104
mRemoteV1/Tools/CustomCollections/FullyObservableCollection.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace mRemoteNG.Tools.CustomCollections
|
||||
{
|
||||
public class FullyObservableCollection<T> : IFullyNotifiableList<T>
|
||||
where T : INotifyPropertyChanged
|
||||
{
|
||||
private readonly IList<T> _list = new List<T>();
|
||||
|
||||
public int Count => _list.Count;
|
||||
public bool IsReadOnly => _list.IsReadOnly;
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get { return _list[index]; }
|
||||
set { _list[index] = value; }
|
||||
}
|
||||
|
||||
public FullyObservableCollection()
|
||||
{
|
||||
}
|
||||
|
||||
public FullyObservableCollection(IEnumerable<T> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
Add(item);
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
_list.Add(item);
|
||||
SubscribeToChildEvents(item);
|
||||
RaiseCredentialChangedEvent(ActionType.Added, new[] {item});
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
_list.Insert(index, item);
|
||||
SubscribeToChildEvents(item);
|
||||
RaiseCredentialChangedEvent(ActionType.Added, new[] { item });
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
var worked = _list.Remove(item);
|
||||
if (!worked) return worked;
|
||||
RaiseCredentialChangedEvent(ActionType.Removed, new[] {item});
|
||||
UnsubscribeFromChildEvents(item);
|
||||
return worked;
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
var item = _list[index];
|
||||
_list.RemoveAt(index);
|
||||
UnsubscribeFromChildEvents(item);
|
||||
RaiseCredentialChangedEvent(ActionType.Removed, new[] { item });
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
var oldItems = _list.ToArray();
|
||||
_list.Clear();
|
||||
foreach (var item in oldItems)
|
||||
UnsubscribeFromChildEvents(item);
|
||||
RaiseCredentialChangedEvent(ActionType.Removed, oldItems);
|
||||
}
|
||||
|
||||
private void SubscribeToChildEvents(INotifyPropertyChanged item)
|
||||
{
|
||||
item.PropertyChanged += ItemOnPropertyChanged;
|
||||
}
|
||||
|
||||
private void UnsubscribeFromChildEvents(INotifyPropertyChanged item)
|
||||
{
|
||||
item.PropertyChanged -= ItemOnPropertyChanged;
|
||||
}
|
||||
|
||||
private void ItemOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
|
||||
{
|
||||
if (sender is T)
|
||||
RaiseCredentialChangedEvent(ActionType.Updated, new []{ (T)sender });
|
||||
}
|
||||
|
||||
public event EventHandler<CollectionUpdatedEventArgs<T>> CollectionUpdated;
|
||||
|
||||
private void RaiseCredentialChangedEvent(ActionType action, IEnumerable<T> changedItems)
|
||||
{
|
||||
CollectionUpdated?.Invoke(this, new CollectionUpdatedEventArgs<T>(action, changedItems));
|
||||
}
|
||||
|
||||
#region Forwarded method calls
|
||||
public int IndexOf(T item) => _list.IndexOf(item);
|
||||
public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
|
||||
public bool Contains(T item) => _list.Contains(item);
|
||||
public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
10
mRemoteV1/Tools/CustomCollections/IFullyNotifiableList.cs
Normal file
10
mRemoteV1/Tools/CustomCollections/IFullyNotifiableList.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace mRemoteNG.Tools.CustomCollections
|
||||
{
|
||||
public interface IFullyNotifiableList<T> : IList<T>, INotifyCollectionUpdated<T>
|
||||
where T : INotifyPropertyChanged
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace mRemoteNG.Tools.CustomCollections
|
||||
{
|
||||
public interface INotifyCollectionUpdated<T>
|
||||
{
|
||||
event EventHandler<CollectionUpdatedEventArgs<T>> CollectionUpdated;
|
||||
}
|
||||
}
|
||||
@@ -212,6 +212,8 @@
|
||||
<Compile Include="Credential\CredentialChangedEventArgs.cs" />
|
||||
<Compile Include="Credential\CredentialDeletionMsgBoxConfirmer.cs" />
|
||||
<Compile Include="Credential\CredentialDomainUserComparer.cs" />
|
||||
<Compile Include="Tools\CustomCollections\IFullyNotifiableList.cs" />
|
||||
<Compile Include="Tools\CustomCollections\FullyObservableCollection.cs" />
|
||||
<Compile Include="Credential\CredentialRepositoryChangedArgs.cs" />
|
||||
<Compile Include="Credential\CredentialRepositoryList.cs" />
|
||||
<Compile Include="Credential\CredentialRecord.cs" />
|
||||
@@ -261,10 +263,12 @@
|
||||
<Compile Include="Security\CryptographyProviderFactory.cs" />
|
||||
<Compile Include="Security\XmlCryptoProviderBuilder.cs" />
|
||||
<Compile Include="Tools\Cmdline\StartupArgumentsInterpreter.cs" />
|
||||
<Compile Include="Tools\CollectionUpdatedEventArgs.cs" />
|
||||
<Compile Include="Tools\ExternalToolArgumentParser.cs" />
|
||||
<Compile Include="Tools\Cmdline\CmdArgumentsInterpreter.cs" />
|
||||
<Compile Include="Tools\ConnectionsTreeToMenuItemsConverter.cs" />
|
||||
<Compile Include="Tools\ExternalToolsTypeConverter.cs" />
|
||||
<Compile Include="Tools\CustomCollections\INotifyCollectionUpdated.cs" />
|
||||
<Compile Include="Tools\MouseClickSimulator.cs" />
|
||||
<Compile Include="Tools\NotificationAreaIcon.cs" />
|
||||
<Compile Include="Tools\ScanHost.cs" />
|
||||
|
||||
Reference in New Issue
Block a user