Added some helper classes for INotifyPropertyChanged; added unit tests for the ObservableSet class; documented the second Count property exposed by the multi dictionary
git-svn-id: file:///srv/devel/repo-conversion/nusu@262 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
df169e376a
commit
1a05bf9d63
|
@ -57,9 +57,6 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Source\AffineThreadPool.Test.cs">
|
|
||||||
<DependentUpon>AffineThreadPool.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Cloning\CloneFactoryTest.cs" />
|
<Compile Include="Source\Cloning\CloneFactoryTest.cs" />
|
||||||
<Compile Include="Source\Cloning\ClonerHelpers.cs" />
|
<Compile Include="Source\Cloning\ClonerHelpers.cs" />
|
||||||
<Compile Include="Source\Cloning\ClonerHelpers.Test.cs">
|
<Compile Include="Source\Cloning\ClonerHelpers.Test.cs">
|
||||||
|
@ -186,27 +183,6 @@
|
||||||
<Compile Include="Source\Collections\ReverseComparer.Test.cs">
|
<Compile Include="Source\Collections\ReverseComparer.Test.cs">
|
||||||
<DependentUpon>ReverseComparer.cs</DependentUpon>
|
<DependentUpon>ReverseComparer.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\AffineThreadPool.cs" />
|
|
||||||
<Compile Include="Source\EnumHelper.cs" />
|
|
||||||
<Compile Include="Source\EnumHelper.Test.cs">
|
|
||||||
<DependentUpon>EnumHelper.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Plugins\PrototypeFactory.cs" />
|
|
||||||
<Compile Include="Source\Plugins\PrototypeFactory.Test.cs">
|
|
||||||
<DependentUpon>PrototypeFactory.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Semaphore.cs" />
|
|
||||||
<Compile Include="Source\Semaphore.Test.cs">
|
|
||||||
<DependentUpon>Semaphore.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\IO\PartialStream.cs" />
|
|
||||||
<Compile Include="Source\IO\PartialStream.Test.cs">
|
|
||||||
<DependentUpon>PartialStream.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\IO\RingMemoryStream.cs" />
|
|
||||||
<Compile Include="Source\IO\RingMemoryStream.Test.cs">
|
|
||||||
<DependentUpon>RingMemoryStream.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Collections\TransformingReadOnlyCollection.cs" />
|
<Compile Include="Source\Collections\TransformingReadOnlyCollection.cs" />
|
||||||
<Compile Include="Source\Collections\TransformingReadOnlyCollection.Interfaces.cs">
|
<Compile Include="Source\Collections\TransformingReadOnlyCollection.Interfaces.cs">
|
||||||
<DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon>
|
<DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon>
|
||||||
|
@ -221,13 +197,17 @@
|
||||||
<Compile Include="Source\Collections\WeakCollection.Test.cs">
|
<Compile Include="Source\Collections\WeakCollection.Test.cs">
|
||||||
<DependentUpon>WeakCollection.cs</DependentUpon>
|
<DependentUpon>WeakCollection.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\FloatHelper.cs" />
|
<Compile Include="Source\IO\PartialStream.cs" />
|
||||||
<Compile Include="Source\FloatHelper.Test.cs">
|
<Compile Include="Source\IO\PartialStream.Test.cs">
|
||||||
<DependentUpon>FloatHelper.cs</DependentUpon>
|
<DependentUpon>PartialStream.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\IntegerHelper.cs" />
|
<Compile Include="Source\IO\RingMemoryStream.cs" />
|
||||||
<Compile Include="Source\IntegerHelper.Test.cs">
|
<Compile Include="Source\IO\RingMemoryStream.Test.cs">
|
||||||
<DependentUpon>IntegerHelper.cs</DependentUpon>
|
<DependentUpon>RingMemoryStream.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\IO\ChainStream.cs" />
|
||||||
|
<Compile Include="Source\IO\ChainStream.Test.cs">
|
||||||
|
<DependentUpon>ChainStream.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\Licensing\LicenseKey.cs" />
|
<Compile Include="Source\Licensing\LicenseKey.cs" />
|
||||||
<Compile Include="Source\Licensing\LicenseKey.Test.cs">
|
<Compile Include="Source\Licensing\LicenseKey.Test.cs">
|
||||||
|
@ -246,10 +226,6 @@
|
||||||
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
||||||
<DependentUpon>CommandLine.cs</DependentUpon>
|
<DependentUpon>CommandLine.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\PathHelper.cs" />
|
|
||||||
<Compile Include="Source\PathHelper.Test.cs">
|
|
||||||
<DependentUpon>PathHelper.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.cs" />
|
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.cs" />
|
||||||
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.Test.cs">
|
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.Test.cs">
|
||||||
<DependentUpon>AssemblyLoadEventArgs.cs</DependentUpon>
|
<DependentUpon>AssemblyLoadEventArgs.cs</DependentUpon>
|
||||||
|
@ -261,6 +237,10 @@
|
||||||
<Compile Include="Source\Plugins\FactoryEmployer.Test.cs">
|
<Compile Include="Source\Plugins\FactoryEmployer.Test.cs">
|
||||||
<DependentUpon>FactoryEmployer.cs</DependentUpon>
|
<DependentUpon>FactoryEmployer.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Source\Plugins\PrototypeFactory.cs" />
|
||||||
|
<Compile Include="Source\Plugins\PrototypeFactory.Test.cs">
|
||||||
|
<DependentUpon>PrototypeFactory.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Plugins\IAssemblyLoader.cs" />
|
<Compile Include="Source\Plugins\IAssemblyLoader.cs" />
|
||||||
<Compile Include="Source\Plugins\InstanceEmployer.Test.cs">
|
<Compile Include="Source\Plugins\InstanceEmployer.Test.cs">
|
||||||
<DependentUpon>InstanceEmployer.cs</DependentUpon>
|
<DependentUpon>InstanceEmployer.cs</DependentUpon>
|
||||||
|
@ -284,14 +264,46 @@
|
||||||
<Compile Include="Source\Plugins\PluginRepository.Test.cs">
|
<Compile Include="Source\Plugins\PluginRepository.Test.cs">
|
||||||
<DependentUpon>PluginRepository.cs</DependentUpon>
|
<DependentUpon>PluginRepository.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Source\PropertyChangedEventArgsHelper.cs" />
|
||||||
|
<Compile Include="Source\PropertyChangedEventArgsHelper.Test.cs">
|
||||||
|
<DependentUpon>PropertyChangedEventArgsHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\AffineThreadPool.cs" />
|
||||||
|
<Compile Include="Source\AffineThreadPool.Test.cs">
|
||||||
|
<DependentUpon>AffineThreadPool.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\EnumHelper.cs" />
|
||||||
|
<Compile Include="Source\EnumHelper.Test.cs">
|
||||||
|
<DependentUpon>EnumHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\Observable.cs" />
|
||||||
|
<Compile Include="Source\Observable.Test.cs">
|
||||||
|
<DependentUpon>Observable.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\ObservableHelper.cs" />
|
||||||
|
<Compile Include="Source\ObservableHelper.Test.cs">
|
||||||
|
<DependentUpon>ObservableHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\Semaphore.cs" />
|
||||||
|
<Compile Include="Source\Semaphore.Test.cs">
|
||||||
|
<DependentUpon>Semaphore.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\FloatHelper.cs" />
|
||||||
|
<Compile Include="Source\FloatHelper.Test.cs">
|
||||||
|
<DependentUpon>FloatHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\IntegerHelper.cs" />
|
||||||
|
<Compile Include="Source\IntegerHelper.Test.cs">
|
||||||
|
<DependentUpon>IntegerHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\PathHelper.cs" />
|
||||||
|
<Compile Include="Source\PathHelper.Test.cs">
|
||||||
|
<DependentUpon>PathHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Shared.cs" />
|
<Compile Include="Source\Shared.cs" />
|
||||||
<Compile Include="Source\Shared.Test.cs">
|
<Compile Include="Source\Shared.Test.cs">
|
||||||
<DependentUpon>Shared.cs</DependentUpon>
|
<DependentUpon>Shared.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\IO\ChainStream.cs" />
|
|
||||||
<Compile Include="Source\IO\ChainStream.Test.cs">
|
|
||||||
<DependentUpon>ChainStream.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\StringBuilderHelper.cs" />
|
<Compile Include="Source\StringBuilderHelper.cs" />
|
||||||
<Compile Include="Source\StringBuilderHelper.Test.cs">
|
<Compile Include="Source\StringBuilderHelper.Test.cs">
|
||||||
<DependentUpon>StringBuilderHelper.cs</DependentUpon>
|
<DependentUpon>StringBuilderHelper.cs</DependentUpon>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>bin\xna-4.0-phone7\Debug\</OutputPath>
|
<OutputPath>bin\xna-4.0-phone7\Debug\</OutputPath>
|
||||||
<DefineConstants>TRACE;DEBUG;WINDOWS_PHONE;NO_CLONING;NO_SERIALIZATION;NO_XMLSCHEMA;NO_SYSTEMEVENTS;NO_EXITCONTEXT;NO_SPECIALIZED_COLLECTIONS</DefineConstants>
|
<DefineConstants>TRACE;DEBUG;WINDOWS_PHONE;NO_CLONING;NO_SERIALIZATION;NO_XMLSCHEMA;NO_SYSTEMEVENTS;NO_EXITCONTEXT;NO_SPECIALIZED_COLLECTIONS;NO_CONCURRENT_COLLECTIONS</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<NoStdLib>true</NoStdLib>
|
<NoStdLib>true</NoStdLib>
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<OutputPath>bin\xna-4.0-phone7\Release\</OutputPath>
|
<OutputPath>bin\xna-4.0-phone7\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE;WINDOWS_PHONE;NO_CLONING;NO_SERIALIZATION;NO_XMLSCHEMA;NO_SYSTEMEVENTS;NO_EXITCONTEXT;NO_SPECIALIZED_COLLECTIONS</DefineConstants>
|
<DefineConstants>TRACE;WINDOWS_PHONE;NO_CLONING;NO_SERIALIZATION;NO_XMLSCHEMA;NO_SYSTEMEVENTS;NO_EXITCONTEXT;NO_SPECIALIZED_COLLECTIONS;NO_CONCURRENT_COLLECTIONS</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<NoStdLib>true</NoStdLib>
|
<NoStdLib>true</NoStdLib>
|
||||||
|
@ -88,9 +88,6 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Source\AffineThreadPool.Test.cs">
|
|
||||||
<DependentUpon>AffineThreadPool.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Cloning\CloneFactoryTest.cs" />
|
<Compile Include="Source\Cloning\CloneFactoryTest.cs" />
|
||||||
<Compile Include="Source\Cloning\ClonerHelpers.cs" />
|
<Compile Include="Source\Cloning\ClonerHelpers.cs" />
|
||||||
<Compile Include="Source\Cloning\ClonerHelpers.Test.cs">
|
<Compile Include="Source\Cloning\ClonerHelpers.Test.cs">
|
||||||
|
@ -217,27 +214,6 @@
|
||||||
<Compile Include="Source\Collections\ReverseComparer.Test.cs">
|
<Compile Include="Source\Collections\ReverseComparer.Test.cs">
|
||||||
<DependentUpon>ReverseComparer.cs</DependentUpon>
|
<DependentUpon>ReverseComparer.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\AffineThreadPool.cs" />
|
|
||||||
<Compile Include="Source\EnumHelper.cs" />
|
|
||||||
<Compile Include="Source\EnumHelper.Test.cs">
|
|
||||||
<DependentUpon>EnumHelper.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Plugins\PrototypeFactory.cs" />
|
|
||||||
<Compile Include="Source\Plugins\PrototypeFactory.Test.cs">
|
|
||||||
<DependentUpon>PrototypeFactory.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Semaphore.cs" />
|
|
||||||
<Compile Include="Source\Semaphore.Test.cs">
|
|
||||||
<DependentUpon>Semaphore.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\IO\PartialStream.cs" />
|
|
||||||
<Compile Include="Source\IO\PartialStream.Test.cs">
|
|
||||||
<DependentUpon>PartialStream.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\IO\RingMemoryStream.cs" />
|
|
||||||
<Compile Include="Source\IO\RingMemoryStream.Test.cs">
|
|
||||||
<DependentUpon>RingMemoryStream.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Collections\TransformingReadOnlyCollection.cs" />
|
<Compile Include="Source\Collections\TransformingReadOnlyCollection.cs" />
|
||||||
<Compile Include="Source\Collections\TransformingReadOnlyCollection.Interfaces.cs">
|
<Compile Include="Source\Collections\TransformingReadOnlyCollection.Interfaces.cs">
|
||||||
<DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon>
|
<DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon>
|
||||||
|
@ -252,13 +228,17 @@
|
||||||
<Compile Include="Source\Collections\WeakCollection.Test.cs">
|
<Compile Include="Source\Collections\WeakCollection.Test.cs">
|
||||||
<DependentUpon>WeakCollection.cs</DependentUpon>
|
<DependentUpon>WeakCollection.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\FloatHelper.cs" />
|
<Compile Include="Source\IO\PartialStream.cs" />
|
||||||
<Compile Include="Source\FloatHelper.Test.cs">
|
<Compile Include="Source\IO\PartialStream.Test.cs">
|
||||||
<DependentUpon>FloatHelper.cs</DependentUpon>
|
<DependentUpon>PartialStream.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\IntegerHelper.cs" />
|
<Compile Include="Source\IO\RingMemoryStream.cs" />
|
||||||
<Compile Include="Source\IntegerHelper.Test.cs">
|
<Compile Include="Source\IO\RingMemoryStream.Test.cs">
|
||||||
<DependentUpon>IntegerHelper.cs</DependentUpon>
|
<DependentUpon>RingMemoryStream.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\IO\ChainStream.cs" />
|
||||||
|
<Compile Include="Source\IO\ChainStream.Test.cs">
|
||||||
|
<DependentUpon>ChainStream.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\Licensing\LicenseKey.cs" />
|
<Compile Include="Source\Licensing\LicenseKey.cs" />
|
||||||
<Compile Include="Source\Licensing\LicenseKey.Test.cs">
|
<Compile Include="Source\Licensing\LicenseKey.Test.cs">
|
||||||
|
@ -277,10 +257,6 @@
|
||||||
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
||||||
<DependentUpon>CommandLine.cs</DependentUpon>
|
<DependentUpon>CommandLine.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\PathHelper.cs" />
|
|
||||||
<Compile Include="Source\PathHelper.Test.cs">
|
|
||||||
<DependentUpon>PathHelper.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.cs" />
|
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.cs" />
|
||||||
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.Test.cs">
|
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.Test.cs">
|
||||||
<DependentUpon>AssemblyLoadEventArgs.cs</DependentUpon>
|
<DependentUpon>AssemblyLoadEventArgs.cs</DependentUpon>
|
||||||
|
@ -292,6 +268,10 @@
|
||||||
<Compile Include="Source\Plugins\FactoryEmployer.Test.cs">
|
<Compile Include="Source\Plugins\FactoryEmployer.Test.cs">
|
||||||
<DependentUpon>FactoryEmployer.cs</DependentUpon>
|
<DependentUpon>FactoryEmployer.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Source\Plugins\PrototypeFactory.cs" />
|
||||||
|
<Compile Include="Source\Plugins\PrototypeFactory.Test.cs">
|
||||||
|
<DependentUpon>PrototypeFactory.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Plugins\IAssemblyLoader.cs" />
|
<Compile Include="Source\Plugins\IAssemblyLoader.cs" />
|
||||||
<Compile Include="Source\Plugins\InstanceEmployer.Test.cs">
|
<Compile Include="Source\Plugins\InstanceEmployer.Test.cs">
|
||||||
<DependentUpon>InstanceEmployer.cs</DependentUpon>
|
<DependentUpon>InstanceEmployer.cs</DependentUpon>
|
||||||
|
@ -315,14 +295,46 @@
|
||||||
<Compile Include="Source\Plugins\PluginRepository.Test.cs">
|
<Compile Include="Source\Plugins\PluginRepository.Test.cs">
|
||||||
<DependentUpon>PluginRepository.cs</DependentUpon>
|
<DependentUpon>PluginRepository.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Source\PropertyChangedEventArgsHelper.cs" />
|
||||||
|
<Compile Include="Source\PropertyChangedEventArgsHelper.Test.cs">
|
||||||
|
<DependentUpon>PropertyChangedEventArgsHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\AffineThreadPool.cs" />
|
||||||
|
<Compile Include="Source\AffineThreadPool.Test.cs">
|
||||||
|
<DependentUpon>AffineThreadPool.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\EnumHelper.cs" />
|
||||||
|
<Compile Include="Source\EnumHelper.Test.cs">
|
||||||
|
<DependentUpon>EnumHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\Observable.cs" />
|
||||||
|
<Compile Include="Source\Observable.Test.cs">
|
||||||
|
<DependentUpon>Observable.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\ObservableHelper.cs" />
|
||||||
|
<Compile Include="Source\ObservableHelper.Test.cs">
|
||||||
|
<DependentUpon>ObservableHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\Semaphore.cs" />
|
||||||
|
<Compile Include="Source\Semaphore.Test.cs">
|
||||||
|
<DependentUpon>Semaphore.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\FloatHelper.cs" />
|
||||||
|
<Compile Include="Source\FloatHelper.Test.cs">
|
||||||
|
<DependentUpon>FloatHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\IntegerHelper.cs" />
|
||||||
|
<Compile Include="Source\IntegerHelper.Test.cs">
|
||||||
|
<DependentUpon>IntegerHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\PathHelper.cs" />
|
||||||
|
<Compile Include="Source\PathHelper.Test.cs">
|
||||||
|
<DependentUpon>PathHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Shared.cs" />
|
<Compile Include="Source\Shared.cs" />
|
||||||
<Compile Include="Source\Shared.Test.cs">
|
<Compile Include="Source\Shared.Test.cs">
|
||||||
<DependentUpon>Shared.cs</DependentUpon>
|
<DependentUpon>Shared.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\IO\ChainStream.cs" />
|
|
||||||
<Compile Include="Source\IO\ChainStream.Test.cs">
|
|
||||||
<DependentUpon>ChainStream.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\StringBuilderHelper.cs" />
|
<Compile Include="Source\StringBuilderHelper.cs" />
|
||||||
<Compile Include="Source\StringBuilderHelper.Test.cs">
|
<Compile Include="Source\StringBuilderHelper.Test.cs">
|
||||||
<DependentUpon>StringBuilderHelper.cs</DependentUpon>
|
<DependentUpon>StringBuilderHelper.cs</DependentUpon>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>bin\xna-4.0-xbox360\Debug\</OutputPath>
|
<OutputPath>bin\xna-4.0-xbox360\Debug\</OutputPath>
|
||||||
<DefineConstants>TRACE;DEBUG;XBOX;XBOX360;NO_CLONING;NO_SERIALIZATION;NO_XMLSCHEMA;NO_SYSTEMEVENTS;NO_EXITCONTEXT;NO_SPECIALIZED_COLLECTIONS</DefineConstants>
|
<DefineConstants>TRACE;DEBUG;XBOX;XBOX360;NO_CLONING;NO_SERIALIZATION;NO_XMLSCHEMA;NO_SYSTEMEVENTS;NO_EXITCONTEXT;NO_SPECIALIZED_COLLECTIONS;NO_LINQ_EXPRESSIONS;NO_CONCURRENT_COLLECTIONS</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<NoStdLib>true</NoStdLib>
|
<NoStdLib>true</NoStdLib>
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<OutputPath>bin\xna-4.0-xbox360\Release\</OutputPath>
|
<OutputPath>bin\xna-4.0-xbox360\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE;XBOX;XBOX360;NO_CLONING;NO_SERIALIZATION;NO_XMLSCHEMA;NO_SYSTEMEVENTS;NO_EXITCONTEXT;NO_SPECIALIZED_COLLECTIONS</DefineConstants>
|
<DefineConstants>TRACE;XBOX;XBOX360;NO_CLONING;NO_SERIALIZATION;NO_XMLSCHEMA;NO_SYSTEMEVENTS;NO_EXITCONTEXT;NO_SPECIALIZED_COLLECTIONS;NO_LINQ_EXPRESSIONS;NO_CONCURRENT_COLLECTIONS</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<NoStdLib>true</NoStdLib>
|
<NoStdLib>true</NoStdLib>
|
||||||
|
@ -99,9 +99,6 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Source\AffineThreadPool.Test.cs">
|
|
||||||
<DependentUpon>AffineThreadPool.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Cloning\CloneFactoryTest.cs" />
|
<Compile Include="Source\Cloning\CloneFactoryTest.cs" />
|
||||||
<Compile Include="Source\Cloning\ClonerHelpers.cs" />
|
<Compile Include="Source\Cloning\ClonerHelpers.cs" />
|
||||||
<Compile Include="Source\Cloning\ClonerHelpers.Test.cs">
|
<Compile Include="Source\Cloning\ClonerHelpers.Test.cs">
|
||||||
|
@ -228,27 +225,6 @@
|
||||||
<Compile Include="Source\Collections\ReverseComparer.Test.cs">
|
<Compile Include="Source\Collections\ReverseComparer.Test.cs">
|
||||||
<DependentUpon>ReverseComparer.cs</DependentUpon>
|
<DependentUpon>ReverseComparer.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\AffineThreadPool.cs" />
|
|
||||||
<Compile Include="Source\EnumHelper.cs" />
|
|
||||||
<Compile Include="Source\EnumHelper.Test.cs">
|
|
||||||
<DependentUpon>EnumHelper.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Plugins\PrototypeFactory.cs" />
|
|
||||||
<Compile Include="Source\Plugins\PrototypeFactory.Test.cs">
|
|
||||||
<DependentUpon>PrototypeFactory.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Semaphore.cs" />
|
|
||||||
<Compile Include="Source\Semaphore.Test.cs">
|
|
||||||
<DependentUpon>Semaphore.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\IO\PartialStream.cs" />
|
|
||||||
<Compile Include="Source\IO\PartialStream.Test.cs">
|
|
||||||
<DependentUpon>PartialStream.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\IO\RingMemoryStream.cs" />
|
|
||||||
<Compile Include="Source\IO\RingMemoryStream.Test.cs">
|
|
||||||
<DependentUpon>RingMemoryStream.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Collections\TransformingReadOnlyCollection.cs" />
|
<Compile Include="Source\Collections\TransformingReadOnlyCollection.cs" />
|
||||||
<Compile Include="Source\Collections\TransformingReadOnlyCollection.Interfaces.cs">
|
<Compile Include="Source\Collections\TransformingReadOnlyCollection.Interfaces.cs">
|
||||||
<DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon>
|
<DependentUpon>TransformingReadOnlyCollection.cs</DependentUpon>
|
||||||
|
@ -263,13 +239,17 @@
|
||||||
<Compile Include="Source\Collections\WeakCollection.Test.cs">
|
<Compile Include="Source\Collections\WeakCollection.Test.cs">
|
||||||
<DependentUpon>WeakCollection.cs</DependentUpon>
|
<DependentUpon>WeakCollection.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\FloatHelper.cs" />
|
<Compile Include="Source\IO\PartialStream.cs" />
|
||||||
<Compile Include="Source\FloatHelper.Test.cs">
|
<Compile Include="Source\IO\PartialStream.Test.cs">
|
||||||
<DependentUpon>FloatHelper.cs</DependentUpon>
|
<DependentUpon>PartialStream.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\IntegerHelper.cs" />
|
<Compile Include="Source\IO\RingMemoryStream.cs" />
|
||||||
<Compile Include="Source\IntegerHelper.Test.cs">
|
<Compile Include="Source\IO\RingMemoryStream.Test.cs">
|
||||||
<DependentUpon>IntegerHelper.cs</DependentUpon>
|
<DependentUpon>RingMemoryStream.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\IO\ChainStream.cs" />
|
||||||
|
<Compile Include="Source\IO\ChainStream.Test.cs">
|
||||||
|
<DependentUpon>ChainStream.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\Licensing\LicenseKey.cs" />
|
<Compile Include="Source\Licensing\LicenseKey.cs" />
|
||||||
<Compile Include="Source\Licensing\LicenseKey.Test.cs">
|
<Compile Include="Source\Licensing\LicenseKey.Test.cs">
|
||||||
|
@ -288,10 +268,6 @@
|
||||||
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
<Compile Include="Source\Parsing\CommandLine.Parser.cs">
|
||||||
<DependentUpon>CommandLine.cs</DependentUpon>
|
<DependentUpon>CommandLine.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\PathHelper.cs" />
|
|
||||||
<Compile Include="Source\PathHelper.Test.cs">
|
|
||||||
<DependentUpon>PathHelper.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.cs" />
|
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.cs" />
|
||||||
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.Test.cs">
|
<Compile Include="Source\Plugins\AssemblyLoadEventArgs.Test.cs">
|
||||||
<DependentUpon>AssemblyLoadEventArgs.cs</DependentUpon>
|
<DependentUpon>AssemblyLoadEventArgs.cs</DependentUpon>
|
||||||
|
@ -303,6 +279,10 @@
|
||||||
<Compile Include="Source\Plugins\FactoryEmployer.Test.cs">
|
<Compile Include="Source\Plugins\FactoryEmployer.Test.cs">
|
||||||
<DependentUpon>FactoryEmployer.cs</DependentUpon>
|
<DependentUpon>FactoryEmployer.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Source\Plugins\PrototypeFactory.cs" />
|
||||||
|
<Compile Include="Source\Plugins\PrototypeFactory.Test.cs">
|
||||||
|
<DependentUpon>PrototypeFactory.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Plugins\IAssemblyLoader.cs" />
|
<Compile Include="Source\Plugins\IAssemblyLoader.cs" />
|
||||||
<Compile Include="Source\Plugins\InstanceEmployer.Test.cs">
|
<Compile Include="Source\Plugins\InstanceEmployer.Test.cs">
|
||||||
<DependentUpon>InstanceEmployer.cs</DependentUpon>
|
<DependentUpon>InstanceEmployer.cs</DependentUpon>
|
||||||
|
@ -326,14 +306,46 @@
|
||||||
<Compile Include="Source\Plugins\PluginRepository.Test.cs">
|
<Compile Include="Source\Plugins\PluginRepository.Test.cs">
|
||||||
<DependentUpon>PluginRepository.cs</DependentUpon>
|
<DependentUpon>PluginRepository.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Source\PropertyChangedEventArgsHelper.cs" />
|
||||||
|
<Compile Include="Source\PropertyChangedEventArgsHelper.Test.cs">
|
||||||
|
<DependentUpon>PropertyChangedEventArgsHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\AffineThreadPool.cs" />
|
||||||
|
<Compile Include="Source\AffineThreadPool.Test.cs">
|
||||||
|
<DependentUpon>AffineThreadPool.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\EnumHelper.cs" />
|
||||||
|
<Compile Include="Source\EnumHelper.Test.cs">
|
||||||
|
<DependentUpon>EnumHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\Observable.cs" />
|
||||||
|
<Compile Include="Source\Observable.Test.cs">
|
||||||
|
<DependentUpon>Observable.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\ObservableHelper.cs" />
|
||||||
|
<Compile Include="Source\ObservableHelper.Test.cs">
|
||||||
|
<DependentUpon>ObservableHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\Semaphore.cs" />
|
||||||
|
<Compile Include="Source\Semaphore.Test.cs">
|
||||||
|
<DependentUpon>Semaphore.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\FloatHelper.cs" />
|
||||||
|
<Compile Include="Source\FloatHelper.Test.cs">
|
||||||
|
<DependentUpon>FloatHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\IntegerHelper.cs" />
|
||||||
|
<Compile Include="Source\IntegerHelper.Test.cs">
|
||||||
|
<DependentUpon>IntegerHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\PathHelper.cs" />
|
||||||
|
<Compile Include="Source\PathHelper.Test.cs">
|
||||||
|
<DependentUpon>PathHelper.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Shared.cs" />
|
<Compile Include="Source\Shared.cs" />
|
||||||
<Compile Include="Source\Shared.Test.cs">
|
<Compile Include="Source\Shared.Test.cs">
|
||||||
<DependentUpon>Shared.cs</DependentUpon>
|
<DependentUpon>Shared.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\IO\ChainStream.cs" />
|
|
||||||
<Compile Include="Source\IO\ChainStream.Test.cs">
|
|
||||||
<DependentUpon>ChainStream.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Source\StringBuilderHelper.cs" />
|
<Compile Include="Source\StringBuilderHelper.cs" />
|
||||||
<Compile Include="Source\StringBuilderHelper.Test.cs">
|
<Compile Include="Source\StringBuilderHelper.Test.cs">
|
||||||
<DependentUpon>StringBuilderHelper.cs</DependentUpon>
|
<DependentUpon>StringBuilderHelper.cs</DependentUpon>
|
||||||
|
|
|
@ -239,9 +239,25 @@ namespace Nuclex.Support.Collections {
|
||||||
return this.typedDictionary.GetEnumerator();
|
return this.typedDictionary.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Removes the specified key/value pair from the dictionary</summary>
|
/// <summary>Number of unique keys in the dictionary</summary>
|
||||||
/// <param name="item">Key/value pair that will be removed</param>
|
/// <remarks>
|
||||||
/// <returns>True if the key/value pair was contained in the dictionary</returns>
|
/// <para>
|
||||||
|
/// This Count property returns a different value from the main interface of
|
||||||
|
/// the multi dictionary to stay consistent with the implemented interfaces.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// If you cast a multi dictionary to a collection of collections, the count
|
||||||
|
/// property of the outer collection should, of course, be the number of inner
|
||||||
|
/// collections it contains (and not the sum of the items contained in all of
|
||||||
|
/// the inner collections).
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// If you use the count property in the main interface of the multi dictionary,
|
||||||
|
/// the value collections are hidden (it behaves as if the key was in the
|
||||||
|
/// dictionary multiple times), so now the sum of all key-value pairs should
|
||||||
|
/// be returned.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
int ICollection<KeyValuePair<TKey, ICollection<TValue>>>.Count {
|
int ICollection<KeyValuePair<TKey, ICollection<TValue>>>.Count {
|
||||||
get { return this.typedDictionary.Count; }
|
get { return this.typedDictionary.Count; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,22 +39,46 @@ namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
public interface IObservableCollectionSubscriber<TItem> {
|
public interface IObservableCollectionSubscriber<TItem> {
|
||||||
|
|
||||||
/// <summary>Raised when an item has been added to the collection</summary>
|
/// <summary>Called when an item has been added to the collection</summary>
|
||||||
event EventHandler<ItemEventArgs<TItem>> ItemAdded;
|
void ItemAdded(object sender, ItemEventArgs<TItem> arguments);
|
||||||
/// <summary>Raised when an item is removed from the collection</summary>
|
/// <summary>Called when an item is removed from the collection</summary>
|
||||||
event EventHandler<ItemEventArgs<TItem>> ItemRemoved;
|
void ItemRemoved(object sender, ItemEventArgs<TItem> arguments);
|
||||||
/// <summary>Raised when an item is replaced in the collection</summary>
|
/// <summary>Called when an item is replaced in the collection</summary>
|
||||||
event EventHandler<ItemReplaceEventArgs<TItem>> ItemReplaced;
|
void ItemReplaced(object sender, ItemReplaceEventArgs<TItem> arguments);
|
||||||
/// <summary>Raised when the collection is about to be cleared</summary>
|
/// <summary>Called when the collection is about to be cleared</summary>
|
||||||
event EventHandler Clearing;
|
void Clearing(object sender, EventArgs arguments);
|
||||||
/// <summary>Raised when the collection has been cleared</summary>
|
/// <summary>Called when the collection has been cleared</summary>
|
||||||
event EventHandler Cleared;
|
void Cleared(object sender, EventArgs arguments);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion // interface IObservableCollectionSubscriber<TItem>
|
#endregion // interface IObservableCollectionSubscriber<TItem>
|
||||||
|
|
||||||
|
/// <summary>Called before each test is run</summary>
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() {
|
||||||
|
this.mockFactory = new MockFactory();
|
||||||
|
this.observableSet = new ObservableSet<int>();
|
||||||
|
|
||||||
|
this.subscriber = this.mockFactory.CreateMock<IObservableCollectionSubscriber<int>>();
|
||||||
|
this.observableSet.ItemAdded += this.subscriber.MockObject.ItemAdded;
|
||||||
|
this.observableSet.ItemRemoved += this.subscriber.MockObject.ItemRemoved;
|
||||||
|
this.observableSet.ItemReplaced += this.subscriber.MockObject.ItemReplaced;
|
||||||
|
this.observableSet.Clearing += this.subscriber.MockObject.Clearing;
|
||||||
|
this.observableSet.Cleared += this.subscriber.MockObject.Cleared;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called after each test has run</summary>
|
||||||
|
[TearDown]
|
||||||
|
public void Teardown() {
|
||||||
|
if(this.mockFactory != null) {
|
||||||
|
this.mockFactory.VerifyAllExpectationsHaveBeenMet();
|
||||||
|
|
||||||
|
this.subscriber = null;
|
||||||
|
this.mockFactory.Dispose();
|
||||||
|
this.mockFactory = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verifies that the observable set has a default constructor
|
/// Verifies that the observable set has a default constructor
|
||||||
|
@ -64,6 +88,119 @@ namespace Nuclex.Support.Collections {
|
||||||
Assert.IsNotNull(new ObservableSet<int>());
|
Assert.IsNotNull(new ObservableSet<int>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that adding items to the set triggers the 'ItemAdded' event
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AddingItemsTriggersEvent() {
|
||||||
|
this.subscriber.Expects.One.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
|
||||||
|
this.observableSet.Add(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that adding items to the set triggers the 'ItemAdded' event
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AddingAlreadyContainedItemDoesNotTriggerEvent() {
|
||||||
|
this.subscriber.Expects.One.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
|
||||||
|
this.observableSet.Add(123);
|
||||||
|
|
||||||
|
this.subscriber.Expects.No.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
|
||||||
|
this.observableSet.Add(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that excepting the set with itself empties the set
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void ExceptWithSelfEmptiesSet() {
|
||||||
|
var set = new ObservableSet<int>();
|
||||||
|
set.Add(1);
|
||||||
|
set.Add(2);
|
||||||
|
set.Add(3);
|
||||||
|
|
||||||
|
Assert.AreEqual(3, set.Count);
|
||||||
|
set.ExceptWith(set);
|
||||||
|
Assert.AreEqual(0, set.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that a set can be excepted with a collection
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetCanBeExceptedWithCollection() {
|
||||||
|
var set = new ObservableSet<int>();
|
||||||
|
set.Add(1);
|
||||||
|
set.Add(2);
|
||||||
|
|
||||||
|
var collection = new List<int>() { 1 };
|
||||||
|
|
||||||
|
Assert.AreEqual(2, set.Count);
|
||||||
|
set.ExceptWith(collection);
|
||||||
|
Assert.AreEqual(1, set.Count);
|
||||||
|
Assert.IsTrue(set.Contains(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that a set can be intersected with a collection
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetCanBeIntersectedWithCollection() {
|
||||||
|
var set = new ObservableSet<int>();
|
||||||
|
set.Add(1);
|
||||||
|
set.Add(2);
|
||||||
|
|
||||||
|
var collection = new List<int>() { 1 };
|
||||||
|
|
||||||
|
Assert.AreEqual(2, set.Count);
|
||||||
|
set.IntersectWith(collection);
|
||||||
|
Assert.AreEqual(1, set.Count);
|
||||||
|
Assert.IsTrue(set.Contains(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's possible to determine whether a set is a proper subset
|
||||||
|
/// or superset of another set
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void CanDetermineProperSubsetAndSuperset() {
|
||||||
|
var set1 = new ObservableSet<int>() { 1, 2, 3 };
|
||||||
|
var set2 = new ObservableSet<int>() { 1, 3 };
|
||||||
|
|
||||||
|
Assert.IsTrue(set1.IsProperSupersetOf(set2));
|
||||||
|
Assert.IsTrue(set2.IsProperSubsetOf(set1));
|
||||||
|
|
||||||
|
set2.Add(2);
|
||||||
|
|
||||||
|
Assert.IsFalse(set1.IsProperSupersetOf(set2));
|
||||||
|
Assert.IsFalse(set2.IsProperSubsetOf(set1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that it's possible to determine whether a set is a subset
|
||||||
|
/// or a superset of another set
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void CanDetermineSubsetAndSuperset() {
|
||||||
|
var set1 = new ObservableSet<int>() { 1, 2, 3 };
|
||||||
|
var set2 = new ObservableSet<int>() { 1, 2, 3 };
|
||||||
|
|
||||||
|
Assert.IsTrue(set1.IsSupersetOf(set2));
|
||||||
|
Assert.IsTrue(set2.IsSubsetOf(set1));
|
||||||
|
|
||||||
|
set2.Add(4);
|
||||||
|
|
||||||
|
Assert.IsFalse(set1.IsSupersetOf(set2));
|
||||||
|
Assert.IsFalse(set2.IsSubsetOf(set1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates mock object for the test</summary>
|
||||||
|
private MockFactory mockFactory;
|
||||||
|
/// <summary>Observable set being tested</summary>
|
||||||
|
private ObservableSet<int> observableSet;
|
||||||
|
/// <summary>Subscriber for the observable set's events</summary>
|
||||||
|
private Mock<IObservableCollectionSubscriber<int>> subscriber;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Nuclex.Support.Collections
|
} // namespace Nuclex.Support.Collections
|
||||||
|
|
|
@ -37,9 +37,9 @@ namespace Nuclex.Support.Collections {
|
||||||
ISet<TItem>,
|
ISet<TItem>,
|
||||||
ICollection<TItem>,
|
ICollection<TItem>,
|
||||||
#if !NO_SPECIALIZED_COLLECTIONS
|
#if !NO_SPECIALIZED_COLLECTIONS
|
||||||
INotifyCollectionChanged,
|
INotifyCollectionChanged,
|
||||||
#endif
|
#endif
|
||||||
IObservableCollection<TItem> {
|
IObservableCollection<TItem> {
|
||||||
|
|
||||||
/// <summary>Raised when an item has been added to the collection</summary>
|
/// <summary>Raised when an item has been added to the collection</summary>
|
||||||
public event EventHandler<ItemEventArgs<TItem>> ItemAdded;
|
public event EventHandler<ItemEventArgs<TItem>> ItemAdded;
|
||||||
|
@ -109,12 +109,22 @@ namespace Nuclex.Support.Collections {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">Other set this set will be filtered by</param>
|
/// <param name="other">Other set this set will be filtered by</param>
|
||||||
public void IntersectWith(IEnumerable<TItem> other) {
|
public void IntersectWith(IEnumerable<TItem> other) {
|
||||||
foreach(TItem item in other) {
|
var otherSet = other as ISet<TItem>;
|
||||||
if(!other.Contains(item)) {
|
if(otherSet == null) {
|
||||||
this.set.Remove(item);
|
otherSet = new HashSet<TItem>(other);
|
||||||
OnRemoved(item);
|
}
|
||||||
|
|
||||||
|
var itemsToRemove = new List<TItem>();
|
||||||
|
foreach(TItem item in this.set) {
|
||||||
|
if(!otherSet.Contains(item)) {
|
||||||
|
itemsToRemove.Add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(int index = 0; index < itemsToRemove.Count; ++index) {
|
||||||
|
this.set.Remove(itemsToRemove[index]);
|
||||||
|
OnRemoved(itemsToRemove[index]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -127,4 +127,4 @@ namespace Nuclex.Support {
|
||||||
|
|
||||||
} // namespace Nuclex.Support
|
} // namespace Nuclex.Support
|
||||||
|
|
||||||
#endif // UNITTEST
|
#endif // UNITTEST
|
||||||
|
|
170
Source/Observable.Test.cs
Normal file
170
Source/Observable.Test.cs
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
#region CPL License
|
||||||
|
/*
|
||||||
|
Nuclex Framework
|
||||||
|
Copyright (C) 2002-2012 Nuclex Development Labs
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the IBM Common Public License as
|
||||||
|
published by the IBM Corporation; either version 1.0 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
IBM Common Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the IBM Common Public
|
||||||
|
License along with this library
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#if UNITTEST
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NMock;
|
||||||
|
|
||||||
|
namespace Nuclex.Support {
|
||||||
|
|
||||||
|
/// <summary>Unit tests for observable class</summary>
|
||||||
|
[TestFixture]
|
||||||
|
internal class ObservableTest {
|
||||||
|
|
||||||
|
#region class TestObservable
|
||||||
|
|
||||||
|
/// <summary>Example class on which unit test generates change notifications</summary>
|
||||||
|
public class TestObservable : Observable {
|
||||||
|
|
||||||
|
/// <summary>Triggers the property changed event for the specified property</summary>
|
||||||
|
/// <param name="propertyName">
|
||||||
|
/// Name of the property that will be reported as changed
|
||||||
|
/// </param>
|
||||||
|
public void FirePropertyChanged(string propertyName) {
|
||||||
|
OnPropertyChanged(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Fires the property changed event for the 'SomePropety' property</summary>
|
||||||
|
public void FireSomePropertyChanged() {
|
||||||
|
OnPropertyChanged(() => SomeProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Example property that will be reported to have changed</summary>
|
||||||
|
public int SomeProperty { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // class TestObservable
|
||||||
|
|
||||||
|
#region class MockedSubscriber
|
||||||
|
|
||||||
|
/// <summary>Mocked change notification subscriber</summary>
|
||||||
|
public class MockedSubscriber {
|
||||||
|
|
||||||
|
/// <summary>Called when the value of a property has changed</summary>
|
||||||
|
/// <param name="sender">Object of which a property has changed</param>
|
||||||
|
/// <param name="arguments">Contains the name of the changed property</param>
|
||||||
|
public void PropertyChanged(object sender, PropertyChangedEventArgs arguments) {
|
||||||
|
this.wasNotified = true;
|
||||||
|
this.changedPropertyName = arguments.PropertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the subscriber was notified of a property change</summary>
|
||||||
|
public bool WasNotified {
|
||||||
|
get { return this.wasNotified; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a change notification for the specified property was received
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyName">Name of the property that will be checked for</param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if a change notification for the specified property was received
|
||||||
|
/// </returns>
|
||||||
|
public bool WasNotifiedOfChangeTo(string propertyName) {
|
||||||
|
if(!this.wasNotified) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(string.IsNullOrEmpty(propertyName)) {
|
||||||
|
return string.IsNullOrEmpty(this.changedPropertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (propertyName == this.changedPropertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether a change notification was received</summary>
|
||||||
|
private bool wasNotified;
|
||||||
|
/// <summary>Name of the property for which a change notification was received</summary>
|
||||||
|
private string changedPropertyName;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // class MockedSubscriber
|
||||||
|
|
||||||
|
/// <summary>Called before each unit test is run</summary>
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() {
|
||||||
|
this.testObservable = new TestObservable();
|
||||||
|
this.subscriber = new MockedSubscriber();
|
||||||
|
|
||||||
|
this.testObservable.PropertyChanged += this.subscriber.PropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the name of the changed property can be specified manually
|
||||||
|
/// when triggering the PropertyChanged event
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void PropertyNameCanBeSpecifiedManually() {
|
||||||
|
this.testObservable.FirePropertyChanged("SomeProperty");
|
||||||
|
Assert.IsTrue(this.subscriber.WasNotifiedOfChangeTo("SomeProperty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG // The check is conditionally performed only in debug mode
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that specifying the name of a property that doesn't exist
|
||||||
|
/// causes an ArgumentException to be thrown
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SpecifyingInvalidPropertyNameThrowsArgumentException() {
|
||||||
|
Assert.Throws<ArgumentException>(
|
||||||
|
delegate() { this.testObservable.FirePropertyChanged("DoesntExist"); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the observable is capable of deducing the name of the property
|
||||||
|
/// from a lambda expression
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void PropertyNameCanBeDeducedFromLambdaExpression() {
|
||||||
|
this.testObservable.FireSomePropertyChanged();
|
||||||
|
Assert.IsTrue(this.subscriber.WasNotifiedOfChangeTo("SomeProperty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that change notifications for all properties of a type can
|
||||||
|
/// be generated
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WildcardChangeNotificationsCanBeSent() {
|
||||||
|
this.testObservable.FirePropertyChanged(string.Empty);
|
||||||
|
Assert.IsTrue(this.subscriber.WasNotifiedOfChangeTo(null));
|
||||||
|
|
||||||
|
this.testObservable.FirePropertyChanged(null);
|
||||||
|
Assert.IsTrue(this.subscriber.WasNotifiedOfChangeTo(string.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Observable object being tested</summary>
|
||||||
|
private TestObservable testObservable;
|
||||||
|
/// <summary>Subscriber to the observable object being tested</summary>
|
||||||
|
private MockedSubscriber subscriber;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support
|
||||||
|
|
||||||
|
#endif // UNITTEST
|
133
Source/Observable.cs
Normal file
133
Source/Observable.cs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
#region CPL License
|
||||||
|
/*
|
||||||
|
Nuclex Framework
|
||||||
|
Copyright (C) 2002-2012 Nuclex Development Labs
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the IBM Common Public License as
|
||||||
|
published by the IBM Corporation; either version 1.0 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
IBM Common Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the IBM Common Public
|
||||||
|
License along with this library
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
#if !NO_LINQ_EXPRESSIONS
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
#endif
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Nuclex.Support {
|
||||||
|
|
||||||
|
/// <summary>Base class for objects that support property change notifications</summary>
|
||||||
|
#if !NO_SERIALIZATION
|
||||||
|
[Serializable]
|
||||||
|
#endif
|
||||||
|
public abstract class Observable : INotifyPropertyChanged {
|
||||||
|
|
||||||
|
/// <summary>Raised when a property of the instance has changed its value</summary>
|
||||||
|
#if !NO_SERIALIZATION
|
||||||
|
[field: NonSerialized]
|
||||||
|
#endif
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
#if !NO_LINQ_EXPRESSIONS
|
||||||
|
/// <summary>Triggers the PropertyChanged event for the specified property</summary>
|
||||||
|
/// <param name="property">
|
||||||
|
/// Lambda expression for the property that will be reported to have changed
|
||||||
|
/// </param>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// This notification should be fired post-change, i.e. when the property has
|
||||||
|
/// already changed its value.
|
||||||
|
/// </para>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// public int Limit {
|
||||||
|
/// get { return this.limit; }
|
||||||
|
/// set {
|
||||||
|
/// if(value != this.limit) {
|
||||||
|
/// this.limit = value;
|
||||||
|
/// OnPropertyChanged(() => Limit);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </remarks>
|
||||||
|
protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> property) {
|
||||||
|
PropertyChangedEventHandler copy = PropertyChanged;
|
||||||
|
if(copy != null) {
|
||||||
|
copy(this, PropertyChangedEventArgsHelper.GetArgumentsFor(property));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // !NO_LINQ_EXPRESSIONS
|
||||||
|
|
||||||
|
/// <summary>Triggers the PropertyChanged event for the specified property</summary>
|
||||||
|
/// <param name="propertyName">Name of the property that has changed its value</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// This notification should be fired post-change, i.e. when the property has
|
||||||
|
/// already changed its value. If possible, use the other overload of this
|
||||||
|
/// method to ensure the property name will be updated during F2 refactoring.
|
||||||
|
/// </para>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// public int Limit {
|
||||||
|
/// get { return this.limit; }
|
||||||
|
/// set {
|
||||||
|
/// if(value != this.limit) {
|
||||||
|
/// this.limit = value;
|
||||||
|
/// OnPropertyChanged("Limit"); // Note: prefer lambda exp whenever possible
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </remarks>
|
||||||
|
protected virtual void OnPropertyChanged(string propertyName) {
|
||||||
|
enforceChangedPropertyExists(propertyName);
|
||||||
|
|
||||||
|
PropertyChangedEventHandler copy = PropertyChanged;
|
||||||
|
if(copy != null) {
|
||||||
|
copy(this, PropertyChangedEventArgsHelper.GetArgumentsFor(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Ensures that a property with the specified name exists in the type</summary>
|
||||||
|
/// <param name="propertyName">Property name that will be checked</param>
|
||||||
|
[Conditional("DEBUG")]
|
||||||
|
private void enforceChangedPropertyExists(string propertyName) {
|
||||||
|
|
||||||
|
// An empty string or null indicates that all properties have changed
|
||||||
|
if(string.IsNullOrEmpty(propertyName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any other string needs to match a property name
|
||||||
|
PropertyInfo property = GetType().GetProperty(propertyName);
|
||||||
|
if(property == null) {
|
||||||
|
throw new ArgumentException(
|
||||||
|
string.Format(
|
||||||
|
"Type '{0}' tried to raise a change notification for property '{1}', " +
|
||||||
|
"but no such property exists!",
|
||||||
|
GetType().Name, propertyName
|
||||||
|
),
|
||||||
|
"propertyName"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support
|
91
Source/ObservableHelper.Test.cs
Normal file
91
Source/ObservableHelper.Test.cs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#region CPL License
|
||||||
|
/*
|
||||||
|
Nuclex Framework
|
||||||
|
Copyright (C) 2002-2012 Nuclex Development Labs
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the IBM Common Public License as
|
||||||
|
published by the IBM Corporation; either version 1.0 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
IBM Common Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the IBM Common Public
|
||||||
|
License along with this library
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#if UNITTEST
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Nuclex.Support {
|
||||||
|
|
||||||
|
/// <summary>Unit tests for the observable helper</summary>
|
||||||
|
[TestFixture]
|
||||||
|
internal class ObservableHelperTest {
|
||||||
|
|
||||||
|
#region class TestReferenceType
|
||||||
|
|
||||||
|
/// <summary>Example class on which unit test generates change notifications</summary>
|
||||||
|
public class TestReferenceType {
|
||||||
|
|
||||||
|
/// <summary>Example property that will be reported to have changed</summary>
|
||||||
|
public int SomeProperty { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // class TestReferenceType
|
||||||
|
|
||||||
|
#region struct TestValueType
|
||||||
|
|
||||||
|
/// <summary>Example class on which unit test generates change notifications</summary>
|
||||||
|
public struct TestValueType {
|
||||||
|
|
||||||
|
/// <summary>Example property that will be reported to have changed</summary>
|
||||||
|
public int SomeProperty { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // struct TestValueType
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the name of a property accessed in a lambda expression
|
||||||
|
/// can be obtained.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void CanObtainPropertyNameFromLambdaExpression() {
|
||||||
|
string propertyName = ObservableHelper.GetPropertyName(
|
||||||
|
() => SomeReferenceType.SomeProperty
|
||||||
|
);
|
||||||
|
Assert.AreEqual("SomeProperty", propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that the name of a property assigned in a lambda expression
|
||||||
|
/// can be obtained.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void CanObtainPropertyNameFromBoxedLambdaExpression() {
|
||||||
|
string propertyName = ObservableHelper.GetPropertyName(
|
||||||
|
() => (object)(SomeValueType.SomeProperty)
|
||||||
|
);
|
||||||
|
Assert.AreEqual("SomeProperty", propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Helper used to construct lambda expressions</summary>
|
||||||
|
protected static TestReferenceType SomeReferenceType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Helper used to construct lambda expressions</summary>
|
||||||
|
protected static TestValueType SomeValueType { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UNITTEST
|
70
Source/ObservableHelper.cs
Normal file
70
Source/ObservableHelper.cs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#region CPL License
|
||||||
|
/*
|
||||||
|
Nuclex Framework
|
||||||
|
Copyright (C) 2002-2012 Nuclex Development Labs
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the IBM Common Public License as
|
||||||
|
published by the IBM Corporation; either version 1.0 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
IBM Common Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the IBM Common Public
|
||||||
|
License along with this library
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
#if !NO_LINQ_EXPRESSIONS
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Nuclex.Support {
|
||||||
|
|
||||||
|
/// <summary>Contains helper methods for observing property changed</summary>
|
||||||
|
public static class ObservableHelper {
|
||||||
|
|
||||||
|
#if !NO_LINQ_EXPRESSIONS
|
||||||
|
/// <summary>Obtains the name of a property from a lambda expression</summary>
|
||||||
|
/// <param name="property">
|
||||||
|
/// Lambda expression for the property whose name will be returned
|
||||||
|
/// </param>
|
||||||
|
/// <returns>The name of the property contained in the lamba expression</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// This method obtains the textual name of a property specified in a lambda
|
||||||
|
/// expression. By going through a lambda expression, the property will be
|
||||||
|
/// stated as actual code, allowing F2 refactoring to correctly update any
|
||||||
|
/// references to the property when it is renamed.
|
||||||
|
/// </para>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// string propertyName = ObservableHelper.GetPropertyName(() => SomeValue);
|
||||||
|
/// Assert.AreEqual("SomeValue", propertyName);
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </remarks>
|
||||||
|
public static string GetPropertyName<TValue>(Expression<Func<TValue>> property) {
|
||||||
|
var lambda = (LambdaExpression)property;
|
||||||
|
|
||||||
|
MemberExpression memberExpression;
|
||||||
|
{
|
||||||
|
var unaryExpression = lambda.Body as UnaryExpression;
|
||||||
|
if(unaryExpression != null) {
|
||||||
|
memberExpression = (MemberExpression)unaryExpression.Operand;
|
||||||
|
} else {
|
||||||
|
memberExpression = (MemberExpression)lambda.Body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return memberExpression.Member.Name;
|
||||||
|
}
|
||||||
|
#endif // !NO_LINQ_EXPRESSIONS
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support
|
117
Source/PropertyChangedEventArgsHelper.Test.cs
Normal file
117
Source/PropertyChangedEventArgsHelper.Test.cs
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
#region CPL License
|
||||||
|
/*
|
||||||
|
Nuclex Framework
|
||||||
|
Copyright (C) 2002-2012 Nuclex Development Labs
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the IBM Common Public License as
|
||||||
|
published by the IBM Corporation; either version 1.0 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
IBM Common Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the IBM Common Public
|
||||||
|
License along with this library
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#if UNITTEST
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Nuclex.Support {
|
||||||
|
|
||||||
|
/// <summary>Unit tests for the property change event argument helper</summary>
|
||||||
|
[TestFixture]
|
||||||
|
internal class PropertyChangedEventArgsHelperTest {
|
||||||
|
|
||||||
|
#region class TestViewModel
|
||||||
|
|
||||||
|
/// <summary>Example class on which unit test generates change notifications</summary>
|
||||||
|
public class TestViewModel {
|
||||||
|
|
||||||
|
/// <summary>Example property that will be reported to have changed</summary>
|
||||||
|
public int SomeProperty { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // class TestViewModel
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that a property change notification matching the property
|
||||||
|
/// passed to the AreAffecting() method is recognized
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void MatchingPropertyChangeNotificationIsRecognized() {
|
||||||
|
var arguments = new PropertyChangedEventArgs("SomeProperty");
|
||||||
|
Assert.IsTrue(arguments.AreAffecting(() => ViewModel.SomeProperty));
|
||||||
|
Assert.IsTrue(arguments.AreAffecting("SomeProperty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that a mismatching property change notification will
|
||||||
|
/// not report the property as being affected.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void MismatchingPropertyIsReportedAsUnaffected() {
|
||||||
|
var arguments = new PropertyChangedEventArgs("AnotherProperty");
|
||||||
|
Assert.IsFalse(arguments.AreAffecting(() => ViewModel.SomeProperty));
|
||||||
|
Assert.IsFalse(arguments.AreAffecting("SomeProperty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that any specific property is reported as being affected
|
||||||
|
/// when the property change notification is a null wildcard
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SpecificPropertyIsAffectedByNullWildcard() {
|
||||||
|
var nullArguments = new PropertyChangedEventArgs(null);
|
||||||
|
Assert.IsTrue(nullArguments.AreAffecting(() => ViewModel.SomeProperty));
|
||||||
|
Assert.IsTrue(nullArguments.AreAffecting("SomeProperty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that any specific property is reported as being affected
|
||||||
|
/// when the property change notification is an empty wildcard
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SpecificPropertyIsAffectedByEmptyWildcard() {
|
||||||
|
var emptyArguments = new PropertyChangedEventArgs(string.Empty);
|
||||||
|
Assert.IsTrue(emptyArguments.AreAffecting(() => ViewModel.SomeProperty));
|
||||||
|
Assert.IsTrue(emptyArguments.AreAffecting("SomeProperty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the helper can recognize a wildcard property change
|
||||||
|
/// notification using null as the wildcard.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void NullWildcardIsRecognized() {
|
||||||
|
var nullArguments = new PropertyChangedEventArgs(null);
|
||||||
|
Assert.IsTrue(nullArguments.AffectAllProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the helper can recognize a wildcard property change
|
||||||
|
/// notification using an empty string as the wildcard.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void EmptyWildcardIsRecognized() {
|
||||||
|
var emptyArguments = new PropertyChangedEventArgs(string.Empty);
|
||||||
|
Assert.IsTrue(emptyArguments.AffectAllProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Helper used to construct lambda expressions</summary>
|
||||||
|
protected static TestViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support
|
||||||
|
|
||||||
|
#endif // UNITTEST
|
256
Source/PropertyChangedEventArgsHelper.cs
Normal file
256
Source/PropertyChangedEventArgsHelper.cs
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
#region CPL License
|
||||||
|
/*
|
||||||
|
Nuclex Framework
|
||||||
|
Copyright (C) 2002-2012 Nuclex Development Labs
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the IBM Common Public License as
|
||||||
|
published by the IBM Corporation; either version 1.0 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
IBM Common Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the IBM Common Public
|
||||||
|
License along with this library
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
#if NO_CONCURRENT_COLLECTIONS
|
||||||
|
using System.Collections.Generic;
|
||||||
|
#else
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
#endif
|
||||||
|
using System.ComponentModel;
|
||||||
|
#if !NO_LINQ_EXPRESSIONS
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Nuclex.Support {
|
||||||
|
|
||||||
|
/// <summary>Contains helper methods for property change notifications</summary>
|
||||||
|
public static class PropertyChangedEventArgsHelper {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A property change event argument container that indicates that all
|
||||||
|
/// properties have changed their value.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly PropertyChangedEventArgs Wildcard =
|
||||||
|
new PropertyChangedEventArgs(null);
|
||||||
|
|
||||||
|
/// <summary>Initializes a new property changed argument helper</summary>
|
||||||
|
static PropertyChangedEventArgsHelper() {
|
||||||
|
#if NO_CONCURRENT_COLLECTIONS
|
||||||
|
cache = new Dictionary<string, PropertyChangedEventArgs>();
|
||||||
|
#else
|
||||||
|
cache = new ConcurrentDictionary<string, PropertyChangedEventArgs>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !NO_LINQ_EXPRESSIONS
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a property change argument container for the specified property
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="property">
|
||||||
|
/// Property for which an event argument container will be provided
|
||||||
|
/// </param>
|
||||||
|
/// <returns>The event argument container for a property of the specified name</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// This method transparently caches instances of the argument containers
|
||||||
|
/// to avoid feeding the garbage collector. A typical application only has
|
||||||
|
/// in the order of tens to hundreds of different properties for which changes
|
||||||
|
/// will be reported, making a cache to avoid garbage collections viable.
|
||||||
|
/// </para>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// PropertyChangedEventArgs arguments =
|
||||||
|
/// PropertyChangedEventArgsHelper.GetArgumentsFor(() => SomeProperty);
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </remarks>
|
||||||
|
public static PropertyChangedEventArgs GetArgumentsFor<TValue>(
|
||||||
|
Expression<Func<TValue>> property
|
||||||
|
) {
|
||||||
|
return GetArgumentsFor(ObservableHelper.GetPropertyName(property));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a property change argument container for the specified property
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyName">
|
||||||
|
/// Property for which an event argument container will be provided
|
||||||
|
/// </param>
|
||||||
|
/// <returns>The event argument container for a property of the specified name</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// This method transparently caches instances of the argument containers
|
||||||
|
/// to avoid feeding the garbage collector. A typical application only has
|
||||||
|
/// in the order of tens to hundreds of different properties for which changes
|
||||||
|
/// will be reported, making a cache to avoid garbage collections viable.
|
||||||
|
/// </para>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// PropertyChangedEventArgs arguments =
|
||||||
|
/// PropertyChangedEventArgsHelper.GetArgumentsFor("SomeProperty");
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </remarks>
|
||||||
|
public static PropertyChangedEventArgs GetArgumentsFor(string propertyName) {
|
||||||
|
if(string.IsNullOrEmpty(propertyName)) {
|
||||||
|
return Wildcard;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NO_CONCURRENT_COLLECTIONS
|
||||||
|
lock(cache) {
|
||||||
|
// Try to reuse the change notification if an instance already exists
|
||||||
|
PropertyChangedEventArgs arguments;
|
||||||
|
if(!cache.TryGetValue(propertyName, out arguments)) {
|
||||||
|
arguments = new PropertyChangedEventArgs(propertyName);
|
||||||
|
cache.Add(propertyName, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// If an instance for this property already exists, just return it
|
||||||
|
PropertyChangedEventArgs arguments;
|
||||||
|
if(cache.TryGetValue(propertyName, out arguments)) {
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No instance existed (at least a short moment ago), so create a new one
|
||||||
|
return cache.GetOrAdd(propertyName, new PropertyChangedEventArgs(propertyName));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !NO_LINQ_EXPRESSIONS
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the property change affects the specified property
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TValue">
|
||||||
|
/// Type of the property that will be tested for being affected
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="arguments">
|
||||||
|
/// Property change that has been reported by the observed object
|
||||||
|
/// </param>
|
||||||
|
/// <param name="property">Property that will be tested for being affected</param>
|
||||||
|
/// <returns>Whether the specified property is affected by the property change</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// By using this method, you can shorten the code needed to test whether
|
||||||
|
/// a property change notification affects a specific property. You also
|
||||||
|
/// avoid hardcoding the property name, which would have the adverse effect
|
||||||
|
/// of not updating the textual property names during F2 refactoring.
|
||||||
|
/// </para>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// private void propertyChanged(object sender, PropertyChangedEventArgs arguments) {
|
||||||
|
/// if(arguments.AreAffecting(() => ViewModel.DisplayedValue)) {
|
||||||
|
/// updateDisplayedValueFromViewModel();
|
||||||
|
/// } // Do not use else if here or wildcards will not work
|
||||||
|
/// if(arguments.AreAffecting(() => ViewModel.OtherValue)) {
|
||||||
|
/// updateOtherValueFromViewModel();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </remarks>
|
||||||
|
public static bool AreAffecting<TValue>(
|
||||||
|
this PropertyChangedEventArgs arguments, Expression<Func<TValue>> property
|
||||||
|
) {
|
||||||
|
if(arguments.AffectAllProperties()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string propertyName = ObservableHelper.GetPropertyName(property);
|
||||||
|
return (arguments.PropertyName == propertyName);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the property change affects the specified property
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TValue">
|
||||||
|
/// Type of the property that will be tested for being affected
|
||||||
|
/// </typeparam>
|
||||||
|
/// <param name="arguments">
|
||||||
|
/// Property change that has been reported by the observed object
|
||||||
|
/// </param>
|
||||||
|
/// <param name="propertyName">Property that will be tested for being affected</param>
|
||||||
|
/// <returns>Whether the specified property is affected by the property change</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// By using this method, you can shorten the code needed to test whether
|
||||||
|
/// a property change notification affects a specific property.
|
||||||
|
/// </para>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// private void propertyChanged(object sender, PropertyChangedEventArgs arguments) {
|
||||||
|
/// if(arguments.AreAffecting("DisplayedValue")) {
|
||||||
|
/// updateDisplayedValueFromViewModel();
|
||||||
|
/// } // Do not use else if here or wildcards will not work
|
||||||
|
/// if(arguments.AreAffecting("OtherValue")) {
|
||||||
|
/// updateOtherValueFromViewModel();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </remarks>
|
||||||
|
public static bool AreAffecting(
|
||||||
|
this PropertyChangedEventArgs arguments, string propertyName
|
||||||
|
) {
|
||||||
|
if(arguments.AffectAllProperties()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (arguments.PropertyName == propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether a property change notification is a wildcard</summary>
|
||||||
|
/// <param name="arguments">
|
||||||
|
/// Property change notification that will be checked on being a wildcard
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// Whether the property change is a wildcard, indicating that all properties
|
||||||
|
/// have changed.
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// As stated on MSDN: "The PropertyChanged event can indicate all properties
|
||||||
|
/// on the object have changed by using either Nothing or String.Empty as
|
||||||
|
/// the property name in the PropertyChangedEventArgs."
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// This method offers an expressive way of checking for that eventuality.
|
||||||
|
/// </para>
|
||||||
|
/// <example>
|
||||||
|
/// <code>
|
||||||
|
/// private void propertyChanged(object sender, PropertyChangedEventArgs arguments) {
|
||||||
|
/// if(arguments.AffectAllProperties()) {
|
||||||
|
/// // Do something
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </remarks>
|
||||||
|
public static bool AffectAllProperties(this PropertyChangedEventArgs arguments) {
|
||||||
|
return string.IsNullOrEmpty(arguments.PropertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Caches PropertyChangedEventArgs instances to avoid feeding the garbage collector
|
||||||
|
/// </summary>
|
||||||
|
#if NO_CONCURRENT_COLLECTIONS
|
||||||
|
private static readonly Dictionary<string, PropertyChangedEventArgs> cache;
|
||||||
|
#else
|
||||||
|
private static readonly ConcurrentDictionary<string, PropertyChangedEventArgs> cache;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support
|
Loading…
Reference in New Issue
Block a user