diff --git a/.gitignore b/.gitignore index 28c41a3..9c455c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,322 @@ -*\bin* -*\obj* +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files *.suo -packages -*/obj/* \ No newline at end of file +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# TypeScript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog diff --git a/.nuget/NuGet.Config b/.nuget/NuGet.Config index 67f8ea0..3343d68 100644 --- a/.nuget/NuGet.Config +++ b/.nuget/NuGet.Config @@ -1,4 +1,5 @@  + diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe deleted file mode 100644 index 8dd7e45..0000000 Binary files a/.nuget/NuGet.exe and /dev/null differ diff --git a/AlphaChiTech.Virtualization.Net4/AlphaChiTech.Virtualization.Net4.csproj b/AlphaChiTech.Virtualization.Net4/AlphaChiTech.Virtualization.Net4.csproj deleted file mode 100644 index f3e6c80..0000000 --- a/AlphaChiTech.Virtualization.Net4/AlphaChiTech.Virtualization.Net4.csproj +++ /dev/null @@ -1,116 +0,0 @@ - - - - - Debug - AnyCPU - {ECEDC613-843A-4D9A-8123-BD9600C9118E} - Library - Properties - AlphaChiTech.Virtualization - AlphaChiTech.Virtualization - v4.0 - 512 - - - ..\ - true - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - 4 - - - - - - - ..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll - True - - - - ..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - \ No newline at end of file diff --git a/AlphaChiTech.Virtualization.Net4/AlphaChiTech.Virtualization.Net4.csproj.DotSettings b/AlphaChiTech.Virtualization.Net4/AlphaChiTech.Virtualization.Net4.csproj.DotSettings deleted file mode 100644 index 662f956..0000000 --- a/AlphaChiTech.Virtualization.Net4/AlphaChiTech.Virtualization.Net4.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp50 \ No newline at end of file diff --git a/AlphaChiTech.Virtualization.Net4/BasePagedSourceProvider.cs b/AlphaChiTech.Virtualization.Net4/BasePagedSourceProvider.cs deleted file mode 100644 index af27879..0000000 --- a/AlphaChiTech.Virtualization.Net4/BasePagedSourceProvider.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public class BasePagedSourceProvider : IPagedSourceProvider - { - public BasePagedSourceProvider() - { - } - - public BasePagedSourceProvider( - Func> funcGetItemsAt = null, - Func funcGetCount = null, - Func funcIndexOf = null, - Action actionOnReset = null - ) - { - this.FuncGetItemsAt = funcGetItemsAt; - this.FuncGetCount = funcGetCount; - this.FuncIndexOf = funcIndexOf; - this.ActionOnReset = actionOnReset; - } - - private Func> _FuncGetItemsAt = null; - - public Func> FuncGetItemsAt - { - get { return _FuncGetItemsAt; } - set { _FuncGetItemsAt = value; } - } - private Func _FuncGetCount = null; - - public Func FuncGetCount - { - get { return _FuncGetCount; } - set { _FuncGetCount = value; } - } - private Func _FuncIndexOf = null; - - public Func FuncIndexOf - { - get { return _FuncIndexOf; } - set { _FuncIndexOf = value; } - } - private Action _ActionOnReset = null; - - public Action ActionOnReset - { - get { return _ActionOnReset; } - set { _ActionOnReset = value; } - } - - public virtual PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder) - { - if (_FuncGetItemsAt != null) return _FuncGetItemsAt.Invoke(pageoffset, count); - - return null; - } - - public virtual int Count - { - get - { - int ret = 0; - - if (_FuncGetCount != null) ret = _FuncGetCount.Invoke(); - - return ret; - } - } - - public virtual int IndexOf(T item) - { - int ret = -1; - - if (_FuncIndexOf != null) ret = _FuncIndexOf.Invoke(item); - - return ret; - } - - public virtual void OnReset(int count) - { - if (_ActionOnReset != null) _ActionOnReset.Invoke(count); - } - } -} diff --git a/AlphaChiTech.Virtualization.Net4/BaseRepeatableActionVirtualization.cs b/AlphaChiTech.Virtualization.Net4/BaseRepeatableActionVirtualization.cs deleted file mode 100644 index f30e0c1..0000000 --- a/AlphaChiTech.Virtualization.Net4/BaseRepeatableActionVirtualization.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - /// - /// Base class there the Action repeats on a periodic basis (the RepeatingSchedule) like BaseActionVirtualization - /// simply implement the DoAction method. - /// - public abstract class BaseRepeatableActionVirtualization : BaseActionVirtualization, IRepeatingVirtualizationAction - { - protected DateTime _LastRun = DateTime.MinValue; - private TimeSpan _RepeatingSchedule = TimeSpan.FromSeconds(1); - - /// - /// Initializes a new instance of the class. - /// - /// The thread model. - /// if set to true [is repeating]. - /// The repeating schedule. - public BaseRepeatableActionVirtualization(VirtualActionThreadModelEnum threadModel = VirtualActionThreadModelEnum.UseUIThread, - bool isRepeating = false, TimeSpan? repeatingSchedule = null) - : base(threadModel) - { - this.IsRepeating = isRepeating; - if (repeatingSchedule.HasValue) - { - this.RepeatingSchedule = repeatingSchedule.Value; - } - } - - /// - /// Gets or sets the repeating schedule. - /// - /// - /// The repeating schedule. - /// - public TimeSpan RepeatingSchedule - { - get { return _RepeatingSchedule; } - set { _RepeatingSchedule = value; } - } - - private bool _IsRepeating = false; - - /// - /// Gets or sets a value indicating whether [is repeating]. - /// - /// - /// true if [is repeating]; otherwise, false. - /// - protected bool IsRepeating - { - get { return _IsRepeating; } - set { _IsRepeating = value; } - } - - /// - /// check to see if the action should be kept. - /// - /// - public virtual bool KeepInActionsList() - { - return this.IsRepeating; - } - - /// - /// Determines whether [is due to run]. - /// - /// - public virtual bool IsDueToRun() - { - - if (DateTime.Now >= _LastRun.Add(this.RepeatingSchedule)) - { - return true; - } - - return false; - } - - } - -} diff --git a/AlphaChiTech.Virtualization.Net4/DateBasedPageExpiryComparer.cs b/AlphaChiTech.Virtualization.Net4/DateBasedPageExpiryComparer.cs deleted file mode 100644 index 76db637..0000000 --- a/AlphaChiTech.Virtualization.Net4/DateBasedPageExpiryComparer.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - /// - /// An implementation of a IPageExiryComparer that uses DateTime to see if the update should be applied - /// - public class DateBasedPageExpiryComparer : IPageExpiryComparer - { - - private static DateBasedPageExpiryComparer _Instance = new DateBasedPageExpiryComparer(); - - /// - /// Gets the default instance. - /// - /// - /// The default instance. - /// - public static DateBasedPageExpiryComparer DefaultInstance - { - get - { - return _Instance; - } - } - - /// - /// Determines whether [is update valid] [the specified page based on the updateAt]. - /// - /// The page update at - null or a DateTime. - /// The update at - null or a DateTime. - /// - public bool IsUpdateValid(object pageUpdateAt, object updateAt) - { - bool isStillValid = true; - - if (pageUpdateAt != null && updateAt != null && pageUpdateAt is DateTime && updateAt is DateTime) - { - if (((DateTime)pageUpdateAt) >= ((DateTime)updateAt)) - { - isStillValid = false; - } - } - - return isStillValid; - } - } -} diff --git a/AlphaChiTech.Virtualization.Net4/ExecuteResetWA.cs b/AlphaChiTech.Virtualization.Net4/ExecuteResetWA.cs deleted file mode 100644 index ddddfd2..0000000 --- a/AlphaChiTech.Virtualization.Net4/ExecuteResetWA.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class ExecuteResetWA : BaseActionVirtualization - { - WeakReference _VOC; - - public ExecuteResetWA(VirtualizingObservableCollection voc) - : base(VirtualActionThreadModelEnum.UseUIThread) - { - _VOC = new WeakReference(voc); - } - - public override void DoAction() - { - var voc = (VirtualizingObservableCollection)_VOC.Target; - - if (voc != null && _VOC.IsAlive) - { - voc.RaiseCollectionChangedEvent(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - } - - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IBaseSourceProvider.cs b/AlphaChiTech.Virtualization.Net4/IBaseSourceProvider.cs deleted file mode 100644 index 71482be..0000000 --- a/AlphaChiTech.Virtualization.Net4/IBaseSourceProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IBaseSourceProvider - { - void OnReset(int count); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IEditableProvider.cs b/AlphaChiTech.Virtualization.Net4/IEditableProvider.cs deleted file mode 100644 index 27312d0..0000000 --- a/AlphaChiTech.Virtualization.Net4/IEditableProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IEditableProvider - { - int OnAppend(T item, object timestamp); - void OnInsert(int index, T item, object timestamp); - void OnRemove(int index, T item, object timestamp); - void OnReplace(int index, T oldItem, T newItem, object timestamp); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IItemSourceProvider.cs b/AlphaChiTech.Virtualization.Net4/IItemSourceProvider.cs deleted file mode 100644 index a57b416..0000000 --- a/AlphaChiTech.Virtualization.Net4/IItemSourceProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IItemSourceProvider : IBaseSourceProvider - { - T GetAt(int index, object voc, bool usePlaceholder); - int GetCount(bool asyncOK); - - int IndexOf(T item); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IItemSourceProviderAsync.cs b/AlphaChiTech.Virtualization.Net4/IItemSourceProviderAsync.cs deleted file mode 100644 index c9358eb..0000000 --- a/AlphaChiTech.Virtualization.Net4/IItemSourceProviderAsync.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public interface IItemSourceProviderAsync : IBaseSourceProvider - { - Task GetAt(int index, object voc, bool usePlaceholder); - - T GetPlaceHolder(int index); - - Task Count { get; } - - Task IndexOf(T item); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/INotifyCountChanged.cs b/AlphaChiTech.Virtualization.Net4/INotifyCountChanged.cs deleted file mode 100644 index c7f0715..0000000 --- a/AlphaChiTech.Virtualization.Net4/INotifyCountChanged.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public delegate void OnCountChanged(object sender, CountChangedEventArgs args); - - public class CountChangedEventArgs : EventArgs - { - public bool NeedsReset { get; set; } - public int Count { get; set; } - } - - public interface INotifyCountChanged - { - event OnCountChanged CountChanged; - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IPageExpiryComparer.cs b/AlphaChiTech.Virtualization.Net4/IPageExpiryComparer.cs deleted file mode 100644 index 860df8c..0000000 --- a/AlphaChiTech.Virtualization.Net4/IPageExpiryComparer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IPageExpiryComparer - { - bool IsUpdateValid(object pageUpdateAt, object updateAt); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IPageReclaimer.cs b/AlphaChiTech.Virtualization.Net4/IPageReclaimer.cs deleted file mode 100644 index 9df1099..0000000 --- a/AlphaChiTech.Virtualization.Net4/IPageReclaimer.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IPageReclaimer - { - /// - /// Reclaims the pages. - /// - /// The pages. - /// The pages needed. - /// - IEnumerable> ReclaimPages(IEnumerable> pages, int pagesNeeded, string sectionContext); - - /// - /// Called when [page touched]. - /// - /// The page. - void OnPageTouched(ISourcePage page); - - /// - /// Called when [page released]. - /// - /// The page. - void OnPageReleased(ISourcePage page); - - /// - /// Makes the page. - /// - /// The page. - /// The size. - /// - ISourcePage MakePage(int page, int size); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IPagedSourceProvider.cs b/AlphaChiTech.Virtualization.Net4/IPagedSourceProvider.cs deleted file mode 100644 index 722d037..0000000 --- a/AlphaChiTech.Virtualization.Net4/IPagedSourceProvider.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IPagedSourceProvider : IBaseSourceProvider - { - PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder); - - int Count { get; } - - int IndexOf(T item); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IPagedSourceProviderAsync.cs b/AlphaChiTech.Virtualization.Net4/IPagedSourceProviderAsync.cs deleted file mode 100644 index f85e216..0000000 --- a/AlphaChiTech.Virtualization.Net4/IPagedSourceProviderAsync.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public interface IPagedSourceProviderAsync : IPagedSourceProvider - { - Task> GetItemsAtAsync(int pageoffset, int count, bool usePlaceholder); - - T GetPlaceHolder(int index, int page, int offset); - - Task GetCountAsync(); - - Task IndexOfAsync( T item ); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IReclaimableService.cs b/AlphaChiTech.Virtualization.Net4/IReclaimableService.cs deleted file mode 100644 index ec7ca47..0000000 --- a/AlphaChiTech.Virtualization.Net4/IReclaimableService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IReclaimableService - { - void RunClaim(string sectionContext); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IRepeatingVirtualizationAction.cs b/AlphaChiTech.Virtualization.Net4/IRepeatingVirtualizationAction.cs deleted file mode 100644 index a97421e..0000000 --- a/AlphaChiTech.Virtualization.Net4/IRepeatingVirtualizationAction.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IRepeatingVirtualizationAction - { - bool KeepInActionsList(); - bool IsDueToRun(); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/IVirtualizationAction.cs b/AlphaChiTech.Virtualization.Net4/IVirtualizationAction.cs deleted file mode 100644 index 3443d49..0000000 --- a/AlphaChiTech.Virtualization.Net4/IVirtualizationAction.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IVirtualizationAction - { - VirtualActionThreadModelEnum ThreadModel { get; } - - void DoAction(); - } -} diff --git a/AlphaChiTech.Virtualization.Net4/PageDelta.cs b/AlphaChiTech.Virtualization.Net4/PageDelta.cs deleted file mode 100644 index 0aafbc3..0000000 --- a/AlphaChiTech.Virtualization.Net4/PageDelta.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - internal class PageDelta - { - public int Page { get; set; } - public int Delta { get; set; } - } -} diff --git a/AlphaChiTech.Virtualization.Net4/PageFetchStateEnum.cs b/AlphaChiTech.Virtualization.Net4/PageFetchStateEnum.cs deleted file mode 100644 index 280f995..0000000 --- a/AlphaChiTech.Virtualization.Net4/PageFetchStateEnum.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public enum PageFetchStateEnum - { - Fetched, - Placeholders - } -} diff --git a/AlphaChiTech.Virtualization.Net4/PagedSourceItemsPacket.cs b/AlphaChiTech.Virtualization.Net4/PagedSourceItemsPacket.cs deleted file mode 100644 index 5df2f1d..0000000 --- a/AlphaChiTech.Virtualization.Net4/PagedSourceItemsPacket.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class PagedSourceItemsPacket - { - public IEnumerable Items { get; set; } - private Object _LoadedAt = DateTime.Now; - - public Object LoadedAt - { - get { return _LoadedAt; } - set { _LoadedAt = value; } - } - - } -} diff --git a/AlphaChiTech.Virtualization.Net4/PagedSourceProviderMakeAsync.cs b/AlphaChiTech.Virtualization.Net4/PagedSourceProviderMakeAsync.cs deleted file mode 100644 index c178c23..0000000 --- a/AlphaChiTech.Virtualization.Net4/PagedSourceProviderMakeAsync.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public class PagedSourceProviderMakeAsync : BasePagedSourceProvider, IPagedSourceProviderAsync, IProviderPreReset - { - private Func> _FuncIndexOfAsync = null; - private Func _FuncGetPlaceHolder = null; - - public PagedSourceProviderMakeAsync() - { - } - - public PagedSourceProviderMakeAsync( - Func> funcGetItemsAt = null, - Func funcGetCount = null, - Func> funcIndexOfAsync = null, - Action actionOnReset = null, - Func funcGetPlaceHolder = null, - Action actionOnBeforeReset = null - ) - : base(funcGetItemsAt, funcGetCount, null, actionOnReset) - //: base(funcGetItemsAt, funcGetCount, funcIndexOf, actionOnReset) - { - this.FuncGetPlaceHolder = funcGetPlaceHolder; - this.ActionOnBeforeReset = actionOnBeforeReset; - _FuncIndexOfAsync = funcIndexOfAsync; - } - - public virtual void OnBeforeReset() - { - if (this.ActionOnBeforeReset != null) - { - this.ActionOnBeforeReset.Invoke(); - } - } - - Action _ActionOnBeforeReset = null; - - public Action ActionOnBeforeReset - { - get { return _ActionOnBeforeReset; } - set { _ActionOnBeforeReset = value; } - } - - public Func FuncGetPlaceHolder - { - get { return _FuncGetPlaceHolder; } - set { _FuncGetPlaceHolder = value; } - } - - public Func> FuncIndexOfAsync - { - get { return _FuncIndexOfAsync; } - set { _FuncIndexOfAsync = value; } - } - - public Task> GetItemsAtAsync(int pageoffset, int count, bool usePlaceholder) - { - var tcs = new TaskCompletionSource>(); - - try - { - tcs.SetResult(this.GetItemsAt(pageoffset, count, usePlaceholder)); - } - catch (Exception e) - { - tcs.SetException(e); - } - - return tcs.Task; - } - - public virtual T GetPlaceHolder(int index, int page, int offset) - { - T ret = default(T); - - if (_FuncGetPlaceHolder != null) - ret = _FuncGetPlaceHolder.Invoke(index, page, offset); - - return ret; - } - - public Task GetCountAsync() - { - var tcs = new TaskCompletionSource(); - - try - { - tcs.SetResult(this.Count); - } catch(Exception e) - { - tcs.SetException(e); - } - - return tcs.Task; - } - - public Task IndexOfAsync( T item ) - { - var ret = default( Task ); - - if (_FuncIndexOfAsync != null) - ret = _FuncIndexOfAsync.Invoke( item ); - - return ret; - } - } -} diff --git a/AlphaChiTech.Virtualization.Net4/PagedSourceProviderMakeSync.cs b/AlphaChiTech.Virtualization.Net4/PagedSourceProviderMakeSync.cs deleted file mode 100644 index ddac838..0000000 --- a/AlphaChiTech.Virtualization.Net4/PagedSourceProviderMakeSync.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public class PagedSourceProviderMakeSync : IPagedSourceProviderAsync, IProviderPreReset - { - public PagedSourceProviderMakeSync() - { - } - - public PagedSourceProviderMakeSync( - Func>> funcGetItemsAtAsync = null, - Func> funcGetCountAsync = null, - Func funcIndexOf = null, - Func> funcIndexOfAsync = null, - Action actionOnReset = null, - Func funcGetPlaceHolder = null, - Action actionOnBeforeReset = null - ) - { - this.FuncGetItemsAtAsync = funcGetItemsAtAsync; - this.FuncGetCountAsync = funcGetCountAsync; - this.FuncIndexOf = funcIndexOf; - this.FuncIndexOfAsync = funcIndexOfAsync; - this.ActionOnReset = actionOnReset; - this.FuncGetPlaceHolder = funcGetPlaceHolder; - this.ActionOnBeforeReset = actionOnBeforeReset; - } - - public virtual void OnBeforeReset() - { - if(this.ActionOnBeforeReset != null) - { - this.ActionOnBeforeReset.Invoke(); - } - } - - Action _ActionOnBeforeReset = null; - - public Action ActionOnBeforeReset - { - get { return _ActionOnBeforeReset; } - set { _ActionOnBeforeReset = value; } - } - - - Func> _FuncIndexOfAsync = null; - - public Func> FuncIndexOfAsync - { - get { return _FuncIndexOfAsync; } - set { _FuncIndexOfAsync = value; } - } - - private Func>> _FuncGetItemsAtAsync = null; - - public Func>> FuncGetItemsAtAsync - { - get { return _FuncGetItemsAtAsync; } - set { _FuncGetItemsAtAsync = value; } - } - - private Func _FuncGetPlaceHolder = null; - - public Func FuncGetPlaceHolder - { - get { return _FuncGetPlaceHolder; } - set { _FuncGetPlaceHolder = value; } - } - - private Func> _FuncGetCountAsync = null; - - public Func> FuncGetCountAsync - { - get { return _FuncGetCountAsync; } - set { _FuncGetCountAsync = value; } - } - - private Func _FuncIndexOf = null; - - public Func FuncIndexOf - { - get { return _FuncIndexOf; } - set { _FuncIndexOf = value; } - } - - private Action _ActionOnReset = null; - - public Action ActionOnReset - { - get { return _ActionOnReset; } - set { _ActionOnReset = value; } - } - - public virtual Task> GetItemsAtAsync(int pageoffset, int count, bool usePlaceholder) - { - if (_FuncGetItemsAtAsync != null) - return _FuncGetItemsAtAsync.Invoke(pageoffset, count); - - return null; - } - - public virtual T GetPlaceHolder(int index, int page, int offset) - { - T ret = default(T); - - if (_FuncGetPlaceHolder != null) ret = _FuncGetPlaceHolder.Invoke(index, page, offset); - - return ret; - } - - public virtual Task GetCountAsync() - { - Task ret = null; - - if (_FuncGetCountAsync != null) - { - ret = _FuncGetCountAsync.Invoke(); - } - - return ret; - } - - public PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder) - { - return Task.Factory.Run(() => GetItemsAtAsync(pageoffset, count, usePlaceholder)).Result; - - } - - public int Count - { - get { return Task.Factory.Run(() => GetCountAsync()).Result; } - } - - public virtual int IndexOf(T item) - { - int ret = -1; - - if (_FuncIndexOf != null) - { - ret = _FuncIndexOf.Invoke(item); - } - else if (_FuncIndexOfAsync != null) - { - ret = Task.Factory.Run( () => _FuncIndexOfAsync.Invoke(item)).Result; - } - else - { - ret = Task.Factory.Run(() => IndexOfAsync(item)).Result; - } - - return ret; - } - - public virtual Task IndexOfAsync(T item) - { - int ret = -1; - - return Task.Factory.Run(() => ret); - } - - public virtual void OnReset(int count) - { - if (_ActionOnReset != null) _ActionOnReset.Invoke(count); - } - } -} diff --git a/AlphaChiTech.Virtualization.Net4/PaginationManager.cs b/AlphaChiTech.Virtualization.Net4/PaginationManager.cs deleted file mode 100644 index 62b8d27..0000000 --- a/AlphaChiTech.Virtualization.Net4/PaginationManager.cs +++ /dev/null @@ -1,1374 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - - public interface IAsyncResetProvider - { - Task GetCountAsync(); - } - - public interface IProviderPreReset - { - void OnBeforeReset(); - } - - public class PaginationManager : IItemSourceProvider, IEditableProvider, IReclaimableService, IAsyncResetProvider, IProviderPreReset, INotifyCountChanged - { - Dictionary> _Pages = new Dictionary>(); - - Dictionary _Deltas = new Dictionary(); - - Dictionary _Tasks = new Dictionary(); - - IPageReclaimer _Reclaimer = null; - - IPageExpiryComparer _ExpiryComparer = null; - - bool _HasGotCount = false; - int _LocalCount = 0; - - public IPageExpiryComparer ExpiryComparer - { - get { return _ExpiryComparer; } - set { _ExpiryComparer = value; } - } - - protected void CancelPageRequest(int page) - { - lock (_PageLock) - { - if (_Tasks.ContainsKey(page)) - { - try - { - _Tasks[page].Cancel(); - } - catch (Exception) - { - - } - - try - { - _Tasks.Remove(page); - } - catch (Exception) - { - - } - } - } - } - - protected void CancelAllRequests() - { - lock (_PageLock) - { - var c = _Tasks.Values.ToList(); - foreach (var t in c) - { - try - { - t.Cancel(false); - } - catch (Exception) - { - - } - } - - _Tasks.Clear(); - } - } - - protected void RemovePageRequest(int page) - { - lock (_PageLock) - { - if (_Tasks.ContainsKey(page)) - { - try - { - _Tasks.Remove(page); - } - catch (Exception) - { - - } - } - } - } - - protected CancellationTokenSource StartPageRequest(int page) - { - CancellationTokenSource cts = new CancellationTokenSource(); - - - CancelPageRequest(page); - - lock (_PageLock) - { - if (!_Tasks.ContainsKey(page)) - { - _Tasks.Add(page, cts); - } - else - { - _Tasks[page] = cts; - } - } - - return cts; - } - - /// - /// Initializes a new instance of the class. - /// - /// The provider. - /// The reclaimer. - /// The expiry comparer. - /// Size of the page. - /// The maximum pages. - /// The maximum deltas. - /// The maximum distance. - /// The section context. - public PaginationManager(IPagedSourceProvider provider, - IPageReclaimer reclaimer = null, - IPageExpiryComparer expiryComparer = null, - int pageSize = 100, - int maxPages = 100, - int maxDeltas = -1, - int maxDistance = -1, - string sectionContext = "", - TimeSpan? delayBetweenReclaims = null) - { - this.PageSize = pageSize; - this.MaxPages = maxPages; - this.MaxDeltas = maxDeltas; - this.MaxDistance = maxDistance; - - if (provider is IPagedSourceProviderAsync) - { - this.ProviderAsync = (IPagedSourceProviderAsync)provider; - } - else - { - this.Provider = provider; - } - - if (reclaimer != null) - { - _Reclaimer = reclaimer; - } - else - { - _Reclaimer = new PageReclaimOnTouched(); - } - - this.ExpiryComparer = expiryComparer; - - VirtualizationManager.Instance.AddAction(new ReclaimPagesWA(this, sectionContext, delayBetweenReclaims)); - } - - - - /// - /// Adds the or update adjustment. - /// - /// The page. - /// The offset change. - public int AddOrUpdateAdjustment(int page, int offsetChange) - { - int ret = 0; - - lock (_PageLock) - { - if (!_Deltas.ContainsKey(page)) - { - if (this.MaxDeltas == -1 || _Deltas.Count < this.MaxDeltas) - { - ret = offsetChange; - _Deltas.Add(page, new PageDelta() { Page = page, Delta = offsetChange }); - } - else - { - DropAllDeltasAndPages(); - } - } - else - { - var adjustment = _Deltas[page]; - adjustment.Delta += offsetChange; - - if (adjustment.Delta == 0) - { - _Deltas.Remove(page); - } - - ret = adjustment.Delta; - } - } - - return ret; - } - - /// - /// Drops all deltas and pages. - /// - protected void DropAllDeltasAndPages() - { - lock (_PageLock) - { - _Deltas.Clear(); - _Pages.Clear(); - _BasePage = 0; - CancelAllRequests(); - } - } - - /// - /// Gets the provider as editable. - /// - /// - /// - protected IEditableProvider GetProviderAsEditable() - { - IEditableProvider ret = null; - - if (this.Provider != null) - { - ret = this.Provider as IEditableProvider; - - } - else - { - ret = this.ProviderAsync as IEditableProvider; - } - - return ret; - } - - // The _LastXXX are used for optimizations.. - //private int _LastIndex = -1; - //private int _LastPage = -1; - //private int _LastOffset = -1; - - /// - /// Clears the optimizations. - /// - protected void ClearOptimizations() - { - //_LastIndex = -1; - } - - /* Old implementation - - /// - /// Calculates the page and the offset from the index. - /// - /// The index. - /// The index adjustment. - /// The page. - /// The inneroffset. - protected void CalculateFromIndex(int index, int indexAdjustment, out int page, out int inneroffset, int adjustmentsAppliedToPages = -1) - { - if (adjustmentsAppliedToPages == -1 && 1==0) - { - // See if we can use some optimization.. aka its the same index as last time.. - if (_LastIndex != -1 && index == _LastIndex) - { - page = _LastPage; - inneroffset = _LastOffset; - return; - } - // See if we can use some optimization... aka its the next index from last.. - if (_LastIndex != -1 && index == _LastIndex + 1) - { - int basepageg = page = _LastPage; - - inneroffset = _LastOffset + 1; - - int items = this.PageSize; - if (_Deltas.ContainsKey(basepageg)) - { - - items += _Deltas[basepageg].Delta; - } - - - if (inneroffset >= items) - { - bool got = false; - inneroffset = 0; - basepageg = page = page + 1; - - while (!got) - { - int itemsg = this.PageSize; - if (_Deltas.ContainsKey(basepageg)) - { - - itemsg += _Deltas[basepageg].Delta; - } - - if (inneroffset < itemsg) got = true; - } - } - - _LastIndex = index; - _LastPage = page; - _LastOffset = inneroffset; - return; - } - } - - // First work out the base page from the index and the offset inside that page - int basepage = page = index / this.PageSize; - inneroffset = (index + indexAdjustment) - (page * this.PageSize); - - // We only need to do the rest if there have been modifications to the page sizes on pages (deltas) - if (_Deltas.Count > 0) - { - int adjustment = 0; - - lock (_PageLock) - { - // First, get the total adjustments for any pages BEFORE the current page.. - adjustment = (from d in _Deltas.Values where d.Page < basepage && d.Page > adjustmentsAppliedToPages select d.Delta).Sum(); - } - - // If we do have adjustments... - if (adjustment != 0) - { - // cull down the inner offset by the adjustments (so an extra item reduces the offset by one etc) - inneroffset -= adjustment; - - if (inneroffset < 0) - { - while (inneroffset < 0 && page >= 0) - { - page = --basepage; - var items = this.PageSize; - - if (_Deltas.ContainsKey(basepage)) - { - - items += _Deltas[basepage].Delta; - } - - - inneroffset = items + inneroffset; - } - - - // We should be on an earlier page, so recurse using the adjustments - //CalculateFromIndex(index - adjustment, adjustment, out page, out inneroffset, basepage); - } - else if (inneroffset >= this.PageSize) - { - // If the inneroffset seems to be on a later page, but we need to check to see if this page is expanded - if (!_Deltas.ContainsKey(basepage)) - { - // Its not expanded, so recurse in using the adjusted index - CalculateFromIndex(index - adjustment, 0, out page, out inneroffset, basepage); - } - else - { - var delta = _Deltas[basepage]; - // It is expanded, see if the expanded page contains this offset - if (inneroffset < (this.PageSize + delta.Delta)) - { - // Its just fine - } - else - { - // No it does not include this offset, so recurse in using the adjusted index - CalculateFromIndex(index + delta.Delta, 0, out page, out inneroffset, basepage); - } - } - } - } - else - { - // we dont have any earlier page adjustments, but we might have a short page, so check the offset is within range.. - PageDelta adjustmentForCurrentPage = null; - if (_Deltas.ContainsKey(basepage)) adjustmentForCurrentPage = _Deltas[basepage]; - if (adjustmentForCurrentPage != null && adjustmentForCurrentPage.Delta < 0) - { - if (inneroffset >= this.PageSize + adjustmentForCurrentPage.Delta) - { - // Recurse in using the adjustment for the current page to deal with the offset.. - //CalculateFromIndex(index - adjustmentForCurrentPage.Delta, 0, out page, out inneroffset); - - inneroffset += adjustmentForCurrentPage.Delta; - - while(inneroffset<0) - { - page = ++basepage; - - var items = this.PageSize; - - if (_Deltas.ContainsKey(basepage)) - { - - items += _Deltas[basepage].Delta; - } - - inneroffset += items; - } - } - } - } - } - - if (adjustmentsAppliedToPages == -1) - { - _LastIndex = index; - - _LastPage = basepage; - _LastOffset = inneroffset; - } - - } - */ - - int _BasePage = 0; - - protected void CalculateFromIndex(int index, out int page, out int inneroffset) - { - // First work out the base page from the index and the offset inside that page - int basepage = page = (index / this.PageSize) + _BasePage; - inneroffset = (index+(_BasePage*this.PageSize)) - (page * this.PageSize); - - // We only need to do the rest if there have been modifications to the page sizes on pages (deltas) - if (_Deltas.Count > 0) - { - // Get the adjustment BEFORE checking for a short page, because we are going to adjust for that first.. - int adjustment = 0; - - lock (_PageLock) - { - // First, get the total adjustments for any pages BEFORE the current page.. - adjustment = (from d in _Deltas.Values where d.Page < basepage select d.Delta).Sum(); - } - - // Now check to see if we are currently in a short page - in which case we need to adjust for that - if (_Deltas.ContainsKey(page)) - { - int delta = _Deltas[page].Delta; - - if(delta<0) - { - // In a short page, are we over the edge ? - if(inneroffset >= this.PageSize+delta) - { - int step = inneroffset-(this.PageSize+delta-1); - inneroffset -= step; - DoStepForward(ref page, ref inneroffset, step); - } - } - } - - // If we do have adjustments... - if (adjustment != 0) - { - if(adjustment>0) - { - // items have been added to earlier pages, so we need to step back - DoStepBackwards(ref page, ref inneroffset, adjustment); - } - else - { - // items have been removed from earlier pages, so we need to step forward - DoStepForward(ref page, ref inneroffset, Math.Abs(adjustment)); - } - } - - } - - } - - private int _StepToJumpThreashold = 10; - - public int StepToJumpThreashold - { - get { return _StepToJumpThreashold; } - set { _StepToJumpThreashold = value; } - } - - private void DoStepBackwards(ref int page, ref int offset, int stepAmount) - { - bool done = false; - int ignoreSteps = -1; - - while(!done) - { - - if (stepAmount > this.PageSize * StepToJumpThreashold && ignoreSteps <= 0) - { - int targetPage = page - stepAmount/this.PageSize; - int sourcePage = page; - var adj = (from a in _Deltas.Values where a.Page >= targetPage && a.Page <= sourcePage orderby a.Page select a); - if(adj.Count() == 0) - { - page = targetPage; - stepAmount -= (sourcePage - targetPage) * this.PageSize; - - if(stepAmount == 0) - { - done = true; - } - } else if(adj.Last().Page < page-2) - { - targetPage = adj.Last().Page + 1; - page = targetPage; - stepAmount -= (sourcePage - targetPage) * this.PageSize; - - if (stepAmount == 0) - { - done = true; - } - } - else - { - ignoreSteps = sourcePage - adj.Last().Page; - } - } - - if (!done) - { - int items = this.PageSize; - if (_Deltas.ContainsKey(page)) items += _Deltas[page].Delta; - if (offset - stepAmount < 0) - { - stepAmount -= (offset + 1); - page--; - items = this.PageSize; - if (_Deltas.ContainsKey(page)) items += _Deltas[page].Delta; - offset = items - 1; - } - else - { - offset -= stepAmount; - done = true; - } - - ignoreSteps--; - } - } - } - - private void DoStepForward(ref int page, ref int offset, int stepAmount) - { - bool done = false; - int ignoreSteps = -1; - - while(!done) - { - - if (stepAmount > this.PageSize * StepToJumpThreashold && ignoreSteps <= 0) - { - int targetPage = page + stepAmount / this.PageSize; - int sourcePage = page; - var adj = (from a in _Deltas.Values where a.Page <= targetPage && a.Page >= sourcePage orderby a.Page select a); - if (adj.Count() == 0) - { - page = targetPage; - stepAmount -= (targetPage - sourcePage) * this.PageSize; - - if (stepAmount == 0) - { - done = true; - } - } - else if (adj.Last().Page > page - 2) - { - targetPage = adj.Last().Page - 1; - page = targetPage; - stepAmount -= (targetPage - sourcePage) * this.PageSize; - - if (stepAmount == 0) - { - done = true; - } - } else - { - ignoreSteps = adj.Last().Page - sourcePage; - } - } - - if (!done) - { - int items = this.PageSize; - if (_Deltas.ContainsKey(page)) items += _Deltas[page].Delta; - if (items <= offset + stepAmount) - { - stepAmount -= (items) - offset; - offset = 0; - page++; - } - else - { - offset += stepAmount; - done = true; - } - - ignoreSteps--; - } - } - } - IPagedSourceProvider _Provider = null; - - /// - /// Gets or sets the provider. - /// - /// - /// The provider. - /// - public IPagedSourceProvider Provider - { - get { return _Provider; } - set { _Provider = value; } - } - - IPagedSourceProviderAsync _ProviderAsync = null; - - /// - /// Gets or sets the provider asynchronous. - /// - /// - /// The provider asynchronous. - /// - public IPagedSourceProviderAsync ProviderAsync - { - get { return _ProviderAsync; } - set { _ProviderAsync = value; } - } - - int _PageSize = 100; - - /// - /// Gets or sets the size of the page. - /// - /// - /// The size of the page. - /// - public int PageSize - { - get { return _PageSize; } - set - { - DropAllDeltasAndPages(); - _PageSize = value; - } - } - - int _MaxPages = 100; - - /// - /// Gets or sets the maximum pages. - /// - /// - /// The maximum pages. - /// - public int MaxPages - { - get { return _MaxPages; } - set { _MaxPages = value; } - } - - int _MaxDeltas = -1; - - /// - /// Gets or sets the maximum deltas. - /// - /// - /// The maximum deltas. - /// - public int MaxDeltas - { - get { return _MaxDeltas; } - set { _MaxDeltas = value; } - } - - int _MaxDistance = -1; - - /// - /// Gets or sets the maximum distance. - /// - /// - /// The maximum distance. - /// - public int MaxDistance - { - get { return _MaxDistance; } - set { _MaxDistance = value; } - } - - protected object _PageLock = new Object(); - - /// - /// Gets at. - /// - /// The index. - /// The voc. - /// if set to true [use placeholder]. - /// - public T GetAt(int index, object voc, bool usePlaceholder = true) - { - T ret = default(T); - - int page; - int offset; - - CalculateFromIndex(index, out page, out offset); - - var datapage = SafeGetPage(page, usePlaceholder, voc, index); - - if (datapage != null) ret = datapage.GetAt(offset); - - if (ret == null) - { - - } - - //Debug.WriteLine("Get at index:" + index + "returned:" + ret.ToString() + " page=" + page + " offset=" + offset); - return ret; - } - - /// - /// Fills the page. - /// - /// The new page. - /// The page offset. - void FillPage(ISourcePage newPage, int pageOffset) - { - - var data = this.Provider.GetItemsAt(pageOffset, newPage.ItemsPerPage, false); - if (data != null) - { - newPage.WiredDateTime = data.LoadedAt; - foreach (var o in data.Items) - { - newPage.Append(o, null, this.ExpiryComparer); - } - - newPage.PageFetchState = PageFetchStateEnum.Fetched; - } - } - - /// - /// Fills the page from asynchronous provider. - /// - /// The new page. - /// The page offset. - void FillPageFromAsyncProvider(ISourcePage newPage, int pageOffset) - { - var data = this.ProviderAsync.GetItemsAt(pageOffset, newPage.ItemsPerPage, false); - if (data != null) - { - newPage.WiredDateTime = data.LoadedAt; - foreach (var o in data.Items) - { - newPage.Append(o, null, this.ExpiryComparer); - } - newPage.PageFetchState = PageFetchStateEnum.Fetched; - } - } - - /// - /// Gets the count. - /// - /// - /// The count. - /// - public int GetCount(bool asyncOK) - { - - int ret = 0; - - if (!_HasGotCount) - { - lock (this) - { - if (!IsAsync) - { - ret = this.Provider.Count; - _LocalCount = ret; - } - else - { - if (!asyncOK) - { - ret = this.ProviderAsync.GetCountAsync().Result; - //ret = this.ProviderAsync.Count; - _LocalCount = ret; - } - else - { - ret = 0; - var cts = StartPageRequest(Int32.MinValue); - GetCountAsync(cts); - } - } - - _HasGotCount = true; - } - } - - return _LocalCount; - - } - - public Task GetCountAsync() - { - Task task; - if (!IsAsync) - { - task = Task.Factory.Run(() => this.Provider.Count); - task.ContinueWith(t => _HasGotCount = true); - } - else - { - task = this.ProviderAsync.GetCountAsync(); - task.ContinueWith(t => _HasGotCount = true); - } - return task; - } - - private Task GetCountAsync(CancellationTokenSource cts) - { - if (!cts.IsCancellationRequested) - { - return this.ProviderAsync.GetCountAsync() - .ContinueWith(t => - { - if (!cts.IsCancellationRequested) - { - lock (this) - { - _HasGotCount = true; - _LocalCount = t.Result; - } - } - - if (!cts.IsCancellationRequested) - this.RaiseCountChanged(true, _LocalCount); - RemovePageRequest(Int32.MinValue); - }, TaskScheduler.FromCurrentSynchronizationContext()); - } - else - { - return Task.Factory.StartNew(() => { }).ContinueWith(t => RemovePageRequest(Int32.MinValue)); - } - } - - /// - /// Gets the Index of item. - /// - /// The item. - /// the index of the item, or -1 if not found - public int IndexOf(T item) - { - // Attempt to get the item from the pages, else call the provider to get it.. - lock (_PageLock) - { - - foreach (var p in _Pages) - { - int o = p.Value.IndexOf(item); - if (o >= 0) - { - return o + ((p.Key - _BasePage) * this.PageSize) + (from d in _Deltas.Values where d.Page < p.Key select d.Delta).Sum(); - } - } - } - - if (!IsAsync) - { - return this.Provider.IndexOf(item); - } - else - { - return this.ProviderAsync.IndexOfAsync( item ).Result; - //return this.ProviderAsync.IndexOf(item); - } - } - - /// - /// Resets the specified count. - /// - /// The count. - public void OnReset(int count) - { - CancelAllRequests(); - - lock (_PageLock) - { - DropAllDeltasAndPages(); - } - - ClearOptimizations(); - - if (count < 0) - { - _HasGotCount = false; - } - else - { - lock (this) - { - _LocalCount = count; - _HasGotCount = true; - } - } - - if (!IsAsync) - { - this.Provider.OnReset(count); - } - else - { - this.ProviderAsync.OnReset(count); - } - - if(count >= -1) - RaiseCountChanged(true, count); - - } - - public void OnBeforeReset() - { - if(!IsAsync) - { - if(this.Provider is IProviderPreReset) - { - (this.Provider as IProviderPreReset).OnBeforeReset(); - } - } - else - { - if(this.ProviderAsync is IProviderPreReset) - { - (this.ProviderAsync as IProviderPreReset).OnBeforeReset(); - } - } - } - - /// - /// Raises the count changed. - /// - /// The count. - protected void RaiseCountChanged(bool needsReset, int count) - { - var evnt = this.CountChanged; - if (evnt != null) - { - evnt(this, new CountChangedEventArgs() { NeedsReset = needsReset, Count = count }); - } - } - - /// - /// Occurs when [count changed]. - /// - public event OnCountChanged CountChanged; - - #region IEditableProvider Implementation - - /// - /// Called when [append]. - /// - /// The item. - /// The timestamp. - /// - public int OnAppend(T item, object timestamp) - { - ClearOptimizations(); - - int index = _LocalCount; - - int page; int offset; - - if (!_HasGotCount) EnsureCount(); - - CalculateFromIndex(index, out page, out offset); - - if (IsPageWired(page)) - { - bool shortpage = false; - var dataPage = SafeGetPage(page, false, null, index); - if (dataPage.ItemsPerPage < this.PageSize) shortpage = true; - - dataPage.Append(item, timestamp, this.ExpiryComparer); - - if(shortpage) - { - dataPage.ItemsPerPage++; - } - else - { - AddOrUpdateAdjustment(page, 1); - } - - } - - _LocalCount++; - - ClearOptimizations(); - - if (this.IsAsync) - { - var test = this.GetAt(index, this, false); - } - - - var edit = GetProviderAsEditable(); - if (edit != null) - { - edit.OnInsert(index, item, timestamp); - } - - ClearOptimizations(); - - return index; - } - - /// - /// Gets the page, if use placeholders is false - then gets page sync else async. - /// - /// The page. - /// if set to true [allow placeholders]. - /// The voc. - /// The index that this page refers to (effectively the pageoffset. - /// - protected ISourcePage SafeGetPage(int page, bool allowPlaceholders, object voc, int index) - { - ISourcePage ret = null; - - lock (_PageLock) - { - if (_Pages.TryGetValue(page, out ret)) - { - _Reclaimer.OnPageTouched(ret); - } - else - { - PageDelta delta = null; - if (_Deltas.ContainsKey(page)) delta = _Deltas[page]; - int pageOffset = (page - _BasePage) * this.PageSize + (from d in _Deltas.Values where d.Page < page select d.Delta).Sum(); - int pageSize = Math.Min(this.PageSize, this.GetCount(false)-pageOffset); - if (delta != null) pageSize += delta.Delta; - var newPage = this._Reclaimer.MakePage(page, pageSize); - _Pages[page] = newPage; - - if (!IsAsync) - { - FillPage(newPage, pageOffset); - - ret = newPage; - } - else - { - bool up = allowPlaceholders; - - if (up && voc != null) - { - // Fill with placeholders - //Debug.WriteLine("Filling with placeholders, pagesize=" + pageSize); - for (int loop = 0; loop < pageSize; loop++) - { - newPage.Append(this.ProviderAsync.GetPlaceHolder(newPage.Page * pageSize + loop, newPage.Page, loop), null, this.ExpiryComparer); - } - - ret = newPage; - - CancellationTokenSource cts = StartPageRequest(newPage.Page); - Task.Factory.Run(() => DoRealPageGet(voc, newPage, pageOffset, index, cts)); - } - else - { - FillPageFromAsyncProvider(newPage, pageOffset); - ret = newPage; - } - } - } - } - - return ret; - } - - private Task DoRealPageGet(Object voc, ISourcePage page, int pageOffset, int index, CancellationTokenSource cts) - { - //Debug.WriteLine("DoRealPageGet: pageOffset=" + pageOffset + " index=" + index); - VirtualizingObservableCollection realVOC = (VirtualizingObservableCollection)voc; - List> listOfReplaces = new List>(); - - Task task = Task.Factory.Run(() => { }); - - if (realVOC != null) - { - if (cts.IsCancellationRequested) - { - task.ContinueWith(t => - { - page.PageFetchState = PageFetchStateEnum.Fetched; - ClearOptimizations(); - RemovePageRequest(page.Page); - }); - } - else - { - task = ProviderAsync.GetItemsAtAsync(pageOffset, page.ItemsPerPage, false).ContinueWith(t => - { - var data = t.Result; - - if (cts.IsCancellationRequested || data == null) return; - - page.WiredDateTime = data.LoadedAt; - - int i = 0; - foreach (var item in data.Items) - { - if (cts.IsCancellationRequested) - { - RemovePageRequest(page.Page); - return; - } - - ClearOptimizations(); - if (page.ReplaceNeeded(i)) - { - var old = page.GetAt(i); - if (old == null) - { - - } - - ClearOptimizations(); - - page.ReplaceAt(i, old, item, null, null); - //VirtualizationManager.Instance.RunOnUI(new PlaceholderReplaceWA(realVOC, old, item, pageOffset+i)); - listOfReplaces.Add(new PlaceholderReplaceWA(realVOC, old, item, pageOffset + i)); - } - else - { - page.ReplaceAt(i, default(T), item, null, null); - } - - i++; - } - page.PageFetchState = PageFetchStateEnum.Fetched; - - ClearOptimizations(); - foreach (var replace in listOfReplaces) - { - if (cts.IsCancellationRequested) - { - RemovePageRequest(page.Page); - return; - } - VirtualizationManager.Instance.RunOnUI(replace); - } - - RemovePageRequest(page.Page); - }); - } - } - return task; - } - - protected bool IsPageWired(int page) - { - bool wired = false; - - lock (_PageLock) - { - if (_Pages.ContainsKey(page)) wired = true; - } - - return wired; - } - - public void OnInsert(int index, T item, object timestamp) - { - int page; int offset; - - if (!_HasGotCount) EnsureCount(); - - CalculateFromIndex(index, out page, out offset); - - if (IsPageWired(page)) - { - var dataPage = SafeGetPage(page, false, null, index); - dataPage.InsertAt(offset, item, timestamp, this.ExpiryComparer); - } - int adj = AddOrUpdateAdjustment(page, 1); - - if(page == _BasePage && adj == this.PageSize*2) - { - lock (_PageLock) - { - if (IsPageWired(page)) - { - var dataPage = SafeGetPage(page, false, null, index); - ISourcePage newdataPage = null; - if (IsPageWired(page - 1)) - { - newdataPage = SafeGetPage(page-1, false, null, index); - } - else - { - newdataPage = this._Reclaimer.MakePage(page - 1, this.PageSize); - _Pages[page - 1] = newdataPage; - } - - for (int loop = 0; loop < this.PageSize; loop++) - { - var i = dataPage.GetAt(0); - - dataPage.RemoveAt(0, null, null); - newdataPage.Append(i, null, null); - } - - } - - AddOrUpdateAdjustment(page, -this.PageSize); - - _BasePage--; - } - } - - if (this.IsAsync) - { - var test = this.GetAt(index, this, false); - } - - var edit = GetProviderAsEditable(); - if (edit != null) - { - edit.OnInsert(index, item, timestamp); - } - - _LocalCount++; - - ClearOptimizations(); - } - - void EnsureCount() - { - GetCount(false); - } - - protected bool IsAsync - { - get - { - return _ProviderAsync != null ? true : false; - } - } - - public void OnRemove(int index, T item, object timestamp) - { - int page; int offset; - - if (!_HasGotCount) EnsureCount(); - - CalculateFromIndex(index, out page, out offset); - - if (IsPageWired(page)) - { - var dataPage = SafeGetPage(page, false, null, index); - dataPage.RemoveAt(offset, timestamp, this.ExpiryComparer); - } - AddOrUpdateAdjustment(page, -1); - - if (page == _BasePage) - { - int items = this.PageSize; - if (_Deltas.ContainsKey(page)) items += _Deltas[page].Delta; - if (items == 0) - { - _Deltas.Remove(page); - _BasePage++; - } - } - - if (this.IsAsync) - { - var test = this.GetAt(index, this, false); - } - - var edit = GetProviderAsEditable(); - if (edit != null) - { - edit.OnRemove(index, item, timestamp); - } - - _LocalCount--; - - ClearOptimizations(); - } - - public void OnReplace(int index, T oldItem, T newItem, object timestamp) - { - int page; int offset; - - CalculateFromIndex(index, out page, out offset); - - if (IsPageWired(page)) - { - var dataPage = SafeGetPage(page, false, null, index); - dataPage.ReplaceAt(offset, oldItem, newItem, timestamp, this.ExpiryComparer); - } - - var edit = GetProviderAsEditable(); - if (edit != null) - { - edit.OnReplace(index, oldItem, newItem, timestamp); - } - } - - #endregion IEditableProvider Implementation - - - public void RunClaim(string sectionContext = "") - { - if (_Reclaimer != null) - { - int needed = 0; - - lock (_PageLock) - { - needed = Math.Max(0, _Pages.Count - this.MaxPages); - if (needed != 0) - { - var l = _Reclaimer.ReclaimPages(_Pages.Values, needed, sectionContext).ToList(); - - foreach (var p in l) - { - if (p.Page != _BasePage) - { - lock (_Pages) - { - if (_Pages.ContainsKey(p.Page)) - { - _Pages.Remove(p.Page); - _Reclaimer.OnPageReleased(p); - } - } - } - } - } - } - } - } - } - -} diff --git a/AlphaChiTech.Virtualization.Net4/PlaceholderReplaceWA.cs b/AlphaChiTech.Virtualization.Net4/PlaceholderReplaceWA.cs deleted file mode 100644 index 4f4d17b..0000000 --- a/AlphaChiTech.Virtualization.Net4/PlaceholderReplaceWA.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class PlaceholderReplaceWA : BaseActionVirtualization - { - private T _OldValue; - private T _NewValue; - private int _Index; - - WeakReference _VOC; - - public PlaceholderReplaceWA(VirtualizingObservableCollection voc, T oldValue, T newValue, int index) - : base(VirtualActionThreadModelEnum.UseUIThread) - { - _VOC = new WeakReference(voc); - _OldValue = oldValue; - _NewValue = newValue; - _Index = index; - } - - public override void DoAction() - { - var voc = (VirtualizingObservableCollection)_VOC.Target; - - if (voc != null && _VOC.IsAlive) - { - voc.ReplaceAt(_Index, _OldValue, _NewValue, null); - } - } - } - -} diff --git a/AlphaChiTech.Virtualization.Net4/Properties/AssemblyInfo.cs b/AlphaChiTech.Virtualization.Net4/Properties/AssemblyInfo.cs deleted file mode 100644 index 6598238..0000000 --- a/AlphaChiTech.Virtualization.Net4/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Les informations générales relatives à un assembly dépendent de -// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations -// associées à un assembly. -[assembly: AssemblyTitle("AlphaChiTech.Virtualization.Net4")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AlphaChiTech.Virtualization.Net4")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly -// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de -// COM, affectez la valeur true à l'attribut ComVisible sur ce type. -[assembly: ComVisible(false)] - -// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM -[assembly: Guid("ecedc613-843a-4d9a-8123-bd9600c9118e")] - -// Les informations de version pour un assembly se composent des quatre valeurs suivantes : -// -// Version principale -// Version secondaire -// Numéro de build -// Révision -// -// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut -// en utilisant '*', comme indiqué ci-dessous : -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AlphaChiTech.Virtualization.Net4/ReclaimPagesWA.cs b/AlphaChiTech.Virtualization.Net4/ReclaimPagesWA.cs deleted file mode 100644 index 1e4af09..0000000 --- a/AlphaChiTech.Virtualization.Net4/ReclaimPagesWA.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class ReclaimPagesWA : BaseRepeatableActionVirtualization - { - public ReclaimPagesWA(IReclaimableService provider, string sectionContext, TimeSpan? delayBetweenReclaims) - : base(VirtualActionThreadModelEnum.Background, true, delayBetweenReclaims ?? TimeSpan.FromMinutes(1)) - { - _WRProvider = new WeakReference(provider); - } - - WeakReference _WRProvider = null; - - string _SectionContext = ""; - - public override void DoAction() - { - _LastRun = DateTime.Now; - - var reclaimer = _WRProvider.Target as IReclaimableService; - - if (reclaimer != null) - { - reclaimer.RunClaim(_SectionContext); - } - } - - public override bool KeepInActionsList() - { - bool ret = base.KeepInActionsList(); - - if (!_WRProvider.IsAlive) ret = false; - - return ret; - } - } - -} diff --git a/AlphaChiTech.Virtualization.Net4/SourcePage.cs b/AlphaChiTech.Virtualization.Net4/SourcePage.cs deleted file mode 100644 index f5c0d64..0000000 --- a/AlphaChiTech.Virtualization.Net4/SourcePage.cs +++ /dev/null @@ -1,234 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class SourcePage : ISourcePage - { - protected List _Items = new List(); - - - /// - /// Gets or sets the page. - /// - /// - /// The page. - /// - public int Page { get; set; } - - private List _PendingUpdates = new List(); - - public List PendingUpdates - { - get { return _PendingUpdates; } - } - - /// - /// Gets or sets the items per page. - /// - /// - /// The items per page. - /// - public int ItemsPerPage { get; set; } - - public int ItemsCount - { - get - { - return _Items.Count; - } - } - - private List _ReplaceNeededList = new List(); - - /// - /// Gets a value indicating whether [can reclaim page]. - /// - /// - /// true if [can reclaim page]; otherwise, false. - /// - public bool CanReclaimPage - { - get - { - bool ret = true; - if (this._PageFetchState == PageFetchStateEnum.Placeholders) ret = false; - return ret; - } - } - - /// - /// Determines whether it is safe to update into a page where the pending update was generated at a given time. - /// - /// The updated happened at this datetime. - /// - public bool IsSafeToUpdate(IPageExpiryComparer comparer, object updatedAt) - { - bool ret = true; - - if (comparer != null) - { - ret = comparer.IsUpdateValid(this.WiredDateTime, updatedAt); - } - - //if(updatedAt.HasValue && updatedAt.Value != DateTime.MinValue) - //{ - // if(updatedAt.Value< this.WiredDateTime) - // { - // ret = false; - // } - //} - - return ret; - } - - /// - /// Gets or sets the last touch. - /// - /// - /// The last touch. - /// - public Object LastTouch { get; set; } - - /// - /// Gets at. - /// - /// The offset. - /// - public T GetAt(int offset) - { - T ret = default(T); - - if (this._PageFetchState == PageFetchStateEnum.Placeholders) _ReplaceNeededList.Add(offset); - - if (_Items.Count > offset) ret = _Items[offset]; - - LastTouch = DateTime.Now; - - return ret; - } - - public bool ReplaceNeeded(int offset) - { - bool ret = false; - - if (_ReplaceNeededList.Contains(offset)) ret = true; - - return ret; - } - - /// - /// Appends the specified item. - /// - /// The item. - /// - public int Append(T item, object updatedAt, IPageExpiryComparer comparer) - { - _Items.Add(item); - - LastTouch = DateTime.Now; - - return _Items.IndexOf(item); - } - - /// - /// Inserts at. - /// - /// The offset. - /// The item. - /// The updated at. - public void InsertAt(int offset, T item, object updatedAt, IPageExpiryComparer comparer) - { - if (IsSafeToUpdate(comparer, updatedAt)) - { - if (_Items.Count > offset) - { - _Items.Insert(offset, item); - } - else - { - _Items.Add(item); - } - - } - } - - /// - /// Removes at. - /// - /// The offset. - /// The updated at. - /// - public bool RemoveAt(int offset, object updatedAt, IPageExpiryComparer comparer) - { - bool removed = true; - - if (IsSafeToUpdate(comparer, updatedAt)) - { - _Items.RemoveAt(offset); - } - - return removed; - } - - /// - /// Replaces at. - /// - /// The offset. - /// The old value. - /// The new value. - /// The updated at. - public void ReplaceAt(int offset, T oldValue, T newValue, object updatedAt, IPageExpiryComparer comparer) - { - if (IsSafeToUpdate(comparer, updatedAt)) - { - if (_Items.Count > offset) _Items[offset] = newValue; - } - } - - /// - /// Indexes the of. - /// - /// The item. - /// - public int IndexOf(T item) - { - LastTouch = DateTime.Now; - - return _Items.IndexOf(item); - } - - private PageFetchStateEnum _PageFetchState = PageFetchStateEnum.Placeholders; - - /// - /// Gets or sets the state of the page fetch state. - /// - /// - /// The state of the page fetch. - /// - public PageFetchStateEnum PageFetchState - { - get { return _PageFetchState; } - set { _PageFetchState = value; } - } - - private Object _WiredDateTime = DateTime.MinValue; - - /// - /// Gets or sets the wired date time. - /// - /// - /// The wired date time. - /// - public object WiredDateTime - { - get { return _WiredDateTime; } - set { _WiredDateTime = value; } - } - - - } - -} diff --git a/AlphaChiTech.Virtualization.Net4/TaskExtension.cs b/AlphaChiTech.Virtualization.Net4/TaskExtension.cs deleted file mode 100644 index 52a8a3c..0000000 --- a/AlphaChiTech.Virtualization.Net4/TaskExtension.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public static class TaskExtension - { - public static Task Run(this TaskFactory factory, Action action) - { - return Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); - } - - public static Task Run(this TaskFactory factory, Func func) - { - return Task.Factory.StartNew(func, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); - } - - public static Task Run(this TaskFactory factory, Func> func) - { - return Task.Factory.StartNew(func, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap(); - } - } -} diff --git a/AlphaChiTech.Virtualization.Net4/VirtualActionThreadModelEnum.cs b/AlphaChiTech.Virtualization.Net4/VirtualActionThreadModelEnum.cs deleted file mode 100644 index 37839c3..0000000 --- a/AlphaChiTech.Virtualization.Net4/VirtualActionThreadModelEnum.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public enum VirtualActionThreadModelEnum - { - UseUIThread, - Background - } -} diff --git a/AlphaChiTech.Virtualization.Net4/VirtualizationManager.cs b/AlphaChiTech.Virtualization.Net4/VirtualizationManager.cs deleted file mode 100644 index ce06a2d..0000000 --- a/AlphaChiTech.Virtualization.Net4/VirtualizationManager.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public class VirtualizationManager - { - private static VirtualizationManager _Instance = new VirtualizationManager(); - - private List _Actions = new List(); - private object _ActionLock = new object(); - - private static bool _IsInitialized = false; - - public static bool IsInitialized - { - get - { - return _IsInitialized; - } - } - - public static VirtualizationManager Instance - { - get - { - return _Instance; - } - } - - bool _Processing = false; - - private Action _UIThreadExcecuteAction = null; - - public Action UIThreadExcecuteAction - { - get { return _UIThreadExcecuteAction; } - set - { - _UIThreadExcecuteAction = value; - _IsInitialized = true; - } - } - - public void ProcessActions() - { - if (_Processing) return; - - _Processing = true; - - List lst; - lock (_ActionLock) - { - lst = _Actions.ToList(); - } - - foreach (var action in lst) - { - bool bdo = true; - - if (action is IRepeatingVirtualizationAction) - { - bdo = (action as IRepeatingVirtualizationAction).IsDueToRun(); - } - - if (bdo) - { - switch (action.ThreadModel) - { - case VirtualActionThreadModelEnum.UseUIThread: - if (UIThreadExcecuteAction == null) // PLV - throw new Exception( "VirtualizationManager isn’t already initialized ! set the VirtualizationManager’s UIThreadExcecuteAction (VirtualizationManager.Instance.UIThreadExcecuteAction = a => Dispatcher.Invoke( a );)" ); - UIThreadExcecuteAction.Invoke(() => action.DoAction()); - break; - case VirtualActionThreadModelEnum.Background: - Task.Factory.Run(() => action.DoAction()); - break; - } - - if (action is IRepeatingVirtualizationAction) - { - if (!(action as IRepeatingVirtualizationAction).KeepInActionsList()) - { - lock (_ActionLock) - { - _Actions.Remove(action); - } - } - } - else - { - lock (_ActionLock) - { - _Actions.Remove(action); - } - } - } - } - - _Processing = false; - } - public void AddAction(IVirtualizationAction action) - { - lock (_ActionLock) - { - _Actions.Add(action); - } - } - - public void AddAction(Action action) - { - AddAction(new ActionVirtualizationWrapper(action)); - } - - public void RunOnUI(IVirtualizationAction action) - { - if (UIThreadExcecuteAction == null) // PLV - throw new Exception( "VirtualizationManager isn’t already initialized ! set the VirtualizationManager’s UIThreadExcecuteAction (VirtualizationManager.Instance.UIThreadExcecuteAction = a => Dispatcher.Invoke( a );)" ); - UIThreadExcecuteAction.Invoke(() => action.DoAction()); - } - - public void RunOnUI(Action action) - { - RunOnUI(new ActionVirtualizationWrapper(action)); - } - } -} diff --git a/AlphaChiTech.Virtualization.Net4/VirtualizingObservableCollection.cs b/AlphaChiTech.Virtualization.Net4/VirtualizingObservableCollection.cs deleted file mode 100644 index 595f914..0000000 --- a/AlphaChiTech.Virtualization.Net4/VirtualizingObservableCollection.cs +++ /dev/null @@ -1,913 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Threading; - -namespace AlphaChiTech.Virtualization -{ - public class VirtualizingObservableCollection : IEnumerable, ICollection, IList, IEnumerable, ICollection, IList, INotifyCollectionChanged, INotifyPropertyChanged - { - #region Ctors Etc - - /// - /// Initializes a new instance of the class. - /// - /// The provider. - public VirtualizingObservableCollection(IItemSourceProvider provider) - { - this.Provider = provider; - } - - /// - /// Initializes a new instance of the class. - /// - /// The asynchronous provider. - public VirtualizingObservableCollection(IItemSourceProviderAsync asyncProvider) - { - this.ProviderAsync = asyncProvider; - } - - /// - /// Initializes a new instance of the class. - /// - /// The provider. - /// The optional reclaimer. - /// The optional expiry comparer. - /// Size of the page. - /// The maximum pages. - /// The maximum deltas. - /// The maximum distance. - public VirtualizingObservableCollection( - IPagedSourceProvider provider, - IPageReclaimer reclaimer = null, - IPageExpiryComparer expiryComparer = null, - int pageSize = 100, - int maxPages = 100, - int maxDeltas = -1, - int maxDistance = -1 - ) - { - this.Provider = new PaginationManager(provider, reclaimer, expiryComparer, pageSize, maxPages, maxDeltas, maxDistance); - } - - - #endregion Ctors Etc - - #region IEnumerable Implementation - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - string sc = new Guid().ToString(); - - EnsureCountIsGotNONASync(); - - int count = InternalGetCount(); - - for (int iLoop = 0; iLoop < count; iLoop++) - { - yield return InternalGetValue(iLoop, sc); - } - } - - #endregion IEnumerable Implementation - - #region IEnumerable Implementation - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// A that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - string sc = new Guid().ToString(); - - EnsureCountIsGotNONASync(); - - int count = InternalGetCount(); - - for (int iLoop = 0; iLoop < count; iLoop++) - { - yield return InternalGetValue(iLoop, sc); - } - } - - #endregion IEnumerable Implementation - - #region ICollection Implementation - - public void CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - - /// - /// Gets the number of elements contained in the . - /// - /// The number of elements contained in the . - public int Count - { - get { return InternalGetCount(); } - } - - /// - /// Gets a value indicating whether access to the is synchronized (thread safe). - /// - /// true if access to the is synchronized (thread safe); otherwise, false. - public bool IsSynchronized - { - get { return false; } - } - - private object _SyncRoot = new object(); - - /// - /// Gets an object that can be used to synchronize access to the . - /// - /// An object that can be used to synchronize access to the . - public object SyncRoot - { - get { return _SyncRoot; } - } - - #endregion ICollection Implementation - - #region ICollection Implementation - - /// - /// Adds an item to the . - /// - /// The object to add to the . - public void Add(T item) - { - InternalAdd(item, null); - } - - /// - /// Resets the collection - aka forces a get all data, including the count . - /// - public void Clear() - { - InternalClear(); - } - - /// - /// Determines whether the contains a specific value. - /// - /// The object to locate in the . - /// - /// true if is found in the ; otherwise, false. - /// - public bool Contains(T item) - { - return IndexOf(item) != -1 ? true : false; - } - - public void CopyTo(T[] array, int arrayIndex) - { - throw new NotImplementedException(); - } - - /// - /// Gets a value indicating whether the is read-only. - /// - /// true if the is read-only; otherwise, false. - public bool IsReadOnly - { - get { return false; } - } - - /// - /// Removes the first occurrence of a specific object from the . - /// - /// The object to remove from the . - /// - /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . - /// - public bool Remove(T item) - { - return InternalRemoveAt(IndexOf(item)); - } - - #endregion ICollection Implementation - - #region Extended CRUD operators that take into account the DateTime of the change - - /// - /// Removes the specified item - extended to only remove the item if the page was not pulled before the updatedat DateTime. - /// - /// The item. - /// The updated at. - /// - public bool Remove(T item, object updatedAt) - { - return InternalRemoveAt(IndexOf(item), updatedAt); - } - - /// - /// Removes at the given index - extended to only remove the item if the page was not pulled before the updatedat DateTime. - /// - /// The index. - /// The updated at. - /// - public bool RemoveAt(int index, object updatedAt) - { - return InternalRemoveAt(index, updatedAt); - } - - /// - /// Adds (appends) the specified item - extended to only add the item if the page was not pulled before the updatedat DateTime. - /// - /// The item. - /// The updated at. - /// - public int Add(T item, object updatedAt) - { - return InternalAdd(item, updatedAt); - } - - /// - /// Inserts the specified index - extended to only insert the item if the page was not pulled before the updatedat DateTime. - /// - /// The index. - /// The item. - /// The updated at. - public void Insert(int index, T item, object updatedAt) - { - InternalInsertAt(index, item, updatedAt); - } - /// - /// Adds the range. - /// - /// The new values. - /// The updatedat object. - /// Index of the last appended object - public int AddRange(IEnumerable newValues, object timestamp = null) - { - var edit = GetProviderAsEditable(); - - int index = -1; - List items = new List(); - - foreach (var item in newValues) - { - items.Add(item); - index = edit.OnAppend(item, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); - RaiseCollectionChangedEvent(args); - } - - - OnCountTouched(); - - - return index; - } - - #endregion Extended CRUD operators that take into account the DateTime of the change - - #region IList Implementation - - /// - /// Adds an item to the . - /// - /// The object to add to the . - /// - /// The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the collection. - /// - public int Add(object value) - { - return InternalAdd((T)value, null); - } - - /// - /// Determines whether the contains a specific value. - /// - /// The object to locate in the . - /// - /// true if the is found in the ; otherwise, false. - /// - public bool Contains(object value) - { - return Contains((T)value); - } - - /// - /// Determines the index of a specific item in the . - /// - /// The object to locate in the . - /// - /// The index of if found in the list; otherwise, -1. - /// - public int IndexOf(object value) - { - return IndexOf((T)value); - } - - /// - /// Inserts an item to the at the specified index. - /// - /// The zero-based index at which should be inserted. - /// The object to insert into the . - public void Insert(int index, object value) - { - Insert(index, (T)value); - } - - /// - /// Gets a value indicating whether the has a fixed size. - /// - /// true if the has a fixed size; otherwise, false. - public bool IsFixedSize - { - get { return false; } - } - - /// - /// Removes the first occurrence of a specific object from the . - /// - /// The object to remove from the . - public void Remove(object value) - { - Remove((T)value); - } - - /// - /// Removes the item at the specified index. - /// - /// The zero-based index of the item to remove. - public void RemoveAt(int index) - { - InternalRemoveAt(index); - } - - /// - /// Gets or sets the element at the specified index. - /// - /// The index. - /// - public object this[int index] - { - get - { - return InternalGetValue(index, _DefaultSelectionContext); - } - set - { - InternalSetValue(index, (T)value); - } - } - - #endregion IList Implementation - - #region IList Implementation - - /// - /// Determines the index of a specific item in the . - /// - /// The object to locate in the . - /// - /// The index of if found in the list; otherwise, -1. - /// - public int IndexOf(T item) - { - return InternalIndexOf(item); - } - - /// - /// Inserts an item to the at the specified index. - /// - /// The zero-based index at which should be inserted. - /// The object to insert into the . - public void Insert(int index, T item) - { - InternalInsertAt(index, item); - } - - /// - /// Gets or sets the element at the specified index. - /// - /// The index. - /// - T IList.this[int index] - { - get - { - return InternalGetValue(index, _DefaultSelectionContext); - } - set - { - InternalSetValue(index, value); - } - } - - #endregion IList Implementation - - #region Public Properties - - /// - /// Gets or sets the provider if its asynchronous. - /// - /// - /// The provider asynchronous. - /// - public IItemSourceProviderAsync ProviderAsync - { - get { return _ProviderAsync; } - set - { - ClearCountChangedHooks(); - _ProviderAsync = value; - if (_ProviderAsync is INotifyCountChanged) - { - (_ProviderAsync as INotifyCountChanged).CountChanged += VirtualizingObservableCollection_CountChanged; - } - } - } - - void VirtualizingObservableCollection_CountChanged(object sender, CountChangedEventArgs args) - { - if (args.NeedsReset) - { - // Send a reset.. - RaiseCollectionChangedEvent(_CC_ResetArgs); - } - OnCountTouched(); - } - - /// - /// Gets or sets the provider if its not asynchronous. - /// - /// - /// The provider. - /// - public IItemSourceProvider Provider - { - get { return _Provider; } - set - { - ClearCountChangedHooks(); - _Provider = value; - - if (_Provider is INotifyCountChanged) - { - (_Provider as INotifyCountChanged).CountChanged += VirtualizingObservableCollection_CountChanged; - } - } - } - - void ClearCountChangedHooks() - { - if(_Provider is INotifyCountChanged) - { - (_Provider as INotifyCountChanged).CountChanged -= VirtualizingObservableCollection_CountChanged; - } - - if(_ProviderAsync is INotifyCountChanged) - { - (_ProviderAsync as INotifyCountChanged).CountChanged -= VirtualizingObservableCollection_CountChanged; - } - } - - #endregion Public Properties - - #region INotifyCollectionChanged Implementation - - private bool _SupressEventErrors = false; - - public bool SupressEventErrors - { - get - { - return _SupressEventErrors; - } - - set - { - _SupressEventErrors = value; - } - } - - public event NotifyCollectionChangedEventHandler CollectionChanged; - - /// - /// Raises the collection changed event. - /// - /// The instance containing the event data. - internal void RaiseCollectionChangedEvent(NotifyCollectionChangedEventArgs args) - { - if (_BulkCount > 0) return; - - var evnt = CollectionChanged; - - if (evnt != null) - { - try - { - evnt(this, args); - } - catch (Exception ex) - { - if (!this.SupressEventErrors) - { - throw ex; - } - } - } - } - - #endregion INotifyCollectionChanged Implementation - - #region INotifyPropertyChanged implementation - - public event PropertyChangedEventHandler PropertyChanged; - - private static PropertyChangedEventArgs _PC_CountArgs = new PropertyChangedEventArgs("Count"); - private static NotifyCollectionChangedEventArgs _CC_ResetArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); - - private void OnCountTouched() - { - RaisePropertyChanged(_PC_CountArgs); - } - - protected void RaisePropertyChanged(PropertyChangedEventArgs args) - { - if (_BulkCount > 0) return; - - var evnt = PropertyChanged; - - if (evnt != null) - { - evnt(this, args); - } - } - - #endregion INotifyPropertyChanged implementation - - #region Bulk Operation implementation - - /// - /// Releases the bulk mode. - /// - internal void ReleaseBulkMode() - { - if (_BulkCount > 0) _BulkCount--; - - if (_BulkCount == 0) - { - RaiseCollectionChangedEvent(_CC_ResetArgs); - RaisePropertyChanged(_PC_CountArgs); - } - } - - /// - /// Enters the bulk mode. - /// - /// - public BulkMode EnterBulkMode() - { - _BulkCount++; - - return new BulkMode(this); - } - - /// - /// The Bulk mode IDisposable proxy - /// - public class BulkMode : IDisposable - { - public BulkMode(VirtualizingObservableCollection voc) - { - _voc = voc; - } - - private VirtualizingObservableCollection _voc = null; - - bool _IsDisposed = false; - - public void Dispose() - { - OnDispose(); - } - - void OnDispose() - { - if (!_IsDisposed) - { - _IsDisposed = true; - if (_voc != null) _voc.ReleaseBulkMode(); - } - } - - ~BulkMode() - { - OnDispose(); - } - } - - #endregion Bulk Operation implementation - - #region Private Properties - - protected String _DefaultSelectionContext = new Guid().ToString(); - private IItemSourceProvider _Provider = null; - private IItemSourceProviderAsync _ProviderAsync = null; - private int _BulkCount = 0; - - #endregion Private Properties - - #region Internal implementation - - - /// - /// Gets the provider as editable. - /// - /// - /// - protected IEditableProvider GetProviderAsEditable() - { - IEditableProvider ret = null; - - if (this.Provider != null) - { - ret = this.Provider as IEditableProvider; - - } - else - { - ret = this.ProviderAsync as IEditableProvider; - } - - if (ret == null) - { - throw new NotSupportedException(); - } - - return ret; - } - - /// - /// Replaces oldValue with newValue at index if updatedat is newer or null. - /// - /// The index. - /// The old value. - /// The new value. - /// The timestamp. - internal void ReplaceAt(int index, T oldValue, T newValue, object timestamp) - { - var edit = this.GetProviderAsEditable(); - - if (edit != null) - { - edit.OnReplace(index, oldValue, newValue, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue, oldValue, index); - RaiseCollectionChangedEvent(args); - } - } - - void InternalClear() - { - if (this.Provider != null) - { - if (this.Provider is IProviderPreReset) - { - (this.Provider as IProviderPreReset).OnBeforeReset(); - } - this.Provider.OnReset(-1); - } - else - { - if (this.ProviderAsync is IProviderPreReset) - { - (this.ProviderAsync as IProviderPreReset).OnBeforeReset(); - } - this.ProviderAsync.OnReset(-1); - } - } - - CancellationTokenSource _ResetToken = null; - - public Task ResetAsync() - { - CancellationTokenSource cts = null; - - lock(this) - { - if(_ResetToken != null) - { - _ResetToken.Cancel(); - _ResetToken = null; - } - - cts = _ResetToken = new CancellationTokenSource(); - } - - if (this.Provider != null) - { - if (this.Provider is IProviderPreReset) - { - (this.Provider as IProviderPreReset).OnBeforeReset(); - if (cts.IsCancellationRequested) - { - return Task.Factory.Run(() => { }); - } - - } - - //this.Provider.OnReset(-2); - - return Task.Factory.Run(() => - { - if (this.Provider is IAsyncResetProvider) - { - (this.Provider as IAsyncResetProvider).GetCountAsync() - .ContinueWith(t => - { - int count = t.Result; - - if (!cts.IsCancellationRequested) - { - VirtualizationManager.Instance.RunOnUI(() => - this.Provider.OnReset(count) - ); - } - }); - } - else - { - int count = this.Provider.GetCount(false); - if (!cts.IsCancellationRequested) - { - VirtualizationManager.Instance.RunOnUI(() => - this.Provider.OnReset(count) - ); - } - } - - }); - - } - else - { - if (this.ProviderAsync is IProviderPreReset) - { - (this.ProviderAsync as IProviderPreReset).OnBeforeReset(); - } - - this.ProviderAsync.Count.ContinueWith(t => - { - var count = t.Result; - this.ProviderAsync.OnReset(count); - }, TaskScheduler.FromCurrentSynchronizationContext()); - } - - lock(this) - { - if(_ResetToken == cts) - { - _ResetToken = null; - } - } - - return Task.Factory.Run(() => { }); - } - - T InternalGetValue(int index, string selectionContext) - { - bool allowPlaceholder = true; - if (selectionContext != _DefaultSelectionContext) allowPlaceholder = false; - - if (this.Provider != null) - { - return this.Provider.GetAt(index, this, allowPlaceholder); - } - else - { - return Task.Factory.Run(() => this.ProviderAsync.GetAt(index, this, allowPlaceholder)).Result; - } - } - - T InternalSetValue(int index, T newValue) - { - T oldValue = InternalGetValue(index, _DefaultSelectionContext); - var edit = GetProviderAsEditable(); - edit.OnReplace(index, oldValue, newValue, null); - - List newItems = new List(); newItems.Add(newValue); - List oldItems = new List(); oldItems.Add(oldValue); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItems, oldItems, index); - RaiseCollectionChangedEvent(args); - - return oldValue; - } - - int InternalAdd(T newValue, object timestamp) - { - var edit = GetProviderAsEditable(); - var index = edit.OnAppend(newValue, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newValue, index); - RaiseCollectionChangedEvent(args); - - OnCountTouched(); - - return index; - } - - int InternalGetCount() - { - int ret = 0; - - if (this.Provider != null) - { - ret = this.Provider.GetCount(true); - } - else - { - ret = Task.Factory.Run( () => this.ProviderAsync.Count).Result; - } - - - return ret; - } - - void InternalInsertAt(int index, T item, object timestamp = null) - { - - var edit = GetProviderAsEditable(); - edit.OnInsert(index, item, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); - RaiseCollectionChangedEvent(args); - - OnCountTouched(); - } - - bool InternalRemoveAt(int index, object timestamp = null) - { - T oldValue = InternalGetValue(index, _DefaultSelectionContext); - - if (oldValue == null) - { - return false; - } - else - { - var edit = GetProviderAsEditable(); - edit.OnRemove(index, oldValue, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldValue, index); - RaiseCollectionChangedEvent(args); - - OnCountTouched(); - - return true; - } - } - - int InternalIndexOf(T item) - { - if (this.Provider != null) - { - return this.Provider.IndexOf(item); - } - else - { - return Task.Factory.Run( () => this.ProviderAsync.IndexOf(item)).Result; - } - } - - void EnsureCountIsGotNONASync() - { - if(this.Provider != null) - { - this.Provider.GetCount(false); - } - else - { - - } - } - - #endregion Internal implementation - - } - -} diff --git a/AlphaChiTech.Virtualization.Net4/app.config b/AlphaChiTech.Virtualization.Net4/app.config deleted file mode 100644 index 23d75ee..0000000 --- a/AlphaChiTech.Virtualization.Net4/app.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/AlphaChiTech.Virtualization.Net4/packages.config b/AlphaChiTech.Virtualization.Net4/packages.config deleted file mode 100644 index 8a9a7ac..0000000 --- a/AlphaChiTech.Virtualization.Net4/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/AlphaChiTech.Virtualization.sln b/AlphaChiTech.Virtualization.sln index 2646d8d..2073258 100644 --- a/AlphaChiTech.Virtualization.sln +++ b/AlphaChiTech.Virtualization.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataGridAsyncDemo", "DataGridAsyncDemo\DataGridAsyncDemo.csproj", "{B1235F30-C8A9-4A18-B870-A7F23F214039}" EndProject @@ -12,7 +12,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{90A3E4 .nuget\NuGet.targets = .nuget\NuGet.targets EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlphaChiTech.Virtualization.Net4", "AlphaChiTech.Virtualization.Net4\AlphaChiTech.Virtualization.Net4.csproj", "{ECEDC613-843A-4D9A-8123-BD9600C9118E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataGridAsyncDemoMVVM", "DataGridAsyncDemoMVVM\DataGridAsyncDemoMVVM.csproj", "{DE19AAA5-881C-48BE-A8E7-A8799F5C4BD3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualizingCollection", "VirtualizingCollection\VirtualizingCollection.csproj", "{D8D58C1D-2596-45AD-83BD-E0D5C9FA7F69}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualizingCollection.Tests", "VirtualizingCollection.Tests\VirtualizingCollection.Tests.csproj", "{DA20F632-94B1-48B6-A4BB-5262E3EC2973}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F3339508-BAE9-47BD-B013-BF4C334B729B}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -24,12 +33,23 @@ Global {B1235F30-C8A9-4A18-B870-A7F23F214039}.Debug|Any CPU.Build.0 = Debug|Any CPU {B1235F30-C8A9-4A18-B870-A7F23F214039}.Release|Any CPU.ActiveCfg = Release|Any CPU {B1235F30-C8A9-4A18-B870-A7F23F214039}.Release|Any CPU.Build.0 = Release|Any CPU - {ECEDC613-843A-4D9A-8123-BD9600C9118E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ECEDC613-843A-4D9A-8123-BD9600C9118E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ECEDC613-843A-4D9A-8123-BD9600C9118E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ECEDC613-843A-4D9A-8123-BD9600C9118E}.Release|Any CPU.Build.0 = Release|Any CPU + {DE19AAA5-881C-48BE-A8E7-A8799F5C4BD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE19AAA5-881C-48BE-A8E7-A8799F5C4BD3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE19AAA5-881C-48BE-A8E7-A8799F5C4BD3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE19AAA5-881C-48BE-A8E7-A8799F5C4BD3}.Release|Any CPU.Build.0 = Release|Any CPU + {D8D58C1D-2596-45AD-83BD-E0D5C9FA7F69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8D58C1D-2596-45AD-83BD-E0D5C9FA7F69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8D58C1D-2596-45AD-83BD-E0D5C9FA7F69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8D58C1D-2596-45AD-83BD-E0D5C9FA7F69}.Release|Any CPU.Build.0 = Release|Any CPU + {DA20F632-94B1-48B6-A4BB-5262E3EC2973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA20F632-94B1-48B6-A4BB-5262E3EC2973}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA20F632-94B1-48B6-A4BB-5262E3EC2973}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA20F632-94B1-48B6-A4BB-5262E3EC2973}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D457F9E2-2C21-4C1F-93EA-EE4DAD2CA2E4} + EndGlobalSection EndGlobal diff --git a/AlphaChiTech.Virtualization/ActionVirtualizationWrapper.cs b/AlphaChiTech.Virtualization/ActionVirtualizationWrapper.cs deleted file mode 100644 index 7189d0e..0000000 --- a/AlphaChiTech.Virtualization/ActionVirtualizationWrapper.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - /// - /// This is a VirtualAction that wraps an Action, optionally with a repeating schedule. - /// - public class ActionVirtualizationWrapper : BaseRepeatableActionVirtualization - { - private Action _Action = null; - - /// - /// Initializes a new instance of the class. - /// - /// The action. - /// The thread model. - /// if set to true [is repeating]. - /// The repeating schedule. - public ActionVirtualizationWrapper(Action action, - VirtualActionThreadModelEnum threadModel = VirtualActionThreadModelEnum.UseUIThread, - bool isRepeating = false, TimeSpan? repeatingSchedule = null) - : base(threadModel, isRepeating, repeatingSchedule) - { - _Action = action; - } - - /// - /// Does the action. - /// - public override void DoAction() - { - var a = _Action; - _LastRun = DateTime.Now; - - if (a != null) - { - a.Invoke(); - } - } - - } - -} diff --git a/AlphaChiTech.Virtualization/AlphaChiTech.Virtualization.csproj b/AlphaChiTech.Virtualization/AlphaChiTech.Virtualization.csproj deleted file mode 100644 index 1432173..0000000 --- a/AlphaChiTech.Virtualization/AlphaChiTech.Virtualization.csproj +++ /dev/null @@ -1,88 +0,0 @@ - - - - - 10.0 - Debug - AnyCPU - {5455EE53-36B9-48E8-B1F0-EB7C6ABB7A57} - Library - Properties - AlphaChiTech.Virtualization - AlphaChiTech.Virtualization - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile78 - v4.5 - SAK - SAK - SAK - SAK - - - true - full - false - bin\Debug\ - TRACE;DEBUG;EXPEREMENTAL - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/AlphaChiTech.Virtualization/AlphaChiTech.Virtualization.csproj.vspscc b/AlphaChiTech.Virtualization/AlphaChiTech.Virtualization.csproj.vspscc deleted file mode 100644 index b6d3289..0000000 --- a/AlphaChiTech.Virtualization/AlphaChiTech.Virtualization.csproj.vspscc +++ /dev/null @@ -1,10 +0,0 @@ -"" -{ -"FILE_VERSION" = "9237" -"ENLISTMENT_CHOICE" = "NEVER" -"PROJECT_FILE_RELATIVE_PATH" = "" -"NUMBER_OF_EXCLUDED_FILES" = "0" -"ORIGINAL_PROJECT_FILE_PATH" = "" -"NUMBER_OF_NESTED_PROJECTS" = "0" -"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" -} diff --git a/AlphaChiTech.Virtualization/BaseActionVirtualization.cs b/AlphaChiTech.Virtualization/BaseActionVirtualization.cs deleted file mode 100644 index 81d2e8f..0000000 --- a/AlphaChiTech.Virtualization/BaseActionVirtualization.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - /// - /// Base Class that does an action on the dispatcher thread. Simply implement the DoAction method. - /// - public abstract class BaseActionVirtualization : IVirtualizationAction - { - /// - /// Gets or sets the thread model. - /// - /// - /// The thread model. - /// - public VirtualActionThreadModelEnum ThreadModel { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The thread model. - public BaseActionVirtualization(VirtualActionThreadModelEnum threadModel) - { - this.ThreadModel = threadModel; - } - - public abstract void DoAction(); - } -} diff --git a/AlphaChiTech.Virtualization/BasePagedSourceProvider.cs b/AlphaChiTech.Virtualization/BasePagedSourceProvider.cs deleted file mode 100644 index af27879..0000000 --- a/AlphaChiTech.Virtualization/BasePagedSourceProvider.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public class BasePagedSourceProvider : IPagedSourceProvider - { - public BasePagedSourceProvider() - { - } - - public BasePagedSourceProvider( - Func> funcGetItemsAt = null, - Func funcGetCount = null, - Func funcIndexOf = null, - Action actionOnReset = null - ) - { - this.FuncGetItemsAt = funcGetItemsAt; - this.FuncGetCount = funcGetCount; - this.FuncIndexOf = funcIndexOf; - this.ActionOnReset = actionOnReset; - } - - private Func> _FuncGetItemsAt = null; - - public Func> FuncGetItemsAt - { - get { return _FuncGetItemsAt; } - set { _FuncGetItemsAt = value; } - } - private Func _FuncGetCount = null; - - public Func FuncGetCount - { - get { return _FuncGetCount; } - set { _FuncGetCount = value; } - } - private Func _FuncIndexOf = null; - - public Func FuncIndexOf - { - get { return _FuncIndexOf; } - set { _FuncIndexOf = value; } - } - private Action _ActionOnReset = null; - - public Action ActionOnReset - { - get { return _ActionOnReset; } - set { _ActionOnReset = value; } - } - - public virtual PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder) - { - if (_FuncGetItemsAt != null) return _FuncGetItemsAt.Invoke(pageoffset, count); - - return null; - } - - public virtual int Count - { - get - { - int ret = 0; - - if (_FuncGetCount != null) ret = _FuncGetCount.Invoke(); - - return ret; - } - } - - public virtual int IndexOf(T item) - { - int ret = -1; - - if (_FuncIndexOf != null) ret = _FuncIndexOf.Invoke(item); - - return ret; - } - - public virtual void OnReset(int count) - { - if (_ActionOnReset != null) _ActionOnReset.Invoke(count); - } - } -} diff --git a/AlphaChiTech.Virtualization/BaseRepeatableActionVirtualization.cs b/AlphaChiTech.Virtualization/BaseRepeatableActionVirtualization.cs deleted file mode 100644 index f30e0c1..0000000 --- a/AlphaChiTech.Virtualization/BaseRepeatableActionVirtualization.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - /// - /// Base class there the Action repeats on a periodic basis (the RepeatingSchedule) like BaseActionVirtualization - /// simply implement the DoAction method. - /// - public abstract class BaseRepeatableActionVirtualization : BaseActionVirtualization, IRepeatingVirtualizationAction - { - protected DateTime _LastRun = DateTime.MinValue; - private TimeSpan _RepeatingSchedule = TimeSpan.FromSeconds(1); - - /// - /// Initializes a new instance of the class. - /// - /// The thread model. - /// if set to true [is repeating]. - /// The repeating schedule. - public BaseRepeatableActionVirtualization(VirtualActionThreadModelEnum threadModel = VirtualActionThreadModelEnum.UseUIThread, - bool isRepeating = false, TimeSpan? repeatingSchedule = null) - : base(threadModel) - { - this.IsRepeating = isRepeating; - if (repeatingSchedule.HasValue) - { - this.RepeatingSchedule = repeatingSchedule.Value; - } - } - - /// - /// Gets or sets the repeating schedule. - /// - /// - /// The repeating schedule. - /// - public TimeSpan RepeatingSchedule - { - get { return _RepeatingSchedule; } - set { _RepeatingSchedule = value; } - } - - private bool _IsRepeating = false; - - /// - /// Gets or sets a value indicating whether [is repeating]. - /// - /// - /// true if [is repeating]; otherwise, false. - /// - protected bool IsRepeating - { - get { return _IsRepeating; } - set { _IsRepeating = value; } - } - - /// - /// check to see if the action should be kept. - /// - /// - public virtual bool KeepInActionsList() - { - return this.IsRepeating; - } - - /// - /// Determines whether [is due to run]. - /// - /// - public virtual bool IsDueToRun() - { - - if (DateTime.Now >= _LastRun.Add(this.RepeatingSchedule)) - { - return true; - } - - return false; - } - - } - -} diff --git a/AlphaChiTech.Virtualization/DateBasedPageExpiryComparer.cs b/AlphaChiTech.Virtualization/DateBasedPageExpiryComparer.cs deleted file mode 100644 index 76db637..0000000 --- a/AlphaChiTech.Virtualization/DateBasedPageExpiryComparer.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - /// - /// An implementation of a IPageExiryComparer that uses DateTime to see if the update should be applied - /// - public class DateBasedPageExpiryComparer : IPageExpiryComparer - { - - private static DateBasedPageExpiryComparer _Instance = new DateBasedPageExpiryComparer(); - - /// - /// Gets the default instance. - /// - /// - /// The default instance. - /// - public static DateBasedPageExpiryComparer DefaultInstance - { - get - { - return _Instance; - } - } - - /// - /// Determines whether [is update valid] [the specified page based on the updateAt]. - /// - /// The page update at - null or a DateTime. - /// The update at - null or a DateTime. - /// - public bool IsUpdateValid(object pageUpdateAt, object updateAt) - { - bool isStillValid = true; - - if (pageUpdateAt != null && updateAt != null && pageUpdateAt is DateTime && updateAt is DateTime) - { - if (((DateTime)pageUpdateAt) >= ((DateTime)updateAt)) - { - isStillValid = false; - } - } - - return isStillValid; - } - } -} diff --git a/AlphaChiTech.Virtualization/Documentation/PaginationSystem.cd b/AlphaChiTech.Virtualization/Documentation/PaginationSystem.cd deleted file mode 100644 index d9b495b..0000000 --- a/AlphaChiTech.Virtualization/Documentation/PaginationSystem.cd +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - AGUEAIKAAA5EQCBAIAkEpACYJoIwAIBgAUJIMIGkAQU= - PaginationManager.cs - - - - - - - - - - - - - - - - - - - QAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - PageDelta.cs - - - - - - BUAAAAAAABAQIAgAAACAAIABAAIIAQEIiIQIAAAAAAA= - SourcePage.cs - - - - - - - - - - - - - AAgAAAAAAAAAAACAAAACAAAAAAAAAAAAAAAAAAAIAAA= - PageReclaimOnTouched.cs - - - - - - - - - AAAAAAAAAAAAAAAAAAAEAAEAAAAAABAAAAAAAAAAAAA= - DateBasedPageExpiryComparer.cs - - - - - - - - - - - AAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAEAAA= - ISourcePage.cs - - - - - - - - - - BEAAAAAAAAAQIAgAAACAAAABAAIIAQEIiAQAAAAAAAA= - ISourcePage.cs - - - - - - AEAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAIAAA= - IPagedSourceProvider.cs - - - - - - AAgAAAAAAAAAAACAAAACAAAAAAAAAAAAAAAAAAAIAAA= - IPageReclaimer.cs - - - - - - AEAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAIAAAAAAAA= - IPagedSourceProviderAsync.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAA= - IPageExpiryComparer.cs - - - - - - AAAIAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAA= - INotifyCountChanged.cs - - - - \ No newline at end of file diff --git a/AlphaChiTech.Virtualization/Documentation/TypicalUsage.cd b/AlphaChiTech.Virtualization/Documentation/TypicalUsage.cd deleted file mode 100644 index 472009e..0000000 --- a/AlphaChiTech.Virtualization/Documentation/TypicalUsage.cd +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - AGEEAICAAAZEQCBAIAkEJASQIoIwAIAgAUBIMAGkAQU= - PaginationManager.cs - - - - - - - - - - - - - - - BUJABEACAACEABCQCBIAlAS4gJAQAAAACAAAIAhwISQ= - VirtualizingObservableCollection.cs - - - - - - - - - - AEAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAIAAA= - IPagedSourceProvider.cs - - - - - - AEAAAAAAAAAAAAAAAAAAAAQAAAIAAAAAAAAAAAAAAAA= - IItemSourceProvider.cs - - - - - - AAAEAAAAAAAAAAAAAAEABAAAAAAAAAAAAAAAAACAAAA= - IEditableProvider.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAA= - PaginationManager.cs - - - - \ No newline at end of file diff --git a/AlphaChiTech.Virtualization/ExecuteResetWA.cs b/AlphaChiTech.Virtualization/ExecuteResetWA.cs deleted file mode 100644 index ddddfd2..0000000 --- a/AlphaChiTech.Virtualization/ExecuteResetWA.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class ExecuteResetWA : BaseActionVirtualization - { - WeakReference _VOC; - - public ExecuteResetWA(VirtualizingObservableCollection voc) - : base(VirtualActionThreadModelEnum.UseUIThread) - { - _VOC = new WeakReference(voc); - } - - public override void DoAction() - { - var voc = (VirtualizingObservableCollection)_VOC.Target; - - if (voc != null && _VOC.IsAlive) - { - voc.RaiseCollectionChangedEvent(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - } - - } -} diff --git a/AlphaChiTech.Virtualization/IBaseSourceProvider.cs b/AlphaChiTech.Virtualization/IBaseSourceProvider.cs deleted file mode 100644 index 71482be..0000000 --- a/AlphaChiTech.Virtualization/IBaseSourceProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IBaseSourceProvider - { - void OnReset(int count); - } -} diff --git a/AlphaChiTech.Virtualization/IEditableProvider.cs b/AlphaChiTech.Virtualization/IEditableProvider.cs deleted file mode 100644 index 27312d0..0000000 --- a/AlphaChiTech.Virtualization/IEditableProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IEditableProvider - { - int OnAppend(T item, object timestamp); - void OnInsert(int index, T item, object timestamp); - void OnRemove(int index, T item, object timestamp); - void OnReplace(int index, T oldItem, T newItem, object timestamp); - } -} diff --git a/AlphaChiTech.Virtualization/INotifyCountChanged.cs b/AlphaChiTech.Virtualization/INotifyCountChanged.cs deleted file mode 100644 index c7f0715..0000000 --- a/AlphaChiTech.Virtualization/INotifyCountChanged.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public delegate void OnCountChanged(object sender, CountChangedEventArgs args); - - public class CountChangedEventArgs : EventArgs - { - public bool NeedsReset { get; set; } - public int Count { get; set; } - } - - public interface INotifyCountChanged - { - event OnCountChanged CountChanged; - } -} diff --git a/AlphaChiTech.Virtualization/IPageExpiryComparer.cs b/AlphaChiTech.Virtualization/IPageExpiryComparer.cs deleted file mode 100644 index 860df8c..0000000 --- a/AlphaChiTech.Virtualization/IPageExpiryComparer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IPageExpiryComparer - { - bool IsUpdateValid(object pageUpdateAt, object updateAt); - } -} diff --git a/AlphaChiTech.Virtualization/IPagedSourceProviderAsync.cs b/AlphaChiTech.Virtualization/IPagedSourceProviderAsync.cs deleted file mode 100644 index f85e216..0000000 --- a/AlphaChiTech.Virtualization/IPagedSourceProviderAsync.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public interface IPagedSourceProviderAsync : IPagedSourceProvider - { - Task> GetItemsAtAsync(int pageoffset, int count, bool usePlaceholder); - - T GetPlaceHolder(int index, int page, int offset); - - Task GetCountAsync(); - - Task IndexOfAsync( T item ); - } -} diff --git a/AlphaChiTech.Virtualization/IReclaimableService.cs b/AlphaChiTech.Virtualization/IReclaimableService.cs deleted file mode 100644 index ec7ca47..0000000 --- a/AlphaChiTech.Virtualization/IReclaimableService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IReclaimableService - { - void RunClaim(string sectionContext); - } -} diff --git a/AlphaChiTech.Virtualization/IRepeatingVirtualizationAction.cs b/AlphaChiTech.Virtualization/IRepeatingVirtualizationAction.cs deleted file mode 100644 index a97421e..0000000 --- a/AlphaChiTech.Virtualization/IRepeatingVirtualizationAction.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public interface IRepeatingVirtualizationAction - { - bool KeepInActionsList(); - bool IsDueToRun(); - } -} diff --git a/AlphaChiTech.Virtualization/ISourcePage.cs b/AlphaChiTech.Virtualization/ISourcePage.cs deleted file mode 100644 index d78c6a4..0000000 --- a/AlphaChiTech.Virtualization/ISourcePage.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class SourcePagePendingUpdates - { - public INotifyCollectionChanged Args { get; set; } - public Object UpdatedAt { get; set; } - } - - public interface ISourcePage - { - int Page { get; set; } - - int ItemsPerPage { get; set; } - - bool CanReclaimPage { get; } - - Object LastTouch { get; set; } - - T GetAt(int offset); - - int Append(T item, object updatedAt, IPageExpiryComparer comparer); - - int IndexOf(T item); - - void InsertAt(int offset, T item, object updatedAt, IPageExpiryComparer comparer); - - bool RemoveAt(int offset, object updatedAt, IPageExpiryComparer comparer); - - void ReplaceAt(int offset, T oldItem, T newItem, object updatedAt, IPageExpiryComparer comparer); - - PageFetchStateEnum PageFetchState { get; set; } - - Object WiredDateTime { get; set; } - - bool ReplaceNeeded(int offset); - - List PendingUpdates { get; } - - int ItemsCount { get; } - } -} diff --git a/AlphaChiTech.Virtualization/PageDelta.cs b/AlphaChiTech.Virtualization/PageDelta.cs deleted file mode 100644 index 0aafbc3..0000000 --- a/AlphaChiTech.Virtualization/PageDelta.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - internal class PageDelta - { - public int Page { get; set; } - public int Delta { get; set; } - } -} diff --git a/AlphaChiTech.Virtualization/PageFetchStateEnum.cs b/AlphaChiTech.Virtualization/PageFetchStateEnum.cs deleted file mode 100644 index 280f995..0000000 --- a/AlphaChiTech.Virtualization/PageFetchStateEnum.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public enum PageFetchStateEnum - { - Fetched, - Placeholders - } -} diff --git a/AlphaChiTech.Virtualization/PageReclaimOnTouched.cs b/AlphaChiTech.Virtualization/PageReclaimOnTouched.cs deleted file mode 100644 index 75f92df..0000000 --- a/AlphaChiTech.Virtualization/PageReclaimOnTouched.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - /// - /// PageReclainOnTouched is a Page Reclaimer implementation that releases pages based on when - /// they where last touched. - /// - /// - public class PageReclaimOnTouched : IPageReclaimer - { - - /// - /// Reclaims the pages. - /// - /// The pages. - /// The pages needed. - /// The section context. - /// - public IEnumerable> ReclaimPages(IEnumerable> pages, int pagesNeeded, string sectionContext) - { - List> ret = new List>(); - - var candiadates = (from p in pages where p.CanReclaimPage == true orderby p.LastTouch select p).Take(pagesNeeded); - - foreach (var c in candiadates) ret.Add(c); - - return ret; - } - - /// - /// Called when [page touched]. - /// - /// The page. - public void OnPageTouched(ISourcePage page) - { - page.LastTouch = DateTime.Now; - } - - /// - /// Called when [page released]. - /// - /// The page. - public void OnPageReleased(ISourcePage page) - { - } - - /// - /// Makes the page. - /// - /// The page. - /// The size. - /// - public ISourcePage MakePage(int page, int size) - { - return new SourcePage() { Page = page, ItemsPerPage = size }; - } - - } - -} diff --git a/AlphaChiTech.Virtualization/PagedSourceItemsPacket.cs b/AlphaChiTech.Virtualization/PagedSourceItemsPacket.cs deleted file mode 100644 index 5df2f1d..0000000 --- a/AlphaChiTech.Virtualization/PagedSourceItemsPacket.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class PagedSourceItemsPacket - { - public IEnumerable Items { get; set; } - private Object _LoadedAt = DateTime.Now; - - public Object LoadedAt - { - get { return _LoadedAt; } - set { _LoadedAt = value; } - } - - } -} diff --git a/AlphaChiTech.Virtualization/PagedSourceProviderMakeSync.cs b/AlphaChiTech.Virtualization/PagedSourceProviderMakeSync.cs deleted file mode 100644 index a2c93a4..0000000 --- a/AlphaChiTech.Virtualization/PagedSourceProviderMakeSync.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public class PagedSourceProviderMakeSync : IPagedSourceProviderAsync, IProviderPreReset - { - public PagedSourceProviderMakeSync() - { - } - - public PagedSourceProviderMakeSync( - Func>> funcGetItemsAtAsync = null, - Func> funcGetCountAsync = null, - Func funcIndexOf = null, - Func> funcIndexOfAsync = null, - Action actionOnReset = null, - Func funcGetPlaceHolder = null, - Action actionOnBeforeReset = null - ) - { - this.FuncGetItemsAtAsync = funcGetItemsAtAsync; - this.FuncGetCountAsync = funcGetCountAsync; - this.FuncIndexOf = funcIndexOf; - this.FuncIndexOfAsync = funcIndexOfAsync; - this.ActionOnReset = actionOnReset; - this.FuncGetPlaceHolder = funcGetPlaceHolder; - this.ActionOnBeforeReset = actionOnBeforeReset; - } - - public virtual void OnBeforeReset() - { - if(this.ActionOnBeforeReset != null) - { - this.ActionOnBeforeReset.Invoke(); - } - } - - Action _ActionOnBeforeReset = null; - - public Action ActionOnBeforeReset - { - get { return _ActionOnBeforeReset; } - set { _ActionOnBeforeReset = value; } - } - - - Func> _FuncIndexOfAsync = null; - - public Func> FuncIndexOfAsync - { - get { return _FuncIndexOfAsync; } - set { _FuncIndexOfAsync = value; } - } - - private Func>> _FuncGetItemsAtAsync = null; - - public Func>> FuncGetItemsAtAsync - { - get { return _FuncGetItemsAtAsync; } - set { _FuncGetItemsAtAsync = value; } - } - - private Func _FuncGetPlaceHolder = null; - - public Func FuncGetPlaceHolder - { - get { return _FuncGetPlaceHolder; } - set { _FuncGetPlaceHolder = value; } - } - - private Func> _FuncGetCountAsync = null; - - public Func> FuncGetCountAsync - { - get { return _FuncGetCountAsync; } - set { _FuncGetCountAsync = value; } - } - - private Func _FuncIndexOf = null; - - public Func FuncIndexOf - { - get { return _FuncIndexOf; } - set { _FuncIndexOf = value; } - } - - private Action _ActionOnReset = null; - - public Action ActionOnReset - { - get { return _ActionOnReset; } - set { _ActionOnReset = value; } - } - - public virtual Task> GetItemsAtAsync(int pageoffset, int count, bool usePlaceholder) - { - if (_FuncGetItemsAtAsync != null) - return _FuncGetItemsAtAsync.Invoke(pageoffset, count); - - return null; - } - - public virtual T GetPlaceHolder(int index, int page, int offset) - { - T ret = default(T); - - if (_FuncGetPlaceHolder != null) ret = _FuncGetPlaceHolder.Invoke(index, page, offset); - - return ret; - } - - public virtual Task GetCountAsync() - { - Task ret = null; - - if (_FuncGetCountAsync != null) - { - ret = _FuncGetCountAsync.Invoke(); - } - - return ret; - } - - public PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder) - { - PagedSourceItemsPacket ret = null; - - return Task.Run( () => GetItemsAtAsync(pageoffset, count, usePlaceholder)).Result; - - } - - public int Count - { - get { return Task.Run( () => GetCountAsync()).Result; } - } - - public virtual int IndexOf(T item) - { - int ret = -1; - - if (_FuncIndexOf != null) - { - ret = _FuncIndexOf.Invoke(item); - } - else if (_FuncIndexOfAsync != null) - { - ret = Task.Run( () => _FuncIndexOfAsync.Invoke(item)).Result; - } - else - { - ret = Task.Run(() => IndexOfAsync(item)).Result; - } - - return ret; - } - - public virtual async Task IndexOfAsync(T item) - { - int ret = -1; - - return ret; - } - - public virtual void OnReset(int count) - { - if (_ActionOnReset != null) _ActionOnReset.Invoke(count); - } - } -} diff --git a/AlphaChiTech.Virtualization/PaginationManager.cs b/AlphaChiTech.Virtualization/PaginationManager.cs deleted file mode 100644 index 79a18c7..0000000 --- a/AlphaChiTech.Virtualization/PaginationManager.cs +++ /dev/null @@ -1,1355 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - - public interface IAsyncResetProvider - { - Task GetCountAsync(); - } - - public interface IProviderPreReset - { - void OnBeforeReset(); - } - - public class PaginationManager : IItemSourceProvider, IEditableProvider, IReclaimableService, IAsyncResetProvider, IProviderPreReset, INotifyCountChanged - { - Dictionary> _Pages = new Dictionary>(); - - Dictionary _Deltas = new Dictionary(); - - Dictionary _Tasks = new Dictionary(); - - IPageReclaimer _Reclaimer = null; - - IPageExpiryComparer _ExpiryComparer = null; - - bool _HasGotCount = false; - int _LocalCount = 0; - - public IPageExpiryComparer ExpiryComparer - { - get { return _ExpiryComparer; } - set { _ExpiryComparer = value; } - } - - protected void CancelPageRequest(int page) - { - lock (_PageLock) - { - if (_Tasks.ContainsKey(page)) - { - try - { - _Tasks[page].Cancel(); - } - catch (Exception e1) - { - - } - - try - { - _Tasks.Remove(page); - } - catch (Exception e2) - { - - } - } - } - } - - protected void CancelAllRequests() - { - lock (_PageLock) - { - var c = _Tasks.Values.ToList(); - foreach (var t in c) - { - try - { - t.Cancel(false); - } - catch (Exception e) - { - - } - } - - _Tasks.Clear(); - } - } - - protected void RemovePageRequest(int page) - { - lock (_PageLock) - { - if (_Tasks.ContainsKey(page)) - { - try - { - _Tasks.Remove(page); - } - catch (Exception e) - { - - } - } - } - } - - protected CancellationTokenSource StartPageRequest(int page) - { - CancellationTokenSource cts = new CancellationTokenSource(); - - - CancelPageRequest(page); - - lock (_PageLock) - { - if (!_Tasks.ContainsKey(page)) - { - _Tasks.Add(page, cts); - } - else - { - _Tasks[page] = cts; - } - } - - return cts; - } - - /// - /// Initializes a new instance of the class. - /// - /// The provider. - /// The reclaimer. - /// The expiry comparer. - /// Size of the page. - /// The maximum pages. - /// The maximum deltas. - /// The maximum distance. - /// The section context. - public PaginationManager(IPagedSourceProvider provider, - IPageReclaimer reclaimer = null, - IPageExpiryComparer expiryComparer = null, - int pageSize = 100, - int maxPages = 100, - int maxDeltas = -1, - int maxDistance = -1, - string sectionContext = "" - ) - { - this.PageSize = pageSize; - this.MaxPages = maxPages; - this.MaxDeltas = maxDeltas; - this.MaxDistance = maxDistance; - - if (provider is IPagedSourceProviderAsync) - { - this.ProviderAsync = (IPagedSourceProviderAsync)provider; - } - else - { - this.Provider = provider; - } - - if (reclaimer != null) - { - _Reclaimer = reclaimer; - } - else - { - _Reclaimer = new PageReclaimOnTouched(); - } - - this.ExpiryComparer = expiryComparer; - - VirtualizationManager.Instance.AddAction(new ReclaimPagesWA(this, sectionContext)); - } - - - - /// - /// Adds the or update adjustment. - /// - /// The page. - /// The offset change. - public int AddOrUpdateAdjustment(int page, int offsetChange) - { - int ret = 0; - - lock (_PageLock) - { - if (!_Deltas.ContainsKey(page)) - { - if (this.MaxDeltas == -1 || _Deltas.Count < this.MaxDeltas) - { - ret = offsetChange; - _Deltas.Add(page, new PageDelta() { Page = page, Delta = offsetChange }); - } - else - { - DropAllDeltasAndPages(); - } - } - else - { - var adjustment = _Deltas[page]; - adjustment.Delta += offsetChange; - - if (adjustment.Delta == 0) - { - _Deltas.Remove(page); - } - - ret = adjustment.Delta; - } - } - - return ret; - } - - /// - /// Drops all deltas and pages. - /// - protected void DropAllDeltasAndPages() - { - lock (_PageLock) - { - _Deltas.Clear(); - _Pages.Clear(); - _BasePage = 0; - CancelAllRequests(); - } - } - - /// - /// Gets the provider as editable. - /// - /// - /// - protected IEditableProvider GetProviderAsEditable() - { - IEditableProvider ret = null; - - if (this.Provider != null) - { - ret = this.Provider as IEditableProvider; - - } - else - { - ret = this.ProviderAsync as IEditableProvider; - } - - return ret; - } - - // The _LastXXX are used for optimizations.. - private int _LastIndex = -1; - private int _LastPage = -1; - private int _LastOffset = -1; - - /// - /// Clears the optimizations. - /// - protected void ClearOptimizations() - { - _LastIndex = -1; - } - - /* Old implementation - - /// - /// Calculates the page and the offset from the index. - /// - /// The index. - /// The index adjustment. - /// The page. - /// The inneroffset. - protected void CalculateFromIndex(int index, int indexAdjustment, out int page, out int inneroffset, int adjustmentsAppliedToPages = -1) - { - if (adjustmentsAppliedToPages == -1 && 1==0) - { - // See if we can use some optimization.. aka its the same index as last time.. - if (_LastIndex != -1 && index == _LastIndex) - { - page = _LastPage; - inneroffset = _LastOffset; - return; - } - // See if we can use some optimization... aka its the next index from last.. - if (_LastIndex != -1 && index == _LastIndex + 1) - { - int basepageg = page = _LastPage; - - inneroffset = _LastOffset + 1; - - int items = this.PageSize; - if (_Deltas.ContainsKey(basepageg)) - { - - items += _Deltas[basepageg].Delta; - } - - - if (inneroffset >= items) - { - bool got = false; - inneroffset = 0; - basepageg = page = page + 1; - - while (!got) - { - int itemsg = this.PageSize; - if (_Deltas.ContainsKey(basepageg)) - { - - itemsg += _Deltas[basepageg].Delta; - } - - if (inneroffset < itemsg) got = true; - } - } - - _LastIndex = index; - _LastPage = page; - _LastOffset = inneroffset; - return; - } - } - - // First work out the base page from the index and the offset inside that page - int basepage = page = index / this.PageSize; - inneroffset = (index + indexAdjustment) - (page * this.PageSize); - - // We only need to do the rest if there have been modifications to the page sizes on pages (deltas) - if (_Deltas.Count > 0) - { - int adjustment = 0; - - lock (_PageLock) - { - // First, get the total adjustments for any pages BEFORE the current page.. - adjustment = (from d in _Deltas.Values where d.Page < basepage && d.Page > adjustmentsAppliedToPages select d.Delta).Sum(); - } - - // If we do have adjustments... - if (adjustment != 0) - { - // cull down the inner offset by the adjustments (so an extra item reduces the offset by one etc) - inneroffset -= adjustment; - - if (inneroffset < 0) - { - while (inneroffset < 0 && page >= 0) - { - page = --basepage; - var items = this.PageSize; - - if (_Deltas.ContainsKey(basepage)) - { - - items += _Deltas[basepage].Delta; - } - - - inneroffset = items + inneroffset; - } - - - // We should be on an earlier page, so recurse using the adjustments - //CalculateFromIndex(index - adjustment, adjustment, out page, out inneroffset, basepage); - } - else if (inneroffset >= this.PageSize) - { - // If the inneroffset seems to be on a later page, but we need to check to see if this page is expanded - if (!_Deltas.ContainsKey(basepage)) - { - // Its not expanded, so recurse in using the adjusted index - CalculateFromIndex(index - adjustment, 0, out page, out inneroffset, basepage); - } - else - { - var delta = _Deltas[basepage]; - // It is expanded, see if the expanded page contains this offset - if (inneroffset < (this.PageSize + delta.Delta)) - { - // Its just fine - } - else - { - // No it does not include this offset, so recurse in using the adjusted index - CalculateFromIndex(index + delta.Delta, 0, out page, out inneroffset, basepage); - } - } - } - } - else - { - // we dont have any earlier page adjustments, but we might have a short page, so check the offset is within range.. - PageDelta adjustmentForCurrentPage = null; - if (_Deltas.ContainsKey(basepage)) adjustmentForCurrentPage = _Deltas[basepage]; - if (adjustmentForCurrentPage != null && adjustmentForCurrentPage.Delta < 0) - { - if (inneroffset >= this.PageSize + adjustmentForCurrentPage.Delta) - { - // Recurse in using the adjustment for the current page to deal with the offset.. - //CalculateFromIndex(index - adjustmentForCurrentPage.Delta, 0, out page, out inneroffset); - - inneroffset += adjustmentForCurrentPage.Delta; - - while(inneroffset<0) - { - page = ++basepage; - - var items = this.PageSize; - - if (_Deltas.ContainsKey(basepage)) - { - - items += _Deltas[basepage].Delta; - } - - inneroffset += items; - } - } - } - } - } - - if (adjustmentsAppliedToPages == -1) - { - _LastIndex = index; - - _LastPage = basepage; - _LastOffset = inneroffset; - } - - } - */ - - int _BasePage = 0; - - protected void CalculateFromIndex(int index, out int page, out int inneroffset) - { - // First work out the base page from the index and the offset inside that page - int basepage = page = (index / this.PageSize) + _BasePage; - inneroffset = (index+(_BasePage*this.PageSize)) - (page * this.PageSize); - - // We only need to do the rest if there have been modifications to the page sizes on pages (deltas) - if (_Deltas.Count > 0) - { - // Get the adjustment BEFORE checking for a short page, because we are going to adjust for that first.. - int adjustment = 0; - - lock (_PageLock) - { - // First, get the total adjustments for any pages BEFORE the current page.. - adjustment = (from d in _Deltas.Values where d.Page < basepage select d.Delta).Sum(); - } - - // Now check to see if we are currently in a short page - in which case we need to adjust for that - if (_Deltas.ContainsKey(page)) - { - int delta = _Deltas[page].Delta; - - if(delta<0) - { - // In a short page, are we over the edge ? - if(inneroffset >= this.PageSize+delta) - { - int step = inneroffset-(this.PageSize+delta-1); - inneroffset -= step; - DoStepForward(ref page, ref inneroffset, step); - } - } - } - - // If we do have adjustments... - if (adjustment != 0) - { - if(adjustment>0) - { - // items have been added to earlier pages, so we need to step back - DoStepBackwards(ref page, ref inneroffset, adjustment); - } - else - { - // items have been removed from earlier pages, so we need to step forward - DoStepForward(ref page, ref inneroffset, Math.Abs(adjustment)); - } - } - - } - - } - - private int _StepToJumpThreashold = 10; - - public int StepToJumpThreashold - { - get { return _StepToJumpThreashold; } - set { _StepToJumpThreashold = value; } - } - - private void DoStepBackwards(ref int page, ref int offset, int stepAmount) - { - bool done = false; - int ignoreSteps = -1; - - while(!done) - { - - if (stepAmount > this.PageSize * StepToJumpThreashold && ignoreSteps <= 0) - { - int targetPage = page - stepAmount/this.PageSize; - int sourcePage = page; - var adj = (from a in _Deltas.Values where a.Page >= targetPage && a.Page <= sourcePage orderby a.Page select a); - if(adj.Count() == 0) - { - page = targetPage; - stepAmount -= (sourcePage - targetPage) * this.PageSize; - - if(stepAmount == 0) - { - done = true; - } - } else if(adj.Last().Page < page-2) - { - targetPage = adj.Last().Page + 1; - page = targetPage; - stepAmount -= (sourcePage - targetPage) * this.PageSize; - - if (stepAmount == 0) - { - done = true; - } - } - else - { - ignoreSteps = sourcePage - adj.Last().Page; - } - } - - if (!done) - { - int items = this.PageSize; - if (_Deltas.ContainsKey(page)) items += _Deltas[page].Delta; - if (offset - stepAmount < 0) - { - stepAmount -= (offset + 1); - page--; - items = this.PageSize; - if (_Deltas.ContainsKey(page)) items += _Deltas[page].Delta; - offset = items - 1; - } - else - { - offset -= stepAmount; - done = true; - } - - ignoreSteps--; - } - } - } - - private void DoStepForward(ref int page, ref int offset, int stepAmount) - { - bool done = false; - int ignoreSteps = -1; - - while(!done) - { - - if (stepAmount > this.PageSize * StepToJumpThreashold && ignoreSteps <= 0) - { - int targetPage = page + stepAmount / this.PageSize; - int sourcePage = page; - var adj = (from a in _Deltas.Values where a.Page <= targetPage && a.Page >= sourcePage orderby a.Page select a); - if (adj.Count() == 0) - { - page = targetPage; - stepAmount -= (targetPage - sourcePage) * this.PageSize; - - if (stepAmount == 0) - { - done = true; - } - } - else if (adj.Last().Page > page - 2) - { - targetPage = adj.Last().Page - 1; - page = targetPage; - stepAmount -= (targetPage - sourcePage) * this.PageSize; - - if (stepAmount == 0) - { - done = true; - } - } else - { - ignoreSteps = adj.Last().Page - sourcePage; - } - } - - if (!done) - { - int items = this.PageSize; - if (_Deltas.ContainsKey(page)) items += _Deltas[page].Delta; - if (items <= offset + stepAmount) - { - stepAmount -= (items) - offset; - offset = 0; - page++; - } - else - { - offset += stepAmount; - done = true; - } - - ignoreSteps--; - } - } - } - IPagedSourceProvider _Provider = null; - - /// - /// Gets or sets the provider. - /// - /// - /// The provider. - /// - public IPagedSourceProvider Provider - { - get { return _Provider; } - set { _Provider = value; } - } - - IPagedSourceProviderAsync _ProviderAsync = null; - - /// - /// Gets or sets the provider asynchronous. - /// - /// - /// The provider asynchronous. - /// - public IPagedSourceProviderAsync ProviderAsync - { - get { return _ProviderAsync; } - set { _ProviderAsync = value; } - } - - int _PageSize = 100; - - /// - /// Gets or sets the size of the page. - /// - /// - /// The size of the page. - /// - public int PageSize - { - get { return _PageSize; } - set - { - DropAllDeltasAndPages(); - _PageSize = value; - } - } - - int _MaxPages = 100; - - /// - /// Gets or sets the maximum pages. - /// - /// - /// The maximum pages. - /// - public int MaxPages - { - get { return _MaxPages; } - set { _MaxPages = value; } - } - - int _MaxDeltas = -1; - - /// - /// Gets or sets the maximum deltas. - /// - /// - /// The maximum deltas. - /// - public int MaxDeltas - { - get { return _MaxDeltas; } - set { _MaxDeltas = value; } - } - - int _MaxDistance = -1; - - /// - /// Gets or sets the maximum distance. - /// - /// - /// The maximum distance. - /// - public int MaxDistance - { - get { return _MaxDistance; } - set { _MaxDistance = value; } - } - - protected object _PageLock = new Object(); - - /// - /// Gets at. - /// - /// The index. - /// The voc. - /// if set to true [use placeholder]. - /// - public T GetAt(int index, object voc, bool usePlaceholder = true) - { - T ret = default(T); - - int page; - int offset; - - CalculateFromIndex(index, out page, out offset); - - var datapage = SafeGetPage(page, usePlaceholder, voc, index); - - if (datapage != null) ret = datapage.GetAt(offset); - - if (ret == null) - { - - } - - //Debug.WriteLine("Get at index:" + index + "returned:" + ret.ToString() + " page=" + page + " offset=" + offset); - return ret; - } - - /// - /// Fills the page. - /// - /// The new page. - /// The page offset. - void FillPage(ISourcePage newPage, int pageOffset) - { - - var data = this.Provider.GetItemsAt(pageOffset, newPage.ItemsPerPage, false); - newPage.WiredDateTime = data.LoadedAt; - foreach (var o in data.Items) - { - newPage.Append(o, null, this.ExpiryComparer); - } - - newPage.PageFetchState = PageFetchStateEnum.Fetched; - } - - /// - /// Fills the page from asynchronous provider. - /// - /// The new page. - /// The page offset. - void FillPageFromAsyncProvider(ISourcePage newPage, int pageOffset) - { - var data = this.ProviderAsync.GetItemsAt(pageOffset, newPage.ItemsPerPage, false); - newPage.WiredDateTime = data.LoadedAt; - foreach (var o in data.Items) - { - newPage.Append(o, null, this.ExpiryComparer); - } - newPage.PageFetchState = PageFetchStateEnum.Fetched; - - } - - /// - /// Gets the count. - /// - /// - /// The count. - /// - public int GetCount(bool asyncOK) - { - - int ret = 0; - - if (!_HasGotCount) - { - lock (this) - { - if (!IsAsync) - { - ret = this.Provider.Count; - _LocalCount = ret; - } - else - { - if (!asyncOK) - { - ret = this.ProviderAsync.GetCountAsync().Result; - //ret = this.ProviderAsync.Count; - _LocalCount = ret; - } - else - { - ret = 0; - var cts = StartPageRequest(Int32.MinValue); - GetCountAsync(cts); - } - } - - _HasGotCount = true; - } - } - - return _LocalCount; - - } - - public async Task GetCountAsync() - { - int ret = 0; - - - if (!IsAsync) - { - ret = this.Provider.Count; - } - else - { - ret = await this.ProviderAsync.GetCountAsync(); - } - - _HasGotCount = true; - - return ret; - } - - private async void GetCountAsync(CancellationTokenSource cts) - { - if (!cts.IsCancellationRequested) - { - int ret = await this.ProviderAsync.GetCountAsync(); - - if (!cts.IsCancellationRequested) - { - lock (this) - { - _HasGotCount = true; - _LocalCount = ret; - } - } - - if (!cts.IsCancellationRequested) - this.RaiseCountChanged(true, _LocalCount); - } - - RemovePageRequest(Int32.MinValue); - } - - /// - /// Gets the Index of item. - /// - /// The item. - /// the index of the item, or -1 if not found - public int IndexOf(T item) - { - // Attempt to get the item from the pages, else call the provider to get it.. - lock (_PageLock) - { - - foreach (var p in _Pages) - { - int o = p.Value.IndexOf(item); - if (o >= 0) - { - return o + ((p.Key - _BasePage) * this.PageSize) + (from d in _Deltas.Values where d.Page < p.Key select d.Delta).Sum(); - } - } - } - - if (!IsAsync) - { - return this.Provider.IndexOf(item); - } - else - { - return this.ProviderAsync.IndexOfAsync( item ).Result; - //return this.ProviderAsync.IndexOf(item); - } - } - - /// - /// Resets the specified count. - /// - /// The count. - public void OnReset(int count) - { - CancelAllRequests(); - - lock (_PageLock) - { - DropAllDeltasAndPages(); - } - - ClearOptimizations(); - - if (count < 0) - { - _HasGotCount = false; - } - else - { - lock (this) - { - _LocalCount = count; - _HasGotCount = true; - } - } - - if (!IsAsync) - { - this.Provider.OnReset(count); - } - else - { - this.ProviderAsync.OnReset(count); - } - - if(count >= -1) - RaiseCountChanged(true, count); - - } - - public void OnBeforeReset() - { - if(!IsAsync) - { - if(this.Provider is IProviderPreReset) - { - (this.Provider as IProviderPreReset).OnBeforeReset(); - } - } - else - { - if(this.ProviderAsync is IProviderPreReset) - { - (this.ProviderAsync as IProviderPreReset).OnBeforeReset(); - } - } - } - - /// - /// Raises the count changed. - /// - /// The count. - protected void RaiseCountChanged(bool needsReset, int count) - { - var evnt = this.CountChanged; - if (evnt != null) - { - evnt(this, new CountChangedEventArgs() { NeedsReset = needsReset, Count = count }); - } - } - - /// - /// Occurs when [count changed]. - /// - public event OnCountChanged CountChanged; - - #region IEditableProvider Implementation - - /// - /// Called when [append]. - /// - /// The item. - /// The timestamp. - /// - public int OnAppend(T item, object timestamp) - { - ClearOptimizations(); - - int index = _LocalCount; - - int page; int offset; - - if (!_HasGotCount) EnsureCount(); - - CalculateFromIndex(index, out page, out offset); - - if (IsPageWired(page)) - { - bool shortpage = false; - var dataPage = SafeGetPage(page, false, null, index); - if (dataPage.ItemsPerPage < this.PageSize) shortpage = true; - - dataPage.Append(item, timestamp, this.ExpiryComparer); - - if(shortpage) - { - dataPage.ItemsPerPage++; - } - else - { - AddOrUpdateAdjustment(page, 1); - } - - } - - _LocalCount++; - - ClearOptimizations(); - - if (this.IsAsync) - { - var test = this.GetAt(index, this, false); - } - - - var edit = GetProviderAsEditable(); - if (edit != null) - { - edit.OnInsert(index, item, timestamp); - } - - ClearOptimizations(); - - return index; - } - - /// - /// Gets the page, if use placeholders is false - then gets page sync else async. - /// - /// The page. - /// if set to true [allow placeholders]. - /// The voc. - /// The index that this page refers to (effectively the pageoffset. - /// - protected ISourcePage SafeGetPage(int page, bool allowPlaceholders, object voc, int index) - { - ISourcePage ret = null; - - lock (_PageLock) - { - if (_Pages.ContainsKey(page)) - { - ret = _Pages[page]; - _Reclaimer.OnPageTouched(ret); - } - else - { - PageDelta delta = null; - if (_Deltas.ContainsKey(page)) delta = _Deltas[page]; - int pageOffset = (page - _BasePage) * this.PageSize + (from d in _Deltas.Values where d.Page < page select d.Delta).Sum(); - int pageSize = Math.Min(this.PageSize, this.GetCount(false)-pageOffset); - if (delta != null) pageSize += delta.Delta; - var newPage = this._Reclaimer.MakePage(page, pageSize); - _Pages.Add(page, newPage); - - if (!IsAsync) - { - FillPage(newPage, pageOffset); - - ret = newPage; - } - else - { - bool up = allowPlaceholders; - - if (up && voc != null) - { - // Fill with placeholders - //Debug.WriteLine("Filling with placeholders, pagesize=" + pageSize); - for (int loop = 0; loop < pageSize; loop++) - { - newPage.Append(this.ProviderAsync.GetPlaceHolder(newPage.Page * pageSize + loop, newPage.Page, loop), null, this.ExpiryComparer); - } - - ret = newPage; - - CancellationTokenSource cts = StartPageRequest(newPage.Page); - Task.Run(() => DoRealPageGet(voc, newPage, pageOffset, index, cts)); - } - else - { - FillPageFromAsyncProvider(newPage, pageOffset); - ret = newPage; - } - } - } - } - - return ret; - } - - private async void DoRealPageGet(Object voc, ISourcePage page, int pageOffset, int index, CancellationTokenSource cts) - { - //Debug.WriteLine("DoRealPageGet: pageOffset=" + pageOffset + " index=" + index); - VirtualizingObservableCollection realVOC = (VirtualizingObservableCollection)voc; - List> listOfReplaces = new List>(); - - if (realVOC != null) - { - if (cts.IsCancellationRequested) return; - - var data = await ProviderAsync.GetItemsAtAsync(pageOffset, page.ItemsPerPage, false); - - if (cts.IsCancellationRequested) return; - - page.WiredDateTime = data.LoadedAt; - - int i = 0; - foreach (var item in data.Items) - { - if (cts.IsCancellationRequested) - { - RemovePageRequest(page.Page); - return; - } - - ClearOptimizations(); - if(page.ReplaceNeeded(i)) - { - var old = page.GetAt(i); - if (old == null) - { - - } - - ClearOptimizations(); - //Debug.WriteLine("Replacing:" + old.ToString() + " with " + item.ToString()); - - page.ReplaceAt(i, old, item, null, null); - //VirtualizationManager.Instance.RunOnUI(new PlaceholderReplaceWA(realVOC, old, item, pageOffset+i)); - listOfReplaces.Add(new PlaceholderReplaceWA(realVOC, old, item, pageOffset + i)); - } - else - { - page.ReplaceAt(i, default(T), item, null, null); - } - - i++; - } - - } - - page.PageFetchState = PageFetchStateEnum.Fetched; - - ClearOptimizations(); - foreach (var replace in listOfReplaces) - { - if (cts.IsCancellationRequested) - { - RemovePageRequest(page.Page); - return; - } - VirtualizationManager.Instance.RunOnUI(replace); - } - - RemovePageRequest(page.Page); - } - - protected bool IsPageWired(int page) - { - bool wired = false; - - lock (_PageLock) - { - if (_Pages.ContainsKey(page)) wired = true; - } - - return wired; - } - - public void OnInsert(int index, T item, object timestamp) - { - int page; int offset; - - if (!_HasGotCount) EnsureCount(); - - CalculateFromIndex(index, out page, out offset); - - if (IsPageWired(page)) - { - var dataPage = SafeGetPage(page, false, null, index); - dataPage.InsertAt(offset, item, timestamp, this.ExpiryComparer); - } - int adj = AddOrUpdateAdjustment(page, 1); - - if(page == _BasePage && adj == this.PageSize*2) - { - lock (_PageLock) - { - if (IsPageWired(page)) - { - var dataPage = SafeGetPage(page, false, null, index); - ISourcePage newdataPage = null; - if (IsPageWired(page - 1)) - { - newdataPage = SafeGetPage(page-1, false, null, index); - } - else - { - newdataPage = this._Reclaimer.MakePage(page - 1, this.PageSize); - _Pages.Add(page - 1, newdataPage); - } - - for (int loop = 0; loop < this.PageSize; loop++) - { - var i = dataPage.GetAt(0); - - dataPage.RemoveAt(0, null, null); - newdataPage.Append(i, null, null); - } - - } - - AddOrUpdateAdjustment(page, -this.PageSize); - - _BasePage--; - } - } - - if (this.IsAsync) - { - var test = this.GetAt(index, this, false); - } - - var edit = GetProviderAsEditable(); - if (edit != null) - { - edit.OnInsert(index, item, timestamp); - } - - _LocalCount++; - - ClearOptimizations(); - } - - void EnsureCount() - { - GetCount(false); - } - - protected bool IsAsync - { - get - { - return _ProviderAsync != null ? true : false; - } - } - - public void OnRemove(int index, T item, object timestamp) - { - int page; int offset; - - if (!_HasGotCount) EnsureCount(); - - CalculateFromIndex(index, out page, out offset); - - if (IsPageWired(page)) - { - var dataPage = SafeGetPage(page, false, null, index); - dataPage.RemoveAt(offset, timestamp, this.ExpiryComparer); - } - AddOrUpdateAdjustment(page, -1); - - if (page == _BasePage) - { - int items = this.PageSize; - if (_Deltas.ContainsKey(page)) items += _Deltas[page].Delta; - if (items == 0) - { - _Deltas.Remove(page); - _BasePage++; - } - } - - if (this.IsAsync) - { - var test = this.GetAt(index, this, false); - } - - var edit = GetProviderAsEditable(); - if (edit != null) - { - edit.OnRemove(index, item, timestamp); - } - - _LocalCount--; - - ClearOptimizations(); - } - - public void OnReplace(int index, T oldItem, T newItem, object timestamp) - { - int page; int offset; - - CalculateFromIndex(index, out page, out offset); - - if (IsPageWired(page)) - { - var dataPage = SafeGetPage(page, false, null, index); - dataPage.ReplaceAt(offset, oldItem, newItem, timestamp, this.ExpiryComparer); - } - - var edit = GetProviderAsEditable(); - if (edit != null) - { - edit.OnReplace(index, oldItem, newItem, timestamp); - } - } - - #endregion IEditableProvider Implementation - - - public void RunClaim(string sectionContext = "") - { - if (_Reclaimer != null) - { - int needed = 0; - - lock (_PageLock) - { - needed = Math.Max(0, _Pages.Count - this.MaxPages); - if (needed != 0) - { - var l = _Reclaimer.ReclaimPages(_Pages.Values, needed, sectionContext).ToList(); - - foreach (var p in l) - { - if (p.Page != _BasePage) - { - lock (_Pages) - { - if (_Pages.ContainsKey(p.Page)) - { - _Pages.Remove(p.Page); - _Reclaimer.OnPageReleased(p); - } - } - } - } - } - } - } - } - } - -} diff --git a/AlphaChiTech.Virtualization/PlaceholderReplaceWA.cs b/AlphaChiTech.Virtualization/PlaceholderReplaceWA.cs deleted file mode 100644 index 4f4d17b..0000000 --- a/AlphaChiTech.Virtualization/PlaceholderReplaceWA.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class PlaceholderReplaceWA : BaseActionVirtualization - { - private T _OldValue; - private T _NewValue; - private int _Index; - - WeakReference _VOC; - - public PlaceholderReplaceWA(VirtualizingObservableCollection voc, T oldValue, T newValue, int index) - : base(VirtualActionThreadModelEnum.UseUIThread) - { - _VOC = new WeakReference(voc); - _OldValue = oldValue; - _NewValue = newValue; - _Index = index; - } - - public override void DoAction() - { - var voc = (VirtualizingObservableCollection)_VOC.Target; - - if (voc != null && _VOC.IsAlive) - { - voc.ReplaceAt(_Index, _OldValue, _NewValue, null); - } - } - } - -} diff --git a/AlphaChiTech.Virtualization/Properties/AssemblyInfo.cs b/AlphaChiTech.Virtualization/Properties/AssemblyInfo.cs deleted file mode 100644 index 5dd54ed..0000000 --- a/AlphaChiTech.Virtualization/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Resources; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("AlphaChiTech.Virtualization")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("AlphaChi Technology")] -[assembly: AssemblyProduct("AlphaChiTech.Virtualization")] -[assembly: AssemblyCopyright("Copyright © 2015 AlphaChi technology")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: NeutralResourcesLanguage("en")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.0.0")] -[assembly: AssemblyFileVersion("1.2.0.0")] diff --git a/AlphaChiTech.Virtualization/ReclaimPagesWA.cs b/AlphaChiTech.Virtualization/ReclaimPagesWA.cs deleted file mode 100644 index 6a8a11b..0000000 --- a/AlphaChiTech.Virtualization/ReclaimPagesWA.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class ReclaimPagesWA : BaseRepeatableActionVirtualization - { - public ReclaimPagesWA(IReclaimableService provider, string sectionContext) - : base(VirtualActionThreadModelEnum.Background, true, TimeSpan.FromMinutes(1)) - { - _WRProvider = new WeakReference(provider); - } - - WeakReference _WRProvider = null; - - string _SectionContext = ""; - - public override void DoAction() - { - _LastRun = DateTime.Now; - - var reclaimer = _WRProvider.Target as IReclaimableService; - - if (reclaimer != null) - { - reclaimer.RunClaim(_SectionContext); - } - } - - public override bool KeepInActionsList() - { - bool ret = base.KeepInActionsList(); - - if (!_WRProvider.IsAlive) ret = false; - - return ret; - } - } - -} diff --git a/AlphaChiTech.Virtualization/SourcePage.cs b/AlphaChiTech.Virtualization/SourcePage.cs deleted file mode 100644 index f5c0d64..0000000 --- a/AlphaChiTech.Virtualization/SourcePage.cs +++ /dev/null @@ -1,234 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public class SourcePage : ISourcePage - { - protected List _Items = new List(); - - - /// - /// Gets or sets the page. - /// - /// - /// The page. - /// - public int Page { get; set; } - - private List _PendingUpdates = new List(); - - public List PendingUpdates - { - get { return _PendingUpdates; } - } - - /// - /// Gets or sets the items per page. - /// - /// - /// The items per page. - /// - public int ItemsPerPage { get; set; } - - public int ItemsCount - { - get - { - return _Items.Count; - } - } - - private List _ReplaceNeededList = new List(); - - /// - /// Gets a value indicating whether [can reclaim page]. - /// - /// - /// true if [can reclaim page]; otherwise, false. - /// - public bool CanReclaimPage - { - get - { - bool ret = true; - if (this._PageFetchState == PageFetchStateEnum.Placeholders) ret = false; - return ret; - } - } - - /// - /// Determines whether it is safe to update into a page where the pending update was generated at a given time. - /// - /// The updated happened at this datetime. - /// - public bool IsSafeToUpdate(IPageExpiryComparer comparer, object updatedAt) - { - bool ret = true; - - if (comparer != null) - { - ret = comparer.IsUpdateValid(this.WiredDateTime, updatedAt); - } - - //if(updatedAt.HasValue && updatedAt.Value != DateTime.MinValue) - //{ - // if(updatedAt.Value< this.WiredDateTime) - // { - // ret = false; - // } - //} - - return ret; - } - - /// - /// Gets or sets the last touch. - /// - /// - /// The last touch. - /// - public Object LastTouch { get; set; } - - /// - /// Gets at. - /// - /// The offset. - /// - public T GetAt(int offset) - { - T ret = default(T); - - if (this._PageFetchState == PageFetchStateEnum.Placeholders) _ReplaceNeededList.Add(offset); - - if (_Items.Count > offset) ret = _Items[offset]; - - LastTouch = DateTime.Now; - - return ret; - } - - public bool ReplaceNeeded(int offset) - { - bool ret = false; - - if (_ReplaceNeededList.Contains(offset)) ret = true; - - return ret; - } - - /// - /// Appends the specified item. - /// - /// The item. - /// - public int Append(T item, object updatedAt, IPageExpiryComparer comparer) - { - _Items.Add(item); - - LastTouch = DateTime.Now; - - return _Items.IndexOf(item); - } - - /// - /// Inserts at. - /// - /// The offset. - /// The item. - /// The updated at. - public void InsertAt(int offset, T item, object updatedAt, IPageExpiryComparer comparer) - { - if (IsSafeToUpdate(comparer, updatedAt)) - { - if (_Items.Count > offset) - { - _Items.Insert(offset, item); - } - else - { - _Items.Add(item); - } - - } - } - - /// - /// Removes at. - /// - /// The offset. - /// The updated at. - /// - public bool RemoveAt(int offset, object updatedAt, IPageExpiryComparer comparer) - { - bool removed = true; - - if (IsSafeToUpdate(comparer, updatedAt)) - { - _Items.RemoveAt(offset); - } - - return removed; - } - - /// - /// Replaces at. - /// - /// The offset. - /// The old value. - /// The new value. - /// The updated at. - public void ReplaceAt(int offset, T oldValue, T newValue, object updatedAt, IPageExpiryComparer comparer) - { - if (IsSafeToUpdate(comparer, updatedAt)) - { - if (_Items.Count > offset) _Items[offset] = newValue; - } - } - - /// - /// Indexes the of. - /// - /// The item. - /// - public int IndexOf(T item) - { - LastTouch = DateTime.Now; - - return _Items.IndexOf(item); - } - - private PageFetchStateEnum _PageFetchState = PageFetchStateEnum.Placeholders; - - /// - /// Gets or sets the state of the page fetch state. - /// - /// - /// The state of the page fetch. - /// - public PageFetchStateEnum PageFetchState - { - get { return _PageFetchState; } - set { _PageFetchState = value; } - } - - private Object _WiredDateTime = DateTime.MinValue; - - /// - /// Gets or sets the wired date time. - /// - /// - /// The wired date time. - /// - public object WiredDateTime - { - get { return _WiredDateTime; } - set { _WiredDateTime = value; } - } - - - } - -} diff --git a/AlphaChiTech.Virtualization/VirtualActionThreadModelEnum.cs b/AlphaChiTech.Virtualization/VirtualActionThreadModelEnum.cs deleted file mode 100644 index 37839c3..0000000 --- a/AlphaChiTech.Virtualization/VirtualActionThreadModelEnum.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization -{ - public enum VirtualActionThreadModelEnum - { - UseUIThread, - Background - } -} diff --git a/AlphaChiTech.Virtualization/VirtualizationManager.cs b/AlphaChiTech.Virtualization/VirtualizationManager.cs deleted file mode 100644 index 4a62215..0000000 --- a/AlphaChiTech.Virtualization/VirtualizationManager.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public class VirtualizationManager - { - private static VirtualizationManager _Instance = new VirtualizationManager(); - - private List _Actions = new List(); - private object _ActionLock = new object(); - - private static bool _IsInitialized = false; - - public static bool IsInitialized - { - get - { - return _IsInitialized; - } - } - - public static VirtualizationManager Instance - { - get - { - return _Instance; - } - } - - bool _Processing = false; - - private Action _UIThreadExcecuteAction = null; - - public Action UIThreadExcecuteAction - { - get { return _UIThreadExcecuteAction; } - set - { - _UIThreadExcecuteAction = value; - _IsInitialized = true; - } - } - - public void ProcessActions() - { - if (_Processing) return; - - _Processing = true; - - List lst; - lock (_ActionLock) - { - lst = _Actions.ToList(); - } - - foreach (var action in lst) - { - bool bdo = true; - - if (action is IRepeatingVirtualizationAction) - { - bdo = (action as IRepeatingVirtualizationAction).IsDueToRun(); - } - - if (bdo) - { - switch (action.ThreadModel) - { - case VirtualActionThreadModelEnum.UseUIThread: - if (UIThreadExcecuteAction == null) // PLV - throw new Exception( "VirtualizationManager isn’t already initialized ! set the VirtualizationManager’s UIThreadExcecuteAction (VirtualizationManager.Instance.UIThreadExcecuteAction = a => Dispatcher.Invoke( a );)" ); - UIThreadExcecuteAction.Invoke(() => action.DoAction()); - break; - case VirtualActionThreadModelEnum.Background: - Task.Run(() => action.DoAction()); - break; - } - - if (action is IRepeatingVirtualizationAction) - { - if (!(action as IRepeatingVirtualizationAction).KeepInActionsList()) - { - lock (_ActionLock) - { - _Actions.Remove(action); - } - } - } - else - { - lock (_ActionLock) - { - _Actions.Remove(action); - } - } - } - } - - _Processing = false; - } - public void AddAction(IVirtualizationAction action) - { - lock (_ActionLock) - { - _Actions.Add(action); - } - } - - public void AddAction(Action action) - { - AddAction(new ActionVirtualizationWrapper(action)); - } - - public void RunOnUI(IVirtualizationAction action) - { - if (UIThreadExcecuteAction == null) // PLV - throw new Exception( "VirtualizationManager isn’t already initialized ! set the VirtualizationManager’s UIThreadExcecuteAction (VirtualizationManager.Instance.UIThreadExcecuteAction = a => Dispatcher.Invoke( a );)" ); - UIThreadExcecuteAction.Invoke(() => action.DoAction()); - } - - public void RunOnUI(Action action) - { - RunOnUI(new ActionVirtualizationWrapper(action)); - } - } -} diff --git a/AlphaChiTech.Virtualization/VirtualizingObservableCollection.cs b/AlphaChiTech.Virtualization/VirtualizingObservableCollection.cs deleted file mode 100644 index 0e14198..0000000 --- a/AlphaChiTech.Virtualization/VirtualizingObservableCollection.cs +++ /dev/null @@ -1,900 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace AlphaChiTech.Virtualization -{ - public class VirtualizingObservableCollection : IEnumerable, IEnumerable, ICollection, ICollection, IList, IList, INotifyCollectionChanged, INotifyPropertyChanged - { - #region Ctors Etc - - /// - /// Initializes a new instance of the class. - /// - /// The provider. - public VirtualizingObservableCollection(IItemSourceProvider provider) - { - this.Provider = provider; - } - - /// - /// Initializes a new instance of the class. - /// - /// The asynchronous provider. - public VirtualizingObservableCollection(IItemSourceProviderAsync asyncProvider) - { - this.ProviderAsync = asyncProvider; - } - - /// - /// Initializes a new instance of the class. - /// - /// The provider. - /// The optional reclaimer. - /// The optional expiry comparer. - /// Size of the page. - /// The maximum pages. - /// The maximum deltas. - /// The maximum distance. - public VirtualizingObservableCollection( - IPagedSourceProvider provider, - IPageReclaimer reclaimer = null, - IPageExpiryComparer expiryComparer = null, - int pageSize = 100, - int maxPages = 100, - int maxDeltas = -1, - int maxDistance = -1 - ) - { - this.Provider = new PaginationManager(provider, reclaimer, expiryComparer, pageSize, maxPages, maxDeltas, maxDistance); - } - - - #endregion Ctors Etc - - #region IEnumerable Implementation - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - string sc = new Guid().ToString(); - - EnsureCountIsGotNONASync(); - - int count = InternalGetCount(); - - for (int iLoop = 0; iLoop < count; iLoop++) - { - yield return InternalGetValue(iLoop, sc); - } - } - - #endregion IEnumerable Implementation - - #region IEnumerable Implementation - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// A that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - string sc = new Guid().ToString(); - - EnsureCountIsGotNONASync(); - - int count = InternalGetCount(); - - for (int iLoop = 0; iLoop < count; iLoop++) - { - yield return InternalGetValue(iLoop, sc); - } - } - - #endregion IEnumerable Implementation - - #region ICollection Implementation - - public void CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - - /// - /// Gets the number of elements contained in the . - /// - /// The number of elements contained in the . - public int Count - { - get { return InternalGetCount(); } - } - - /// - /// Gets a value indicating whether access to the is synchronized (thread safe). - /// - /// true if access to the is synchronized (thread safe); otherwise, false. - public bool IsSynchronized - { - get { return false; } - } - - private object _SyncRoot = new object(); - - /// - /// Gets an object that can be used to synchronize access to the . - /// - /// An object that can be used to synchronize access to the . - public object SyncRoot - { - get { return _SyncRoot; } - } - - #endregion ICollection Implementation - - #region ICollection Implementation - - /// - /// Adds an item to the . - /// - /// The object to add to the . - public void Add(T item) - { - InternalAdd(item, null); - } - - /// - /// Resets the collection - aka forces a get all data, including the count . - /// - public void Clear() - { - InternalClear(); - } - - /// - /// Determines whether the contains a specific value. - /// - /// The object to locate in the . - /// - /// true if is found in the ; otherwise, false. - /// - public bool Contains(T item) - { - return IndexOf(item) != -1 ? true : false; - } - - public void CopyTo(T[] array, int arrayIndex) - { - throw new NotImplementedException(); - } - - /// - /// Gets a value indicating whether the is read-only. - /// - /// true if the is read-only; otherwise, false. - public bool IsReadOnly - { - get { return false; } - } - - /// - /// Removes the first occurrence of a specific object from the . - /// - /// The object to remove from the . - /// - /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . - /// - public bool Remove(T item) - { - return InternalRemoveAt(IndexOf(item)); - } - - #endregion ICollection Implementation - - #region Extended CRUD operators that take into account the DateTime of the change - - /// - /// Removes the specified item - extended to only remove the item if the page was not pulled before the updatedat DateTime. - /// - /// The item. - /// The updated at. - /// - public bool Remove(T item, object updatedAt) - { - return InternalRemoveAt(IndexOf(item), updatedAt); - } - - /// - /// Removes at the given index - extended to only remove the item if the page was not pulled before the updatedat DateTime. - /// - /// The index. - /// The updated at. - /// - public bool RemoveAt(int index, object updatedAt) - { - return InternalRemoveAt(index, updatedAt); - } - - /// - /// Adds (appends) the specified item - extended to only add the item if the page was not pulled before the updatedat DateTime. - /// - /// The item. - /// The updated at. - /// - public int Add(T item, object updatedAt) - { - return InternalAdd(item, updatedAt); - } - - /// - /// Inserts the specified index - extended to only insert the item if the page was not pulled before the updatedat DateTime. - /// - /// The index. - /// The item. - /// The updated at. - public void Insert(int index, T item, object updatedAt) - { - InternalInsertAt(index, item, updatedAt); - } - /// - /// Adds the range. - /// - /// The new values. - /// The updatedat object. - /// Index of the last appended object - public int AddRange(IEnumerable newValues, object timestamp = null) - { - var edit = GetProviderAsEditable(); - - int index = -1; - List items = new List(); - - foreach (var item in newValues) - { - items.Add(item); - index = edit.OnAppend(item, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); - RaiseCollectionChangedEvent(args); - } - - - OnCountTouched(); - - - return index; - } - - #endregion Extended CRUD operators that take into account the DateTime of the change - - #region IList Implementation - - /// - /// Adds an item to the . - /// - /// The object to add to the . - /// - /// The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the collection. - /// - public int Add(object value) - { - return InternalAdd((T)value, null); - } - - /// - /// Determines whether the contains a specific value. - /// - /// The object to locate in the . - /// - /// true if the is found in the ; otherwise, false. - /// - public bool Contains(object value) - { - return Contains((T)value); - } - - /// - /// Determines the index of a specific item in the . - /// - /// The object to locate in the . - /// - /// The index of if found in the list; otherwise, -1. - /// - public int IndexOf(object value) - { - return IndexOf((T)value); - } - - /// - /// Inserts an item to the at the specified index. - /// - /// The zero-based index at which should be inserted. - /// The object to insert into the . - public void Insert(int index, object value) - { - Insert(index, (T)value); - } - - /// - /// Gets a value indicating whether the has a fixed size. - /// - /// true if the has a fixed size; otherwise, false. - public bool IsFixedSize - { - get { return false; } - } - - /// - /// Removes the first occurrence of a specific object from the . - /// - /// The object to remove from the . - public void Remove(object value) - { - Remove((T)value); - } - - /// - /// Removes the item at the specified index. - /// - /// The zero-based index of the item to remove. - public void RemoveAt(int index) - { - InternalRemoveAt(index); - } - - /// - /// Gets or sets the element at the specified index. - /// - /// The index. - /// - public object this[int index] - { - get - { - return InternalGetValue(index, _DefaultSelectionContext); - } - set - { - InternalSetValue(index, (T)value); - } - } - - #endregion IList Implementation - - #region IList Implementation - - /// - /// Determines the index of a specific item in the . - /// - /// The object to locate in the . - /// - /// The index of if found in the list; otherwise, -1. - /// - public int IndexOf(T item) - { - return InternalIndexOf(item); - } - - /// - /// Inserts an item to the at the specified index. - /// - /// The zero-based index at which should be inserted. - /// The object to insert into the . - public void Insert(int index, T item) - { - InternalInsertAt(index, item); - } - - /// - /// Gets or sets the element at the specified index. - /// - /// The index. - /// - T IList.this[int index] - { - get - { - return InternalGetValue(index, _DefaultSelectionContext); - } - set - { - InternalSetValue(index, value); - } - } - - #endregion IList Implementation - - #region Public Properties - - /// - /// Gets or sets the provider if its asynchronous. - /// - /// - /// The provider asynchronous. - /// - public IItemSourceProviderAsync ProviderAsync - { - get { return _ProviderAsync; } - set - { - ClearCountChangedHooks(); - _ProviderAsync = value; - if (_ProviderAsync is INotifyCountChanged) - { - (_ProviderAsync as INotifyCountChanged).CountChanged += VirtualizingObservableCollection_CountChanged; - } - } - } - - void VirtualizingObservableCollection_CountChanged(object sender, CountChangedEventArgs args) - { - if (args.NeedsReset) - { - // Send a reset.. - RaiseCollectionChangedEvent(_CC_ResetArgs); - } - OnCountTouched(); - } - - /// - /// Gets or sets the provider if its not asynchronous. - /// - /// - /// The provider. - /// - public IItemSourceProvider Provider - { - get { return _Provider; } - set - { - ClearCountChangedHooks(); - _Provider = value; - - if (_Provider is INotifyCountChanged) - { - (_Provider as INotifyCountChanged).CountChanged += VirtualizingObservableCollection_CountChanged; - } - } - } - - void ClearCountChangedHooks() - { - if(_Provider is INotifyCountChanged) - { - (_Provider as INotifyCountChanged).CountChanged -= VirtualizingObservableCollection_CountChanged; - } - - if(_ProviderAsync is INotifyCountChanged) - { - (_ProviderAsync as INotifyCountChanged).CountChanged -= VirtualizingObservableCollection_CountChanged; - } - } - - #endregion Public Properties - - #region INotifyCollectionChanged Implementation - - private bool _SupressEventErrors = false; - - public bool SupressEventErrors - { - get - { - return _SupressEventErrors; - } - - set - { - _SupressEventErrors = value; - } - } - - public event NotifyCollectionChangedEventHandler CollectionChanged; - - /// - /// Raises the collection changed event. - /// - /// The instance containing the event data. - internal void RaiseCollectionChangedEvent(NotifyCollectionChangedEventArgs args) - { - if (_BulkCount > 0) return; - - var evnt = CollectionChanged; - - if (evnt != null) - { - try - { - evnt(this, args); - } - catch (Exception ex) - { - if (!this.SupressEventErrors) - { - throw ex; - } - } - } - } - - #endregion INotifyCollectionChanged Implementation - - #region INotifyPropertyChanged implementation - - public event PropertyChangedEventHandler PropertyChanged; - - private static PropertyChangedEventArgs _PC_CountArgs = new PropertyChangedEventArgs("Count"); - private static NotifyCollectionChangedEventArgs _CC_ResetArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); - - private void OnCountTouched() - { - RaisePropertyChanged(_PC_CountArgs); - } - - protected void RaisePropertyChanged(PropertyChangedEventArgs args) - { - if (_BulkCount > 0) return; - - var evnt = PropertyChanged; - - if (evnt != null) - { - evnt(this, args); - } - } - - #endregion INotifyPropertyChanged implementation - - #region Bulk Operation implementation - - /// - /// Releases the bulk mode. - /// - internal void ReleaseBulkMode() - { - if (_BulkCount > 0) _BulkCount--; - - if (_BulkCount == 0) - { - RaiseCollectionChangedEvent(_CC_ResetArgs); - RaisePropertyChanged(_PC_CountArgs); - } - } - - /// - /// Enters the bulk mode. - /// - /// - public BulkMode EnterBulkMode() - { - _BulkCount++; - - return new BulkMode(this); - } - - /// - /// The Bulk mode IDisposable proxy - /// - public class BulkMode : IDisposable - { - public BulkMode(VirtualizingObservableCollection voc) - { - _voc = voc; - } - - private VirtualizingObservableCollection _voc = null; - - bool _IsDisposed = false; - - public void Dispose() - { - OnDispose(); - } - - void OnDispose() - { - if (!_IsDisposed) - { - _IsDisposed = true; - if (_voc != null) _voc.ReleaseBulkMode(); - } - } - - ~BulkMode() - { - OnDispose(); - } - } - - #endregion Bulk Operation implementation - - #region Private Properties - - protected String _DefaultSelectionContext = new Guid().ToString(); - private IItemSourceProvider _Provider = null; - private IItemSourceProviderAsync _ProviderAsync = null; - private int _BulkCount = 0; - - #endregion Private Properties - - #region Internal implementation - - - /// - /// Gets the provider as editable. - /// - /// - /// - protected IEditableProvider GetProviderAsEditable() - { - IEditableProvider ret = null; - - if (this.Provider != null) - { - ret = this.Provider as IEditableProvider; - - } - else - { - ret = this.ProviderAsync as IEditableProvider; - } - - if (ret == null) - { - throw new NotSupportedException(); - } - - return ret; - } - - /// - /// Replaces oldValue with newValue at index if updatedat is newer or null. - /// - /// The index. - /// The old value. - /// The new value. - /// The timestamp. - internal void ReplaceAt(int index, T oldValue, T newValue, object timestamp) - { - var edit = this.GetProviderAsEditable(); - - if (edit != null) - { - edit.OnReplace(index, oldValue, newValue, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue, oldValue, index); - RaiseCollectionChangedEvent(args); - } - } - - void InternalClear() - { - if (this.Provider != null) - { - if (this.Provider is IProviderPreReset) - { - (this.Provider as IProviderPreReset).OnBeforeReset(); - } - this.Provider.OnReset(-1); - } - else - { - if (this.ProviderAsync is IProviderPreReset) - { - (this.ProviderAsync as IProviderPreReset).OnBeforeReset(); - } - this.ProviderAsync.OnReset(-1); - } - } - - CancellationTokenSource _ResetToken = null; - - public async void ResetAsync() - { - CancellationTokenSource cts = null; - - lock(this) - { - if(_ResetToken != null) - { - _ResetToken.Cancel(); - _ResetToken = null; - } - - cts = _ResetToken = new CancellationTokenSource(); - } - - if (this.Provider != null) - { - if (this.Provider is IProviderPreReset) - { - (this.Provider as IProviderPreReset).OnBeforeReset(); - if (cts.IsCancellationRequested) - { - return; - } - - } - - //this.Provider.OnReset(-2); - - Task.Run(async () => - { - if (this.Provider is IAsyncResetProvider) - { - int count = await (this.Provider as IAsyncResetProvider).GetCountAsync(); - if (!cts.IsCancellationRequested) - { - VirtualizationManager.Instance.RunOnUI(() => - this.Provider.OnReset(count) - ); - } - - } - else - { - int count = this.Provider.GetCount(false); - if (!cts.IsCancellationRequested) - { - VirtualizationManager.Instance.RunOnUI(() => - this.Provider.OnReset(count) - ); - } - } - - }); - - } - else - { - if (this.ProviderAsync is IProviderPreReset) - { - (this.ProviderAsync as IProviderPreReset).OnBeforeReset(); - } - this.ProviderAsync.OnReset(await this.ProviderAsync.Count); - } - - lock(this) - { - if(_ResetToken == cts) - { - _ResetToken = null; - } - } - } - - T InternalGetValue(int index, string selectionContext) - { - bool allowPlaceholder = true; - if (selectionContext != _DefaultSelectionContext) allowPlaceholder = false; - - if (this.Provider != null) - { - return this.Provider.GetAt(index, this, allowPlaceholder); - } - else - { - return Task.Run(() => this.ProviderAsync.GetAt(index, this, allowPlaceholder)).Result; - } - } - - T InternalSetValue(int index, T newValue) - { - T oldValue = InternalGetValue(index, _DefaultSelectionContext); - var edit = GetProviderAsEditable(); - edit.OnReplace(index, oldValue, newValue, null); - - List newItems = new List(); newItems.Add(newValue); - List oldItems = new List(); oldItems.Add(oldValue); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItems, oldItems, index); - RaiseCollectionChangedEvent(args); - - return oldValue; - } - - int InternalAdd(T newValue, object timestamp) - { - var edit = GetProviderAsEditable(); - var index = edit.OnAppend(newValue, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newValue, index); - RaiseCollectionChangedEvent(args); - - OnCountTouched(); - - return index; - } - - int InternalGetCount() - { - int ret = 0; - - if (this.Provider != null) - { - ret = this.Provider.GetCount(true); - } - else - { - ret = Task.Run( () => this.ProviderAsync.Count).Result; - } - - - return ret; - } - - void InternalInsertAt(int index, T item, object timestamp = null) - { - - var edit = GetProviderAsEditable(); - edit.OnInsert(index, item, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); - RaiseCollectionChangedEvent(args); - - OnCountTouched(); - } - - bool InternalRemoveAt(int index, object timestamp = null) - { - T oldValue = InternalGetValue(index, _DefaultSelectionContext); - - if (oldValue == null) - { - return false; - } - else - { - var edit = GetProviderAsEditable(); - edit.OnRemove(index, oldValue, timestamp); - - NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldValue, index); - RaiseCollectionChangedEvent(args); - - OnCountTouched(); - - return true; - } - } - - int InternalIndexOf(T item) - { - if (this.Provider != null) - { - return this.Provider.IndexOf(item); - } - else - { - return Task.Run( () => this.ProviderAsync.IndexOf(item)).Result; - } - } - - void EnsureCountIsGotNONASync() - { - if(this.Provider != null) - { - this.Provider.GetCount(false); - } - else - { - - } - } - - #endregion Internal implementation - - } - -} diff --git a/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.dll b/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.dll deleted file mode 100644 index f1636bb..0000000 Binary files a/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.dll and /dev/null differ diff --git a/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.dll.CodeAnalysisLog.xml b/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.dll.CodeAnalysisLog.xml deleted file mode 100644 index 3256afd..0000000 --- a/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.dll.CodeAnalysisLog.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - Declare the second parameter of 'OnCountChanged' as an EventArgs, or an instance of a type that extends EventArgs, named 'e'. - - - - - - - - - Provide an overridable implementation of Dispose(bool) on 'VirtualizingObservableCollection<T>.BulkMode' or mark the type as sealed. A call to Dispose(false) should only clean up native resources. A call to Dispose(true) should clean up both managed and native resources. - - - - - - - Modify 'VirtualizingObservableCollection<T>.BulkMode.Dispose()' so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns. - - - - - - - Modify 'VirtualizingObservableCollection<T>.BulkMode.~BulkMode()' so that it calls Dispose(false) and then returns. - - - - - - - - - - - - - - - Declare event handlers correctly - By convention, .NET events have two parameters that specify the event sender and event data. Event handler signatures should follow this form: void MyEventHandler(object sender, EventArgs e). The 'sender' parameter is always of type System.Object, even if it is possible to employ a more specific type. The 'e' parameter is always of type System.EventArgs. Events that do not provide event data should use the System.EventHandler delegate type. Event handlers return void so that they can send each event to multiple target methods. Any value returned by a target would be lost after the first call. - Declare the second parameter of {0} as an EventArgs, or an instance of a type that extends EventArgs, named 'e'. - - http://msdn.microsoft.com/library/ms182133.aspx - [none] - Error - - - - Implement IDisposable correctly - All IDisposable types should implement the Dispose pattern correctly. - Modify {0} so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns. - Modify {0} so that it calls Dispose(false) and then returns. - Provide an overridable implementation of Dispose(bool) on {0} or mark the type as sealed. A call to Dispose(false) should only clean up native resources. A call to Dispose(true) should clean up both managed and native resources. - - http://msdn.microsoft.com/library/ms244737.aspx - [none] - Error - - - - - Category - Certainty - Collapse All - Check Id - Error - error(s) - Expand All - Help - Line - message(s) - [Location not stored in Pdb] - Project - Resolution - Rule - Rule File - Rule Description - Source - Status - Target - Warning - warning(s) - Code Analysis Report - - diff --git a/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.dll.lastcodeanalysissucceeded b/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.dll.lastcodeanalysissucceeded deleted file mode 100644 index e69de29..0000000 diff --git a/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.pdb b/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.pdb deleted file mode 100644 index 3602413..0000000 Binary files a/AlphaChiTech.Virtualization/bin/Debug/AlphaChiTech.Virtualization.pdb and /dev/null differ diff --git a/AlphaChiTech.Virtualization/bin/Release/AlphaChiTech.Virtualization.dll b/AlphaChiTech.Virtualization/bin/Release/AlphaChiTech.Virtualization.dll deleted file mode 100644 index ce44c3b..0000000 Binary files a/AlphaChiTech.Virtualization/bin/Release/AlphaChiTech.Virtualization.dll and /dev/null differ diff --git a/AlphaChiTech.Virtualization/bin/Release/AlphaChiTech.Virtualization.pdb b/AlphaChiTech.Virtualization/bin/Release/AlphaChiTech.Virtualization.pdb deleted file mode 100644 index 86e7eb2..0000000 Binary files a/AlphaChiTech.Virtualization/bin/Release/AlphaChiTech.Virtualization.pdb and /dev/null differ diff --git a/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.csproj.FileListAbsolute.txt b/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.csproj.FileListAbsolute.txt deleted file mode 100644 index 7cad6d3..0000000 --- a/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.csproj.FileListAbsolute.txt +++ /dev/null @@ -1,15 +0,0 @@ -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Debug\AlphaChiTech.Virtualization.dll -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Debug\AlphaChiTech.Virtualization.pdb -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Debug\AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Debug\AlphaChiTech.Virtualization.dll -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Debug\AlphaChiTech.Virtualization.pdb -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Debug\AlphaChiTech.Virtualization.dll -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Debug\AlphaChiTech.Virtualization.pdb -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Debug\AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Debug\AlphaChiTech.Virtualization.dll -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Debug\AlphaChiTech.Virtualization.pdb -C:\AlphaChiDev\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Debug\AlphaChiTech.Virtualization.dll -C:\AlphaChiDev\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Debug\AlphaChiTech.Virtualization.pdb -C:\AlphaChiDev\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Debug\AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache -C:\AlphaChiDev\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Debug\AlphaChiTech.Virtualization.dll -C:\AlphaChiDev\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Debug\AlphaChiTech.Virtualization.pdb diff --git a/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache b/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache deleted file mode 100644 index 39e7dcd..0000000 Binary files a/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache and /dev/null differ diff --git a/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.dll b/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.dll deleted file mode 100644 index f1636bb..0000000 Binary files a/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.dll and /dev/null differ diff --git a/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.pdb b/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.pdb deleted file mode 100644 index 3602413..0000000 Binary files a/AlphaChiTech.Virtualization/obj/Debug/AlphaChiTech.Virtualization.pdb and /dev/null differ diff --git a/AlphaChiTech.Virtualization/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/AlphaChiTech.Virtualization/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache deleted file mode 100644 index cbd6501..0000000 Binary files a/AlphaChiTech.Virtualization/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache and /dev/null differ diff --git a/AlphaChiTech.Virtualization/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/AlphaChiTech.Virtualization/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs deleted file mode 100644 index e69de29..0000000 diff --git a/AlphaChiTech.Virtualization/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/AlphaChiTech.Virtualization/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs deleted file mode 100644 index e69de29..0000000 diff --git a/AlphaChiTech.Virtualization/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/AlphaChiTech.Virtualization/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs deleted file mode 100644 index e69de29..0000000 diff --git a/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.csproj.FileListAbsolute.txt b/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.csproj.FileListAbsolute.txt deleted file mode 100644 index 7fac028..0000000 --- a/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.csproj.FileListAbsolute.txt +++ /dev/null @@ -1,10 +0,0 @@ -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Release\AlphaChiTech.Virtualization.dll -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Release\AlphaChiTech.Virtualization.pdb -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Release\AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Release\AlphaChiTech.Virtualization.dll -C:\Alphachidev\Internal\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Release\AlphaChiTech.Virtualization.pdb -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Release\AlphaChiTech.Virtualization.dll -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\bin\Release\AlphaChiTech.Virtualization.pdb -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Release\AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Release\AlphaChiTech.Virtualization.dll -C:\Alphachidev\Internal\PCL\AlphaChiTech.Virtualization\AlphaChiTech.Virtualization\obj\Release\AlphaChiTech.Virtualization.pdb diff --git a/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache b/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache deleted file mode 100644 index 39e7dcd..0000000 Binary files a/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.csprojResolveAssemblyReference.cache and /dev/null differ diff --git a/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.dll b/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.dll deleted file mode 100644 index ce44c3b..0000000 Binary files a/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.dll and /dev/null differ diff --git a/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.pdb b/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.pdb deleted file mode 100644 index 86e7eb2..0000000 Binary files a/AlphaChiTech.Virtualization/obj/Release/AlphaChiTech.Virtualization.pdb and /dev/null differ diff --git a/AlphaChiTech.Virtualization/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache b/AlphaChiTech.Virtualization/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache deleted file mode 100644 index b83a4c4..0000000 Binary files a/AlphaChiTech.Virtualization/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache and /dev/null differ diff --git a/AlphaChiTech.Virtualization/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/AlphaChiTech.Virtualization/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs deleted file mode 100644 index e69de29..0000000 diff --git a/AlphaChiTech.Virtualization/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/AlphaChiTech.Virtualization/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs deleted file mode 100644 index e69de29..0000000 diff --git a/AlphaChiTech.Virtualization/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/AlphaChiTech.Virtualization/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs deleted file mode 100644 index e69de29..0000000 diff --git a/DataGridAsyncDemo/App.config b/DataGridAsyncDemo/App.config index d1428ad..efb4d02 100644 --- a/DataGridAsyncDemo/App.config +++ b/DataGridAsyncDemo/App.config @@ -1,6 +1,7 @@ - + + - - - - + + + + \ No newline at end of file diff --git a/DataGridAsyncDemo/App.xaml b/DataGridAsyncDemo/App.xaml index 6872d6a..d3af4d6 100644 --- a/DataGridAsyncDemo/App.xaml +++ b/DataGridAsyncDemo/App.xaml @@ -3,6 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> - + - + \ No newline at end of file diff --git a/DataGridAsyncDemo/App.xaml.cs b/DataGridAsyncDemo/App.xaml.cs index 69479d5..17ab99c 100644 --- a/DataGridAsyncDemo/App.xaml.cs +++ b/DataGridAsyncDemo/App.xaml.cs @@ -1,17 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; namespace Demo { - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application - { - } -} + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} \ No newline at end of file diff --git a/DataGridAsyncDemo/DataGridAsyncDemo.csproj b/DataGridAsyncDemo/DataGridAsyncDemo.csproj index 3232746..ebef1f9 100644 --- a/DataGridAsyncDemo/DataGridAsyncDemo.csproj +++ b/DataGridAsyncDemo/DataGridAsyncDemo.csproj @@ -9,7 +9,7 @@ Properties DataGridAsyncDemo DataGridAsyncDemo - v4.5 + v4.6.1 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 @@ -17,6 +17,7 @@ true + 1.0.0.0 AnyCPU @@ -42,10 +43,8 @@ - - ..\packages\System.Linq.Dynamic.1.0.7\lib\net40\System.Linq.Dynamic.dll - True - + + @@ -105,7 +104,6 @@ ResXFileCodeGenerator Resources.Designer.cs - SettingsSingleFileGenerator Settings.Designer.cs @@ -116,24 +114,12 @@ - - {ecedc613-843a-4d9a-8123-bd9600c9118e} - AlphaChiTech.Virtualization.Net4 + + {d8d58c1d-2596-45ad-83bd-e0d5c9fa7f69} + VirtualizingCollection - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + \ No newline at end of file diff --git a/DataGridAsyncDemo/MainWindow.xaml b/DataGridAsyncDemo/MainWindow.xaml index eb3e907..c499836 100644 --- a/DataGridAsyncDemo/MainWindow.xaml +++ b/DataGridAsyncDemo/MainWindow.xaml @@ -58,4 +58,4 @@ - + \ No newline at end of file diff --git a/DataGridAsyncDemo/MainWindow.xaml.cs b/DataGridAsyncDemo/MainWindow.xaml.cs index cbbbf4d..b9891e1 100644 --- a/DataGridAsyncDemo/MainWindow.xaml.cs +++ b/DataGridAsyncDemo/MainWindow.xaml.cs @@ -9,6 +9,8 @@ using System.Windows.Controls.Primitives; using System.Windows.Threading; using AlphaChiTech.Virtualization; +using AlphaChiTech.Virtualization.Pageing; +using AlphaChiTech.VirtualizingCollection; using DataGridAsyncDemo.filtersort; using SortDescription = DataGridAsyncDemo.filtersort.SortDescription; @@ -16,168 +18,171 @@ namespace DataGridAsyncDemo { - #region + #region - + #endregion - #endregion + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + #region constructors - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : Window - { - #region fields + //private VirtualizingObservableCollection _myDataVirtualizedAsyncFilterSortObservableCollection; + //private RemoteOrDbDataSourceAsyncProxy _myRemoteOrDbDataSourceAsyncProxy; - private Timer _filterTimer; - private VirtualizingObservableCollection _myDataVirtualizedAsyncFilterSortObservableCollection = null; - private RemoteOrDbDataSourceAsyncProxy _myRemoteOrDbDataSourceAsyncProxy = null; + public MainWindow() + { + //this routine only needs to run once, so first check to make sure the + //VirtualizationManager isn’t already initialized + if (!VirtualizationManager.IsInitialized) + { + //set the VirtualizationManager’s UIThreadExcecuteAction. In this case + //we’re using Dispatcher.Invoke to give the VirtualizationManager access + //to the dispatcher thread, and using a DispatcherTimer to run the background + //operations the VirtualizationManager needs to run to reclaim pages and manage memory. + VirtualizationManager.Instance.UiThreadExcecuteAction = a => Application.Current.Dispatcher.Invoke(a); + new DispatcherTimer(TimeSpan.FromMilliseconds(10), + DispatcherPriority.Background, + delegate { VirtualizationManager.Instance.ProcessActions(); }, this.Dispatcher).Start(); + } + + this.InitializeComponent(); + + this.TstDataGridAsyncFilterSort.ItemsSource = this.MyDataVirtualizedAsyncFilterSortObservableCollection; + } - #endregion + #endregion - #region constructors + #region properties - //private VirtualizingObservableCollection _myDataVirtualizedAsyncFilterSortObservableCollection; - //private RemoteOrDbDataSourceAsyncProxy _myRemoteOrDbDataSourceAsyncProxy; + public VirtualizingObservableCollection MyDataVirtualizedAsyncFilterSortObservableCollection + { + get + { + if (this._myDataVirtualizedAsyncFilterSortObservableCollection == null) + { + this._myRemoteOrDbDataSourceAsyncProxy = + new RemoteOrDbDataSourceAsyncProxy(new RemoteOrDbDataSourceEmulation()); + this._myDataVirtualizedAsyncFilterSortObservableCollection = + new VirtualizingObservableCollection( + new PaginationManager(this._myRemoteOrDbDataSourceAsyncProxy, + pageSize: 10, maxPages: 2)); + } + + return this._myDataVirtualizedAsyncFilterSortObservableCollection; + } + } - public MainWindow() - { - //this routine only needs to run once, so first check to make sure the - //VirtualizationManager isn’t already initialized - if ( !VirtualizationManager.IsInitialized ) - { - //set the VirtualizationManager’s UIThreadExcecuteAction. In this case - //we’re using Dispatcher.Invoke to give the VirtualizationManager access - //to the dispatcher thread, and using a DispatcherTimer to run the background - //operations the VirtualizationManager needs to run to reclaim pages and manage memory. - VirtualizationManager.Instance.UIThreadExcecuteAction = a => Application.Current.Dispatcher.Invoke( a ); - new DispatcherTimer( TimeSpan.FromMilliseconds( 10 ), - DispatcherPriority.Background, - delegate - { - VirtualizationManager.Instance.ProcessActions(); - }, - Dispatcher ).Start(); - } - - InitializeComponent(); - - TstDataGridAsyncFilterSort.ItemsSource = MyDataVirtualizedAsyncFilterSortObservableCollection; - } + #endregion - #endregion + #region fields - #region events + private Timer _filterTimer; - private void DbgButton_Click( object sender, RoutedEventArgs e ) - { - Debugger.Break(); - } + private VirtualizingObservableCollection + _myDataVirtualizedAsyncFilterSortObservableCollection; - private void FilterTextBox_TextChanged( object sender, TextChangedEventArgs e ) - { - if ( _filterTimer == null ) - { - _filterTimer = new Timer( 1000 ); - _filterTimer.Elapsed += FilterTimerElapsed; - } - - TextBox textBox = sender as TextBox; - if ( textBox != null ) - { - DataGridColumnHeader dataGridColumnHeader = textBox.ParentOfType(); - if ( dataGridColumnHeader != null ) - { - _filterTimer.Stop(); + private RemoteOrDbDataSourceAsyncProxy _myRemoteOrDbDataSourceAsyncProxy; - string dbg_SortMemberPath = dataGridColumnHeader.Column.SortMemberPath; - // TODO update filter + #endregion - if ( String.IsNullOrWhiteSpace( textBox.Text ) ) - _myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.Remove( dbg_SortMemberPath ); - else - _myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.Add( new FilterDescription( dbg_SortMemberPath, textBox.Text ) ); + #region events - // Will notify filter definition update - _filterTimer.Start(); + private void DbgButton_Click(object sender, RoutedEventArgs e) + { + Debugger.Break(); } - } - } - private void FilterTimerElapsed( object sender, ElapsedEventArgs elapsedEventArgs ) - { - try - { - // Notify filter definition update - _filterTimer.Stop(); - - _myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.OnCollectionReset(); - - Dispatcher.BeginInvoke( (Action)( () - => - { - // call .Clear() on the virtualizingObservableCollection to force a refresh / reset - _myDataVirtualizedAsyncFilterSortObservableCollection.Clear(); - } ) ); - } - catch ( Exception ex ) - { - int aa = 0; - } - } - - private void TstDataGridAsyncFilterSort_Sorting( object sender, DataGridSortingEventArgs e ) - { - DataGrid grid = sender as DataGrid; - - var sortDirection = e.Column.SortDirection; - string sortMemberPath = e.Column.SortMemberPath; - - if ( sortDirection == null ) - { - e.Column.SortDirection = ListSortDirection.Ascending; - _myRemoteOrDbDataSourceAsyncProxy.SortDescriptionList.Add( new SortDescription( sortMemberPath, ListSortDirection.Ascending ) ); - } - else if ( sortDirection == ListSortDirection.Ascending ) - { - e.Column.SortDirection = ListSortDirection.Descending; - _myRemoteOrDbDataSourceAsyncProxy.SortDescriptionList.Add( new SortDescription( sortMemberPath, ListSortDirection.Descending ) ); - } - else if ( sortDirection == ListSortDirection.Descending ) - { - e.Column.SortDirection = null; - _myRemoteOrDbDataSourceAsyncProxy.SortDescriptionList.Remove( sortMemberPath ); - } - - Dispatcher.BeginInvoke( (Action)( () - => - { - // call .Clear() on the virtualizingObservableCollection to force a refresh / reset - _myDataVirtualizedAsyncFilterSortObservableCollection.Clear(); - } ) ); - - e.Handled = true; - } - - #endregion + private void FilterTextBox_TextChanged(object sender, TextChangedEventArgs e) + { + if (this._filterTimer == null) + { + this._filterTimer = new Timer(1000); + this._filterTimer.Elapsed += this.FilterTimerElapsed; + } + + TextBox textBox = sender as TextBox; + if (textBox != null) + { + DataGridColumnHeader dataGridColumnHeader = textBox.ParentOfType(); + if (dataGridColumnHeader != null) + { + this._filterTimer.Stop(); + + string dbg_SortMemberPath = dataGridColumnHeader.Column.SortMemberPath; + // TODO update filter + + if (String.IsNullOrWhiteSpace(textBox.Text)) + this._myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.Remove(dbg_SortMemberPath); + else + this._myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.Add( + new FilterDescription(dbg_SortMemberPath, textBox.Text)); + + // Will notify filter definition update + this._filterTimer.Start(); + } + } + } - #region properties + private void FilterTimerElapsed(object sender, ElapsedEventArgs elapsedEventArgs) + { + try + { + // Notify filter definition update + this._filterTimer.Stop(); + + this._myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.OnCollectionReset(); + + this.Dispatcher.BeginInvoke((Action) (() + => + { + // call .Clear() on the virtualizingObservableCollection to force a refresh / reset + this._myDataVirtualizedAsyncFilterSortObservableCollection.Clear(); + })); + } + catch (Exception ex) + { + int aa = 0; + } + } - public VirtualizingObservableCollection MyDataVirtualizedAsyncFilterSortObservableCollection - { - get - { - if ( _myDataVirtualizedAsyncFilterSortObservableCollection == null ) + private void TstDataGridAsyncFilterSort_Sorting(object sender, DataGridSortingEventArgs e) { - _myRemoteOrDbDataSourceAsyncProxy = new RemoteOrDbDataSourceAsyncProxy( new RemoteOrDbDataSourceEmulation() ); - _myDataVirtualizedAsyncFilterSortObservableCollection = - new VirtualizingObservableCollection( - new PaginationManager(_myRemoteOrDbDataSourceAsyncProxy, pageSize: 10, maxPages:2) ); + DataGrid grid = sender as DataGrid; + + var sortDirection = e.Column.SortDirection; + string sortMemberPath = e.Column.SortMemberPath; + + if (sortDirection == null) + { + e.Column.SortDirection = ListSortDirection.Ascending; + this._myRemoteOrDbDataSourceAsyncProxy.SortDescriptionList.Add( + new SortDescription(sortMemberPath, ListSortDirection.Ascending)); + } + else if (sortDirection == ListSortDirection.Ascending) + { + e.Column.SortDirection = ListSortDirection.Descending; + this._myRemoteOrDbDataSourceAsyncProxy.SortDescriptionList.Add( + new SortDescription(sortMemberPath, ListSortDirection.Descending)); + } + else if (sortDirection == ListSortDirection.Descending) + { + e.Column.SortDirection = null; + this._myRemoteOrDbDataSourceAsyncProxy.SortDescriptionList.Remove(sortMemberPath); + } + + this.Dispatcher.BeginInvoke((Action) (() + => + { + // call .Clear() on the virtualizingObservableCollection to force a refresh / reset + this._myDataVirtualizedAsyncFilterSortObservableCollection.Clear(); + })); + + e.Handled = true; } - return _myDataVirtualizedAsyncFilterSortObservableCollection; - } - } - #endregion - } -} + #endregion + } +} \ No newline at end of file diff --git a/DataGridAsyncDemo/Properties/AssemblyInfo.cs b/DataGridAsyncDemo/Properties/AssemblyInfo.cs index 0d36bdd..f9ac16f 100644 --- a/DataGridAsyncDemo/Properties/AssemblyInfo.cs +++ b/DataGridAsyncDemo/Properties/AssemblyInfo.cs @@ -1,25 +1,23 @@ using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle( "Demo" )] -[assembly: AssemblyDescription( "" )] -[assembly: AssemblyConfiguration( "" )] -[assembly: AssemblyCompany( "" )] -[assembly: AssemblyProduct( "Demo" )] -[assembly: AssemblyCopyright( "Copyright © 2015" )] -[assembly: AssemblyTrademark( "" )] -[assembly: AssemblyCulture( "" )] +[assembly: AssemblyTitle("Demo")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Demo")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible( false )] +[assembly: ComVisible(false)] //In order to begin building localizable applications, set //CultureYouAreCodingWith in your .csproj file @@ -33,11 +31,11 @@ [assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) + //(used if a resource is not found in the page, + // or application resource dictionaries) ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) )] @@ -51,5 +49,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion( "1.0.0.0" )] -[assembly: AssemblyFileVersion( "1.0.0.0" )] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/DataGridAsyncDemo/Properties/Resources.Designer.cs b/DataGridAsyncDemo/Properties/Resources.Designer.cs index 438b470..a03e408 100644 --- a/DataGridAsyncDemo/Properties/Resources.Designer.cs +++ b/DataGridAsyncDemo/Properties/Resources.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// Ce code a été généré par un outil. -// Version du runtime :4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si -// le code est régénéré. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,13 +13,13 @@ namespace DataGridAsyncDemo.Properties { /// - /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder - // à l'aide d'un outil, tel que ResGen ou Visual Studio. - // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen - // avec l'option /str ou régénérez votre projet VS. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -33,7 +33,7 @@ internal Resources() { } /// - /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ internal Resources() { } /// - /// Remplace la propriété CurrentUICulture du thread actuel pour toutes - /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { diff --git a/DataGridAsyncDemo/Properties/Settings.Designer.cs b/DataGridAsyncDemo/Properties/Settings.Designer.cs index fedf8ba..ce84dc7 100644 --- a/DataGridAsyncDemo/Properties/Settings.Designer.cs +++ b/DataGridAsyncDemo/Properties/Settings.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// Ce code a été généré par un outil. -// Version du runtime :4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si -// le code est régénéré. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -12,7 +12,7 @@ namespace DataGridAsyncDemo.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.5.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/DataGridAsyncDemo/Properties/Settings.settings b/DataGridAsyncDemo/Properties/Settings.settings index 033d7a5..c14891b 100644 --- a/DataGridAsyncDemo/Properties/Settings.settings +++ b/DataGridAsyncDemo/Properties/Settings.settings @@ -1,4 +1,5 @@  + diff --git a/DataGridAsyncDemo/RemoteOrDbDataItem.cs b/DataGridAsyncDemo/RemoteOrDbDataItem.cs index 10894a8..4bff21b 100644 --- a/DataGridAsyncDemo/RemoteOrDbDataItem.cs +++ b/DataGridAsyncDemo/RemoteOrDbDataItem.cs @@ -1,27 +1,28 @@ namespace DataGridAsyncDemo { - public class RemoteOrDbDataItem - { - public RemoteOrDbDataItem() - {} - - public RemoteOrDbDataItem( string name, string str1, string str2, int int1, double double1 ) + public class RemoteOrDbDataItem { - Name = name; - Str1 = str1; - Str2 = str2; - Int1 = int1; - Double1 = double1; - } + public RemoteOrDbDataItem() + { + } - #region properties + public RemoteOrDbDataItem(string name, string str1, string str2, int int1, double double1) + { + this.Name = name; + this.Str1 = str1; + this.Str2 = str2; + this.Int1 = int1; + this.Double1 = double1; + } - public double Double1 { get; set; } - public int Int1 { get; set; } - public string Name { get; set; } - public string Str1 { get; set; } - public string Str2 { get; set; } + #region properties - #endregion - } -} + public double Double1 { get; set; } + public int Int1 { get; set; } + public string Name { get; set; } + public string Str1 { get; set; } + public string Str2 { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/DataGridAsyncDemo/RemoteOrDbDataSourceAsyncProxy.cs b/DataGridAsyncDemo/RemoteOrDbDataSourceAsyncProxy.cs index 721c2d8..0bcf66e 100644 --- a/DataGridAsyncDemo/RemoteOrDbDataSourceAsyncProxy.cs +++ b/DataGridAsyncDemo/RemoteOrDbDataSourceAsyncProxy.cs @@ -1,134 +1,150 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AlphaChiTech.Virtualization.Pageing; +using AlphaChiTech.VirtualizingCollection.Interfaces; +using DataGridAsyncDemo.filtersort; + namespace DataGridAsyncDemo { - #region - - using System; - using System.Linq; - using System.Threading.Tasks; - using AlphaChiTech.Virtualization; - using filtersort; + #region - #endregion + #endregion - /// - /// Remote/disk async data proxy. - /// Also add a delay on calls to simulate network/disk delay. - /// - public class RemoteOrDbDataSourceAsyncProxy - : IPagedSourceProviderAsync, IFilteredSortedSourceProviderAsync - { - #region fields + /// + /// Remote/disk async data proxy. + /// Also add a delay on calls to simulate network/disk delay. + /// + public class RemoteOrDbDataSourceAsyncProxy + : IPagedSourceProviderAsync, IFilteredSortedSourceProviderAsync + { + public RemoteOrDbDataSourceAsyncProxy(RemoteOrDbDataSourceEmulation remoteDatas) + { + this._remoteDatas = remoteDatas; + } - private readonly RemoteOrDbDataSourceEmulation _remoteDatas; + #region properties - private Random _rand = new Random(); + public FilterDescriptionList FilterDescriptionList + { + get { return this._remoteDatas.FilterDescriptionList; } + } - #endregion + #endregion - public RemoteOrDbDataSourceAsyncProxy( RemoteOrDbDataSourceEmulation remoteDatas ) - { - _remoteDatas = remoteDatas; - } + #region IFilteredSortedSourceProviderAsync Members - #region properties + public SortDescriptionList SortDescriptionList + { + get { return this._remoteDatas.SortDescriptionList; } + } + #endregion + public bool IsSynchronized { get; } + public object SyncRoot { get; } - public FilterDescriptionList FilterDescriptionList - { - get { return _remoteDatas.FilterDescriptionList; } - } + #region fields - #endregion + private readonly RemoteOrDbDataSourceEmulation _remoteDatas; - #region IFilteredSortedSourceProviderAsync Members + private Random _rand = new Random(); - public SortDescriptionList SortDescriptionList - { - get { return _remoteDatas.SortDescriptionList; } - } + #endregion - #endregion + #region IPagedSourceProvider Members (synchronous not available members) - #region IPagedSourceProvider Members (synchronous not available members) + int IPagedSourceProvider.IndexOf(RemoteOrDbDataItem item) + { + return this._remoteDatas.FilteredOrderedItems.IndexOf(item); + } - int IPagedSourceProvider.IndexOf( RemoteOrDbDataItem item ) - { - return _remoteDatas.FilteredOrderedItems.IndexOf(item); + public bool Contains(RemoteOrDbDataItem item) + { + return this._remoteDatas.Contains(item); } - public PagedSourceItemsPacket GetItemsAt( int pageoffset, int count, bool usePlaceholder ) - { - Task.Delay(50 + (int)Math.Round(_rand.NextDouble() * 100)).Wait(); // Just to slow it down ! + public PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder) + { + Task.Delay(50 + (int) Math.Round(this._rand.NextDouble() * 100)).Wait(); // Just to slow it down ! return new PagedSourceItemsPacket { LoadedAt = DateTime.Now, - Items = (from items in _remoteDatas.FilteredOrderedItems select items).Skip(pageoffset).Take(count) + Items = (from items in this._remoteDatas.FilteredOrderedItems select items).Skip(pageoffset).Take(count) }; } - public int Count - { - get { - Task.Delay(20 + (int)Math.Round(_rand.NextDouble() * 30)).Wait(); // Just to slow it down ! - return _remoteDatas.FilteredOrderedItems.Count; ; } - } - #endregion + public int Count + { + get + { + Task.Delay(20 + (int) Math.Round(this._rand.NextDouble() * 30)).Wait(); // Just to slow it down ! + return this._remoteDatas.FilteredOrderedItems.Count; + ; + } + } - #region public members + #endregion + #region public members + public Task ContainsAsync(RemoteOrDbDataItem item) + { + throw new NotImplementedException(); + } - public Task GetCountAsync() - { - return Task.Run(() => - { - Task.Delay(20 + (int)Math.Round(_rand.NextDouble() * 30)).Wait(); // Just to slow it down ! - return _remoteDatas.FilteredOrderedItems.Count; - } ); - } + public Task GetCountAsync() + { + return Task.Run(() => + { + Task.Delay(20 + (int) Math.Round(this._rand.NextDouble() * 30)).Wait(); // Just to slow it down ! + return this._remoteDatas.FilteredOrderedItems.Count; + }); + } - public Task> GetItemsAtAsync( int pageoffset, int count, bool usePlaceholder ) - { + public Task> GetItemsAtAsync(int pageoffset, int count, + bool usePlaceholder) + { Console.WriteLine("Get"); - return Task.Run( () => - { - Task.Delay(50 + (int)Math.Round(_rand.NextDouble() * 100)).Wait(); // Just to slow it down ! - return new PagedSourceItemsPacket - { - LoadedAt = DateTime.Now, - Items = ( from items in _remoteDatas.FilteredOrderedItems select items ).Skip( pageoffset ).Take( count ) - }; - } ); - } + return Task.Run(() => + { + Task.Delay(50 + (int) Math.Round(this._rand.NextDouble() * 100)).Wait(); // Just to slow it down ! + return new PagedSourceItemsPacket + { + LoadedAt = DateTime.Now, + Items = (from items in this._remoteDatas.FilteredOrderedItems select items).Skip(pageoffset) + .Take(count) + }; + }); + } - public RemoteOrDbDataItem GetPlaceHolder( int index, int page, int offset ) - { - return new RemoteOrDbDataItem {Name = "Waiting [" + page + "/" + offset + "]"}; - } + public RemoteOrDbDataItem GetPlaceHolder(int index, int page, int offset) + { + return new RemoteOrDbDataItem {Name = "Waiting [" + page + "/" + offset + "]"}; + } - /// - /// This returns the index of a specific item. This method is optional – you can just return -1 if you - /// don’t need to use IndexOf. It’s not strictly required if don’t need to be able to seeking to a - /// specific item, but if you are selecting items implementing this method is recommended. - /// - /// - /// - public Task IndexOfAsync( RemoteOrDbDataItem item ) - { - return Task.Run( () => { return _remoteDatas.FilteredOrderedItems.IndexOf( item ); } ); - } + /// + /// This returns the index of a specific item. This method is optional – you can just return -1 if you + /// don’t need to use IndexOf. It’s not strictly required if don’t need to be able to seeking to a + /// specific item, but if you are selecting items implementing this method is recommended. + /// + /// + /// + public Task IndexOfAsync(RemoteOrDbDataItem item) + { + return Task.Run(() => { return this._remoteDatas.FilteredOrderedItems.IndexOf(item); }); + } - /// - /// This is a callback that runs when a Reset is called on a provider. Implementing this is also optional. - /// If you don’t need to do anything in particular when resets occur, you can leave this method body empty. - /// - /// - public void OnReset( int count ) - { - // Do nothing for now - } + /// + /// This is a callback that runs when a Reset is called on a provider. Implementing this is also optional. + /// If you don’t need to do anything in particular when resets occur, you can leave this method body empty. + /// + /// + public void OnReset(int count) + { + // Do nothing for now + } - #endregion - } -} + #endregion + } +} \ No newline at end of file diff --git a/DataGridAsyncDemo/RemoteOrDbDataSourceEmulation.cs b/DataGridAsyncDemo/RemoteOrDbDataSourceEmulation.cs index 628dc6b..2efb82b 100644 --- a/DataGridAsyncDemo/RemoteOrDbDataSourceEmulation.cs +++ b/DataGridAsyncDemo/RemoteOrDbDataSourceEmulation.cs @@ -1,269 +1,280 @@ #region - - #endregion +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq.Dynamic; +using System.Text.RegularExpressions; +using DataGridAsyncDemo.filtersort; +using SortDescription = DataGridAsyncDemo.filtersort.SortDescription; + namespace DataGridAsyncDemo { - using System; - using System.Collections.Generic; - using System.Collections.Specialized; - using System.ComponentModel; - using System.Linq.Dynamic; - using System.Text.RegularExpressions; - using filtersort; - using SortDescription = filtersort.SortDescription; - - /// - /// Emulate a remote data repository (list of item + sort & filter values) - /// - public class RemoteOrDbDataSourceEmulation : IFilteredSortedSourceProviderAsync - { - #region statics - - //private static RemoteOrDbDataSourceEmulation _instance; - private static readonly object _syncRoot = new Object(); - - #endregion - - #region fields - - private readonly FilterDescriptionList _filterDescriptionList = new FilterDescriptionList(); - private readonly List _items = new List(); - private readonly List _orderedItems = new List(); - private readonly SortDescriptionList _sortDescriptionList = new SortDescriptionList(); - private bool _isFilteredItemsValid; - private string _orderByLinqExpression = ""; - private string _whereLinqExpression = ""; - - #endregion - - public RemoteOrDbDataSourceEmulation() + /// + /// Emulate a remote data repository (list of item + sort & filter values) + /// + public class RemoteOrDbDataSourceEmulation : IFilteredSortedSourceProviderAsync { - for ( int i = 0; i < 100000; i++ ) - { - _items.Add( new RemoteOrDbDataItem( "Name_" + i, "Str1_" + i, "Str1_" + i, i, i ) ); - } + #region statics - _sortDescriptionList.CollectionChanged += SortDescriptionListOnCollectionChanged; - _filterDescriptionList.CollectionChanged += FilterDescriptionListOnCollectionChanged; - } + //private static RemoteOrDbDataSourceEmulation _instance; + private static readonly object _syncRoot = new Object(); - #region properties + #endregion - public IList FilteredOrderedItems - { - get - { - if ( !_isFilteredItemsValid ) + public RemoteOrDbDataSourceEmulation() { - lock (this) - { - _orderedItems.Clear(); - - try - { - if ( string.IsNullOrWhiteSpace( WhereLinqExpression ) && string.IsNullOrWhiteSpace( OrderByLinqExpression ) ) - _orderedItems.AddRange( _items ); - else if ( !string.IsNullOrWhiteSpace( WhereLinqExpression ) && string.IsNullOrWhiteSpace( OrderByLinqExpression ) ) - _orderedItems.AddRange( _items.Where( WhereLinqExpression ) ); - else if ( string.IsNullOrWhiteSpace( WhereLinqExpression ) && !string.IsNullOrWhiteSpace( OrderByLinqExpression ) ) - _orderedItems.AddRange( _items.OrderBy( OrderByLinqExpression ) ); - else if ( !string.IsNullOrWhiteSpace( WhereLinqExpression ) && !string.IsNullOrWhiteSpace( OrderByLinqExpression ) ) - _orderedItems.AddRange( _items.Where( WhereLinqExpression ).OrderBy( OrderByLinqExpression ) ); - } - catch ( Exception ex ) + for (int i = 0; i < 100000; i++) { - int aa = 0; + this._items.Add(new RemoteOrDbDataItem("Name_" + i, "Str1_" + i, "Str1_" + i, i, i)); } - _isFilteredItemsValid = true; - } + + this.SortDescriptionList.CollectionChanged += this.SortDescriptionListOnCollectionChanged; + this.FilterDescriptionList.CollectionChanged += this.FilterDescriptionListOnCollectionChanged; } - return _orderedItems; - } - } - public string OrderByLinqExpression - { - get { return _orderByLinqExpression; } - set - { - if ( !string.Equals( _orderByLinqExpression, value ) ) + public bool Contains(RemoteOrDbDataItem item) { - _orderByLinqExpression = value; - _isFilteredItemsValid = false; + return this._items.Contains(item); } - } - } - public string WhereLinqExpression - { - get { return _whereLinqExpression; } - set - { - if ( !string.Equals( _whereLinqExpression, value ) ) + #region fields + + private readonly List _items = new List(); + private readonly List _orderedItems = new List(); + private bool _isFilteredItemsValid; + private string _orderByLinqExpression = ""; + private string _whereLinqExpression = ""; + + #endregion + + #region properties + + public IList FilteredOrderedItems { - _whereLinqExpression = value; - _isFilteredItemsValid = false; + get + { + if (!this._isFilteredItemsValid) + { + lock (this) + { + this._orderedItems.Clear(); + + try + { + if (string.IsNullOrWhiteSpace(this.WhereLinqExpression) && + string.IsNullOrWhiteSpace(this.OrderByLinqExpression)) + this._orderedItems.AddRange(this._items); + else if (!string.IsNullOrWhiteSpace(this.WhereLinqExpression) && + string.IsNullOrWhiteSpace(this.OrderByLinqExpression)) + this._orderedItems.AddRange(this._items.Where(this.WhereLinqExpression)); + else if (string.IsNullOrWhiteSpace(this.WhereLinqExpression) && + !string.IsNullOrWhiteSpace(this.OrderByLinqExpression)) + this._orderedItems.AddRange(this._items.OrderBy(this.OrderByLinqExpression)); + else if (!string.IsNullOrWhiteSpace(this.WhereLinqExpression) && + !string.IsNullOrWhiteSpace(this.OrderByLinqExpression)) + this._orderedItems.AddRange(this._items.Where(this.WhereLinqExpression) + .OrderBy(this.OrderByLinqExpression)); + } + catch (Exception ex) + { + int aa = 0; + } + + this._isFilteredItemsValid = true; + } + } + + return this._orderedItems; + } } - } - } - #endregion + public string OrderByLinqExpression + { + get { return this._orderByLinqExpression; } + set + { + if (!string.Equals(this._orderByLinqExpression, value)) + { + this._orderByLinqExpression = value; + this._isFilteredItemsValid = false; + } + } + } - #region public members + public string WhereLinqExpression + { + get { return this._whereLinqExpression; } + set + { + if (!string.Equals(this._whereLinqExpression, value)) + { + this._whereLinqExpression = value; + this._isFilteredItemsValid = false; + } + } + } - public void OrderBy( string orderByExpression ) - { - if ( !string.Equals( orderByExpression, OrderByLinqExpression ) ) - OrderByLinqExpression = orderByExpression; - } + #endregion - public void Where( string whereExpression ) - { - if ( !string.Equals( whereExpression, WhereLinqExpression ) ) - WhereLinqExpression = whereExpression; - } + #region public members - #endregion + public void OrderBy(string orderByExpression) + { + if (!string.Equals(orderByExpression, this.OrderByLinqExpression)) + this.OrderByLinqExpression = orderByExpression; + } - #region filter & sort Descrioption list + public void Where(string whereExpression) + { + if (!string.Equals(whereExpression, this.WhereLinqExpression)) this.WhereLinqExpression = whereExpression; + } - public SortDescriptionList SortDescriptionList - { - get { return _sortDescriptionList; } - } + #endregion - public FilterDescriptionList FilterDescriptionList - { - get { return _filterDescriptionList; } - } + #region filter & sort Descrioption list - private void SortDescriptionListOnCollectionChanged( object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs ) - { - string sort = ""; + public SortDescriptionList SortDescriptionList { get; } = new SortDescriptionList(); - bool sortFound = false; - foreach ( SortDescription sortDescription in _sortDescriptionList ) - { - if ( sortFound ) - sort += ", "; + public FilterDescriptionList FilterDescriptionList { get; } = new FilterDescriptionList(); - sortFound = true; + private void SortDescriptionListOnCollectionChanged(object sender, + NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + string sort = ""; - sort += sortDescription.PropertyName; - sort += ( sortDescription.Direction == ListSortDirection.Ascending ) ? " ASC" : " DESC"; - } + bool sortFound = false; + foreach (SortDescription sortDescription in this.SortDescriptionList) + { + if (sortFound) + sort += ", "; - //if ((!sortFound) && (!string.IsNullOrWhiteSpace( primaryKey ))) - // sort += primaryKey + " ASC"; + sortFound = true; - OrderByLinqExpression = sort; - } + sort += sortDescription.PropertyName; + sort += (sortDescription.Direction == ListSortDirection.Ascending) ? " ASC" : " DESC"; + } - private void FilterDescriptionListOnCollectionChanged( object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs ) - { - if ( notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Reset ) - { - string filter = ""; + //if ((!sortFound) && (!string.IsNullOrWhiteSpace( primaryKey ))) + // sort += primaryKey + " ASC"; - bool filterFound = false; - foreach ( FilterDescription filterDescription in _filterDescriptionList ) - { - string subFilter = GetLinqQueryString( filterDescription ); - if ( !string.IsNullOrWhiteSpace( subFilter ) ) - { - if ( filterFound ) - filter += " and "; - filterFound = true; - filter += " " + subFilter + " "; - } + this.OrderByLinqExpression = sort; } - WhereLinqExpression = filter; - } - } + private void FilterDescriptionListOnCollectionChanged(object sender, + NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Reset) + { + string filter = ""; - #region query builder + bool filterFound = false; + foreach (FilterDescription filterDescription in this.FilterDescriptionList) + { + string subFilter = GetLinqQueryString(filterDescription); + if (!string.IsNullOrWhiteSpace(subFilter)) + { + if (filterFound) + filter += " and "; + filterFound = true; + filter += " " + subFilter + " "; + } + } - private static readonly Regex _regexSplit = new Regex( @"(and)|(or)|(==)|(<>)|(!=)|(<=)|(>=)|(&&)|(\|\|)|(=)|(>)|(<)|(\*[\-_a-zA-Z0-9]+)|([\-_a-zA-Z0-9]+\*)|([\-_a-zA-Z0-9]+)", - RegexOptions.IgnoreCase ); + this.WhereLinqExpression = filter; + } + } - private static readonly Regex _regexOp = new Regex( @"(and)|(or)|(==)|(<>)|(!=)|(<=)|(>=)|(&&)|(\|\|)|(=)|(>)|(<)", RegexOptions.IgnoreCase ); - private static readonly Regex _regexComparOp = new Regex( @"(==)|(<>)|(!=)|(<=)|(>=)|(=)|(>)|(<)", RegexOptions.None ); + #region query builder - private static string GetLinqQueryString( FilterDescription filterDescription ) - { - string ret = ""; + private static readonly Regex _regexSplit = new Regex( + @"(and)|(or)|(==)|(<>)|(!=)|(<=)|(>=)|(&&)|(\|\|)|(=)|(>)|(<)|(\*[\-_a-zA-Z0-9]+)|([\-_a-zA-Z0-9]+\*)|([\-_a-zA-Z0-9]+)", + RegexOptions.IgnoreCase); - if ( !string.IsNullOrWhiteSpace( filterDescription.Filter ) ) - { - // using user str + linq.dynamic - try - { - // xceed syntax : empty (contains), AND (uppercase), OR (uppercase), <>, * (end with), =, >, >=, <, <=, * (start with) - // see http://doc.xceedsoft.com/products/XceedWpfDataGrid/Filter_Row.html - // linq.dynamic syntax : =, ==, <>, !=, <, >, <=, >=, &&, and, ||, or, x.m(…) (where x is the attrib and m the function (ex: Contains, StartsWith, EndsWith ...) - // see D:\DevC#\VirtualisingCollectionTest1\DynamicQuery\Dynamic Expressions.html - // ex : RemoteOrDbDataSourceEmulation.Instance.Items.Where( "Name.Contains(\"e_1\") or Name.Contains(\"e_2\")" ); + private static readonly Regex _regexOp = + new Regex(@"(and)|(or)|(==)|(<>)|(!=)|(<=)|(>=)|(&&)|(\|\|)|(=)|(>)|(<)", RegexOptions.IgnoreCase); - string exp = filterDescription.Filter; + private static readonly Regex _regexComparOp = + new Regex(@"(==)|(<>)|(!=)|(<=)|(>=)|(=)|(>)|(<)", RegexOptions.None); - // arrange expression + private static string GetLinqQueryString(FilterDescription filterDescription) + { + string ret = ""; - bool previousTermIsOperator = false; - foreach ( Match match in _regexSplit.Matches( exp ) ) - { - if ( match.Success ) + if (!string.IsNullOrWhiteSpace(filterDescription.Filter)) { - //TODO processing results - if ( _regexOp.IsMatch( match.Value ) ) - { - if ( _regexComparOp.IsMatch( match.Value ) ) - { - // simple operator >, <, ==, != ... - ret += " " + filterDescription.PropertyName + " " + match.Value; - previousTermIsOperator = true; - } - else - { - // and, or ... - ret += " " + match.Value; - previousTermIsOperator = false; - } - } - else - { - // Value - if ( previousTermIsOperator ) + // using user str + linq.dynamic + try { - ret += " " + match.Value; - previousTermIsOperator = false; + // xceed syntax : empty (contains), AND (uppercase), OR (uppercase), <>, * (end with), =, >, >=, <, <=, * (start with) + // see http://doc.xceedsoft.com/products/XceedWpfDataGrid/Filter_Row.html + // linq.dynamic syntax : =, ==, <>, !=, <, >, <=, >=, &&, and, ||, or, x.m(…) (where x is the attrib and m the function (ex: Contains, StartsWith, EndsWith ...) + // see D:\DevC#\VirtualisingCollectionTest1\DynamicQuery\Dynamic Expressions.html + // ex : RemoteOrDbDataSourceEmulation.Instance.Items.Where( "Name.Contains(\"e_1\") or Name.Contains(\"e_2\")" ); + + string exp = filterDescription.Filter; + + // arrange expression + + bool previousTermIsOperator = false; + foreach (Match match in _regexSplit.Matches(exp)) + { + if (match.Success) + { + //TODO processing results + if (_regexOp.IsMatch(match.Value)) + { + if (_regexComparOp.IsMatch(match.Value)) + { + // simple operator >, <, ==, != ... + ret += " " + filterDescription.PropertyName + " " + match.Value; + previousTermIsOperator = true; + } + else + { + // and, or ... + ret += " " + match.Value; + previousTermIsOperator = false; + } + } + else + { + // Value + if (previousTermIsOperator) + { + ret += " " + match.Value; + previousTermIsOperator = false; + } + else + { + if (match.Value.StartsWith("*")) + ret += " " + filterDescription.PropertyName + ".EndsWith( \"" + + match.Value.Substring(1) + "\" )"; + else if (match.Value.EndsWith("*")) + ret += " " + filterDescription.PropertyName + ".StartsWith( \"" + + match.Value.Substring(0, match.Value.Length - 1) + "\" )"; + else + ret += " " + filterDescription.PropertyName + ".Contains( \"" + match.Value + + "\" )"; + previousTermIsOperator = false; + } + } + } + } } - else + catch (Exception) { - if ( match.Value.StartsWith( "*" ) ) - ret += " " + filterDescription.PropertyName + ".EndsWith( \"" + match.Value.Substring( 1 ) + "\" )"; - else if ( match.Value.EndsWith( "*" ) ) - ret += " " + filterDescription.PropertyName + ".StartsWith( \"" + match.Value.Substring( 0, match.Value.Length - 1 ) + "\" )"; - else - ret += " " + filterDescription.PropertyName + ".Contains( \"" + match.Value + "\" )"; - previousTermIsOperator = false; } - } } - } - } - catch ( Exception ) - {} - } - return ret; - } + return ret; + } - #endregion query builder + #endregion query builder - #endregion filter & sort Descrioption list - } -} + #endregion filter & sort Descrioption list + } +} \ No newline at end of file diff --git a/DataGridAsyncDemo/filtersort/DescriptionList.cs b/DataGridAsyncDemo/filtersort/DescriptionList.cs index 3f590c2..be38be6 100644 --- a/DataGridAsyncDemo/filtersort/DescriptionList.cs +++ b/DataGridAsyncDemo/filtersort/DescriptionList.cs @@ -1,72 +1,79 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; + namespace DataGridAsyncDemo.filtersort { - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.Specialized; - - public class DescriptionList : IEnumerable, IEnumerable, INotifyCollectionChanged - where T : IFilterOrderDescription - { - private readonly List _filterDescriptions = new List(); - - IEnumerator IEnumerable.GetEnumerator() + public class DescriptionList : IEnumerable, IEnumerable, INotifyCollectionChanged + where T : IFilterOrderDescription { - return _filterDescriptions.GetEnumerator(); - } + private readonly List _filterDescriptions = new List(); - IEnumerator IEnumerable.GetEnumerator() - { - return _filterDescriptions.GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return this._filterDescriptions.GetEnumerator(); + } - public event NotifyCollectionChangedEventHandler CollectionChanged; + IEnumerator IEnumerable.GetEnumerator() + { + return this._filterDescriptions.GetEnumerator(); + } - /// - /// If it exist, remove existing filter that apply on same property name. The add item arg at first position into filter list. - /// - /// - public void Add( T item ) - { - int index = _filterDescriptions.FindIndex( description => description.PropertyName.Equals( item.PropertyName, StringComparison.Ordinal ) ); - if (index >= 0) - { - T removed = _filterDescriptions[index]; - _filterDescriptions.RemoveAt( index ); - //OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, removed, index ) ); - _filterDescriptions.Insert( 0, item ); - //OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Add, item, 0 ) ); + public event NotifyCollectionChangedEventHandler CollectionChanged; - OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Move, removed, 0, index ) ); - } - else - { - _filterDescriptions.Insert( 0, item ); - OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Add, item, 0 ) ); - } - } + /// + /// If it exist, remove existing filter that apply on same property name. The add item arg at first position into + /// filter list. + /// + /// + public void Add(T item) + { + int index = this._filterDescriptions.FindIndex(description => + description.PropertyName.Equals(item.PropertyName, StringComparison.Ordinal)); + if (index >= 0) + { + T removed = this._filterDescriptions[index]; + this._filterDescriptions.RemoveAt(index); + //OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, removed, index ) ); + this._filterDescriptions.Insert(0, item); + //OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Add, item, 0 ) ); - protected void OnCollectionChanged( NotifyCollectionChangedEventArgs arg ) - { - var evnt = CollectionChanged; + this.OnCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, removed, 0, index)); + } + else + { + this._filterDescriptions.Insert(0, item); + this.OnCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, 0)); + } + } - if (evnt != null) - evnt( this, arg ); - } + /// + /// If it exist, remove existing filter that apply on same property name. The add item arg at first position into + /// filter list. + /// + /// + public void Remove(string propertyName) + { + int index = this._filterDescriptions.FindIndex(description => + description.PropertyName.Equals(propertyName, StringComparison.Ordinal)); + if (index >= 0) + { + T removed = this._filterDescriptions[index]; + this._filterDescriptions.RemoveAt(index); + this.OnCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed, index)); + } + } - /// - /// If it exist, remove existing filter that apply on same property name. The add item arg at first position into filter list. - /// - /// - public void Remove( string propertyName ) - { - int index = _filterDescriptions.FindIndex( description => description.PropertyName.Equals( propertyName, StringComparison.Ordinal ) ); - if (index >= 0) - { - T removed = _filterDescriptions[index]; - _filterDescriptions.RemoveAt( index ); - OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, removed, index ) ); - } + protected void OnCollectionChanged(NotifyCollectionChangedEventArgs arg) + { + var evnt = this.CollectionChanged; + + if (evnt != null) + evnt(this, arg); + } } - } } \ No newline at end of file diff --git a/DataGridAsyncDemo/filtersort/FilterDescription.cs b/DataGridAsyncDemo/filtersort/FilterDescription.cs index af533ad..7f7714e 100644 --- a/DataGridAsyncDemo/filtersort/FilterDescription.cs +++ b/DataGridAsyncDemo/filtersort/FilterDescription.cs @@ -1,14 +1,14 @@ namespace DataGridAsyncDemo.filtersort { - public class FilterDescription : IFilterOrderDescription - { - public FilterDescription( string propertyName, string filter ) + public class FilterDescription : IFilterOrderDescription { - PropertyName = propertyName; - Filter = filter; - } + public FilterDescription(string propertyName, string filter) + { + this.PropertyName = propertyName; + this.Filter = filter; + } - public string Filter { get; set; } - public string PropertyName { get; set; } - } + public string Filter { get; set; } + public string PropertyName { get; set; } + } } \ No newline at end of file diff --git a/DataGridAsyncDemo/filtersort/FilterDescriptionList.cs b/DataGridAsyncDemo/filtersort/FilterDescriptionList.cs index ed3e0c0..49b5120 100644 --- a/DataGridAsyncDemo/filtersort/FilterDescriptionList.cs +++ b/DataGridAsyncDemo/filtersort/FilterDescriptionList.cs @@ -1,12 +1,12 @@ +using System.Collections.Specialized; + namespace DataGridAsyncDemo.filtersort { - using System.Collections.Specialized; - - public class FilterDescriptionList : DescriptionList - { - public void OnCollectionReset() + public class FilterDescriptionList : DescriptionList { - OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset ) ); + public void OnCollectionReset() + { + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } } - } } \ No newline at end of file diff --git a/DataGridAsyncDemo/filtersort/IFilterOrderDescription.cs b/DataGridAsyncDemo/filtersort/IFilterOrderDescription.cs index 88d3ed1..462afbd 100644 --- a/DataGridAsyncDemo/filtersort/IFilterOrderDescription.cs +++ b/DataGridAsyncDemo/filtersort/IFilterOrderDescription.cs @@ -1,7 +1,7 @@ namespace DataGridAsyncDemo.filtersort { - public interface IFilterOrderDescription - { - string PropertyName { get; set; } - } + public interface IFilterOrderDescription + { + string PropertyName { get; set; } + } } \ No newline at end of file diff --git a/DataGridAsyncDemo/filtersort/IFilteredSortedSourceProviderAsync.cs b/DataGridAsyncDemo/filtersort/IFilteredSortedSourceProviderAsync.cs index 0de81c9..d13b5bc 100644 --- a/DataGridAsyncDemo/filtersort/IFilteredSortedSourceProviderAsync.cs +++ b/DataGridAsyncDemo/filtersort/IFilteredSortedSourceProviderAsync.cs @@ -1,7 +1,7 @@ namespace DataGridAsyncDemo.filtersort { - public interface IFilteredSortedSourceProviderAsync - { - SortDescriptionList SortDescriptionList { get; } - } + public interface IFilteredSortedSourceProviderAsync + { + SortDescriptionList SortDescriptionList { get; } + } } \ No newline at end of file diff --git a/DataGridAsyncDemo/filtersort/ParentOfTypeExtensions.cs b/DataGridAsyncDemo/filtersort/ParentOfTypeExtensions.cs index 93a97d5..3cdff3f 100644 --- a/DataGridAsyncDemo/filtersort/ParentOfTypeExtensions.cs +++ b/DataGridAsyncDemo/filtersort/ParentOfTypeExtensions.cs @@ -1,124 +1,125 @@ -namespace DataGridAsyncDemo.filtersort -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using System.Windows; - using System.Windows.Media; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Windows; +using System.Windows.Media; - internal static class ArgumentVerificationExtensions - { - public static void TestNotNull( this object parameter, string parameterName ) +namespace DataGridAsyncDemo.filtersort +{ + internal static class ArgumentVerificationExtensions { - if (parameter == null) - { - throw new ArgumentNullException( parameterName ); - } - } + public static void TestNotEmptyString(this string parameter, string parameterName) + { + if (string.IsNullOrEmpty(parameter)) + { + throw new ArgumentException( + string.Format("The parameter '{0}' should not be empty string.", parameterName), parameterName); + } + } - public static void TestNotEmptyString( this string parameter, string parameterName ) - { - if (string.IsNullOrEmpty( parameter )) - { - throw new ArgumentException( string.Format( "The parameter '{0}' should not be empty string.", parameterName ), parameterName ); - } + public static void TestNotNull(this object parameter, string parameterName) + { + if (parameter == null) + { + throw new ArgumentNullException(parameterName); + } + } } - } - /// - /// Contains extension methods for enumerating the parents of an element. - /// - public static class ParentOfTypeExtensions - { /// - /// Gets the parent element from the visual tree by given type. + /// Contains extension methods for enumerating the parents of an element. /// - public static T ParentOfType( this DependencyObject element ) where T : DependencyObject + public static class ParentOfTypeExtensions { - if (element == null) - { - return null; - } + /// + /// This recurses the visual tree for ancestors of a specific type. + /// + public static IEnumerable GetAncestors(this DependencyObject element) where T : class + { + return element.GetParents().OfType(); + } - return element.GetParents().OfType().FirstOrDefault(); - } + /// + /// This recurses the visual tree for a parent of a specific type. + /// + public static T GetParent(this DependencyObject element) where T : FrameworkElement + { + return element.ParentOfType(); + } + /// + /// Enumerates through element's parents in the visual tree. + /// + public static IEnumerable GetParents(this DependencyObject element) + { + if (element == null) + { + throw new ArgumentNullException("element"); + } - /// - /// Determines whether the element is an ancestor of the descendant. - /// - /// true if the visual object is an ancestor of descendant; otherwise, false. - public static bool IsAncestorOf( this DependencyObject element, DependencyObject descendant ) - { - element.TestNotNull( "element" ); - descendant.TestNotNull( "descendant" ); + while ((element = element.GetParent()) != null) + { + yield return element; + } + } - return descendant == element || descendant.GetParents().Contains( element ); - } + /// + /// Searches up in the visual tree for parent element of the specified type. + /// + /// + /// The type of the parent that will be searched up in the visual object hierarchy. + /// The type should be . + /// + /// + /// The target which visual parents will be traversed. + /// + /// Visual parent of the specified type if there is any, otherwise null. + [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] + public static T GetVisualParent(this DependencyObject element) where T : DependencyObject + { + return element.ParentOfType(); + } - /// - /// Searches up in the visual tree for parent element of the specified type. - /// - /// - /// The type of the parent that will be searched up in the visual object hierarchy. - /// The type should be . - /// - /// - /// The target which visual parents will be traversed. - /// - /// Visual parent of the specified type if there is any, otherwise null. - [SuppressMessage( "Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter" )] - public static T GetVisualParent( this DependencyObject element ) where T : DependencyObject - { - return element.ParentOfType(); - } - /// - /// This recurses the visual tree for ancestors of a specific type. - /// - public static IEnumerable GetAncestors( this DependencyObject element ) where T : class - { - return element.GetParents().OfType(); - } + /// + /// Determines whether the element is an ancestor of the descendant. + /// + /// true if the visual object is an ancestor of descendant; otherwise, false. + public static bool IsAncestorOf(this DependencyObject element, DependencyObject descendant) + { + element.TestNotNull("element"); + descendant.TestNotNull("descendant"); - /// - /// This recurses the visual tree for a parent of a specific type. - /// - public static T GetParent( this DependencyObject element ) where T : FrameworkElement - { - return element.ParentOfType(); - } + return descendant == element || descendant.GetParents().Contains(element); + } - /// - /// Enumerates through element's parents in the visual tree. - /// - public static IEnumerable GetParents( this DependencyObject element ) - { - if (element == null) - { - throw new ArgumentNullException( "element" ); - } + /// + /// Gets the parent element from the visual tree by given type. + /// + public static T ParentOfType(this DependencyObject element) where T : DependencyObject + { + if (element == null) + { + return null; + } - while ((element = element.GetParent()) != null) - { - yield return element; - } - } + return element.GetParents().OfType().FirstOrDefault(); + } - private static DependencyObject GetParent( this DependencyObject element ) - { - var parent = VisualTreeHelper.GetParent( element ); - if (parent == null) - { - var frameworkElement = element as FrameworkElement; - if (frameworkElement != null) + private static DependencyObject GetParent(this DependencyObject element) { - parent = frameworkElement.Parent; + var parent = VisualTreeHelper.GetParent(element); + if (parent == null) + { + var frameworkElement = element as FrameworkElement; + if (frameworkElement != null) + { + parent = frameworkElement.Parent; + } + } + + return parent; } - } - return parent; } - } - -} +} \ No newline at end of file diff --git a/DataGridAsyncDemo/filtersort/SortDescription.cs b/DataGridAsyncDemo/filtersort/SortDescription.cs index 7fb0242..cee9062 100644 --- a/DataGridAsyncDemo/filtersort/SortDescription.cs +++ b/DataGridAsyncDemo/filtersort/SortDescription.cs @@ -1,16 +1,16 @@ +using System.ComponentModel; + namespace DataGridAsyncDemo.filtersort { - using System.ComponentModel; - - public class SortDescription : IFilterOrderDescription - { - public SortDescription( string propertyName, ListSortDirection? direction ) + public class SortDescription : IFilterOrderDescription { - Direction = direction; - PropertyName = propertyName; - } + public SortDescription(string propertyName, ListSortDirection? direction) + { + this.Direction = direction; + this.PropertyName = propertyName; + } - public ListSortDirection? Direction { get; set; } - public string PropertyName { get; set; } - } + public ListSortDirection? Direction { get; set; } + public string PropertyName { get; set; } + } } \ No newline at end of file diff --git a/DataGridAsyncDemo/filtersort/SortDescriptionList.cs b/DataGridAsyncDemo/filtersort/SortDescriptionList.cs index 68023aa..564b48f 100644 --- a/DataGridAsyncDemo/filtersort/SortDescriptionList.cs +++ b/DataGridAsyncDemo/filtersort/SortDescriptionList.cs @@ -1,5 +1,6 @@ namespace DataGridAsyncDemo.filtersort { - public class SortDescriptionList : DescriptionList - { } + public class SortDescriptionList : DescriptionList + { + } } \ No newline at end of file diff --git a/DataGridAsyncDemo/packages.config b/DataGridAsyncDemo/packages.config deleted file mode 100644 index 59ab5b6..0000000 --- a/DataGridAsyncDemo/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/App.config b/DataGridAsyncDemoMVVM/App.config new file mode 100644 index 0000000..7f1a3da --- /dev/null +++ b/DataGridAsyncDemoMVVM/App.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/App.xaml b/DataGridAsyncDemoMVVM/App.xaml new file mode 100644 index 0000000..f90e982 --- /dev/null +++ b/DataGridAsyncDemoMVVM/App.xaml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/App.xaml.cs b/DataGridAsyncDemoMVVM/App.xaml.cs new file mode 100644 index 0000000..d71b991 --- /dev/null +++ b/DataGridAsyncDemoMVVM/App.xaml.cs @@ -0,0 +1,11 @@ +using System.Windows; + +namespace DataGridAsyncDemoMVVM +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/DataGridAsyncDemoMVVM.csproj b/DataGridAsyncDemoMVVM/DataGridAsyncDemoMVVM.csproj new file mode 100644 index 0000000..3d997fb --- /dev/null +++ b/DataGridAsyncDemoMVVM/DataGridAsyncDemoMVVM.csproj @@ -0,0 +1,131 @@ + + + + + Debug + AnyCPU + {DE19AAA5-881C-48BE-A8E7-A8799F5C4BD3} + WinExe + DataGridAsyncDemoMVVM + DataGridAsyncDemoMVVM + v4.6.1 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + 1.0.0.0 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + 4.0 + + + + + + + + + MSBuild:Compile + Designer + + + + + + + + + + + + + + + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + {d8d58c1d-2596-45ad-83bd-e0d5c9fa7f69} + VirtualizingCollection + + + + + + + \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/MainViewModel.cs b/DataGridAsyncDemoMVVM/MainViewModel.cs new file mode 100644 index 0000000..f9c053a --- /dev/null +++ b/DataGridAsyncDemoMVVM/MainViewModel.cs @@ -0,0 +1,83 @@ +using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Data; +using AlphaChiTech.Virtualization; +using AlphaChiTech.Virtualization.Pageing; +using DataGridAsyncDemoMVVM.filtersort; +using GalaSoft.MvvmLight.Command; +using SortDescription = DataGridAsyncDemoMVVM.filtersort.SortDescription; + +namespace DataGridAsyncDemoMVVM +{ + internal class MainViewModel + { + private readonly VirtualizingObservableCollection + _myDataVirtualizedAsyncFilterSortObservableCollection; + + private readonly RemoteOrDbDataSourceAsyncProxy _myRemoteOrDbDataSourceAsyncProxy; + + private int _filterWaitingCount; + + public MainViewModel() + { + this._myRemoteOrDbDataSourceAsyncProxy = + new RemoteOrDbDataSourceAsyncProxy(new RemoteOrDbDataSourceEmulation(100)); + this._myDataVirtualizedAsyncFilterSortObservableCollection = + new VirtualizingObservableCollection( + new PaginationManager(this._myRemoteOrDbDataSourceAsyncProxy, + pageSize: 10, maxPages: 2)); + this.MyDataVirtualizedAsyncFilterSortObservableCollectionCollectionView = + CollectionViewSource.GetDefaultView(this._myDataVirtualizedAsyncFilterSortObservableCollection); + + this.FilterCommand = new RelayCommand(async o => await this.Filter(o)); + this.SortCommand = new RelayCommand(async o => await this.Sort(o)); + } + + public RelayCommand FilterCommand { get; } + + public ICollectionView MyDataVirtualizedAsyncFilterSortObservableCollectionCollectionView { get; } + + public RelayCommand SortCommand { get; } + + private async Task Filter(MemberPathFilterText memberPathFilterText) + { + if (string.IsNullOrWhiteSpace(memberPathFilterText.FilterText)) + this._myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.Remove(memberPathFilterText + .MemberPath); + else + this._myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.Add( + new FilterDescription(memberPathFilterText.MemberPath, memberPathFilterText.FilterText)); + Interlocked.Increment(ref this._filterWaitingCount); + await Task.Delay(500); + if (Interlocked.Decrement(ref this._filterWaitingCount) != 0) return; + this._myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.OnCollectionReset(); + this._myDataVirtualizedAsyncFilterSortObservableCollection.Clear(); + } + + private async Task Sort(MemberPathSortingDirection memberPathSortingDirection) + { + while (this._filterWaitingCount != 0) + await Task.Delay(500); + var sortDirection = memberPathSortingDirection.SortDirection; + var sortMemberPath = memberPathSortingDirection.MemberPath; + switch (sortDirection) + { + case null: + this._myRemoteOrDbDataSourceAsyncProxy.SortDescriptionList.Remove(sortMemberPath); + break; + case ListSortDirection.Ascending: + this._myRemoteOrDbDataSourceAsyncProxy.SortDescriptionList.Add( + new SortDescription(sortMemberPath, ListSortDirection.Ascending)); + break; + case ListSortDirection.Descending: + this._myRemoteOrDbDataSourceAsyncProxy.SortDescriptionList.Add( + new SortDescription(sortMemberPath, ListSortDirection.Descending)); + break; + } + + this._myRemoteOrDbDataSourceAsyncProxy.FilterDescriptionList.OnCollectionReset(); + this._myDataVirtualizedAsyncFilterSortObservableCollection.Clear(); + } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/MainWindow.xaml b/DataGridAsyncDemoMVVM/MainWindow.xaml new file mode 100644 index 0000000..06a365d --- /dev/null +++ b/DataGridAsyncDemoMVVM/MainWindow.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/MainWindow.xaml.cs b/DataGridAsyncDemoMVVM/MainWindow.xaml.cs new file mode 100644 index 0000000..907f17f --- /dev/null +++ b/DataGridAsyncDemoMVVM/MainWindow.xaml.cs @@ -0,0 +1,41 @@ +#region + +using System; +using System.Windows; +using System.Windows.Threading; +using AlphaChiTech.VirtualizingCollection; + +#endregion + +namespace DataGridAsyncDemoMVVM +{ + #region + + #endregion + + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + public MainWindow() + { + //this routine only needs to run once, so first check to make sure the + //VirtualizationManager isn’t already initialized + if (!VirtualizationManager.IsInitialized) + { + //set the VirtualizationManager’s UIThreadExcecuteAction. In this case + //we’re using Dispatcher.Invoke to give the VirtualizationManager access + //to the dispatcher thread, and using a DispatcherTimer to run the background + //operations the VirtualizationManager needs to run to reclaim pages and manage memory. + VirtualizationManager.Instance.UiThreadExcecuteAction = a => Application.Current.Dispatcher.Invoke(a); + new DispatcherTimer(TimeSpan.FromMilliseconds(10), + DispatcherPriority.Background, + delegate { VirtualizationManager.Instance.ProcessActions(); }, + this.Dispatcher).Start(); + } + + this.InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/Properties/AssemblyInfo.cs b/DataGridAsyncDemoMVVM/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..215acf3 --- /dev/null +++ b/DataGridAsyncDemoMVVM/Properties/AssemblyInfo.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DataGridAsyncDemoMVVM")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DataGridAsyncDemoMVVM")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/Properties/Resources.Designer.cs b/DataGridAsyncDemoMVVM/Properties/Resources.Designer.cs new file mode 100644 index 0000000..80eab27 --- /dev/null +++ b/DataGridAsyncDemoMVVM/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace DataGridAsyncDemoMVVM.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DataGridAsyncDemoMVVM.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/DataGridAsyncDemoMVVM/Properties/Resources.resx b/DataGridAsyncDemoMVVM/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/DataGridAsyncDemoMVVM/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/Properties/Settings.Designer.cs b/DataGridAsyncDemoMVVM/Properties/Settings.Designer.cs new file mode 100644 index 0000000..440a360 --- /dev/null +++ b/DataGridAsyncDemoMVVM/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace DataGridAsyncDemoMVVM.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.5.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/DataGridAsyncDemoMVVM/Properties/Settings.settings b/DataGridAsyncDemoMVVM/Properties/Settings.settings new file mode 100644 index 0000000..c14891b --- /dev/null +++ b/DataGridAsyncDemoMVVM/Properties/Settings.settings @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/RemoteOrDbDataItem.cs b/DataGridAsyncDemoMVVM/RemoteOrDbDataItem.cs new file mode 100644 index 0000000..d1963d7 --- /dev/null +++ b/DataGridAsyncDemoMVVM/RemoteOrDbDataItem.cs @@ -0,0 +1,28 @@ +namespace DataGridAsyncDemoMVVM +{ + public class RemoteOrDbDataItem + { + public RemoteOrDbDataItem() + { + } + + public RemoteOrDbDataItem(string name, string str1, string str2, int int1, double double1) + { + this.Name = name; + this.Str1 = str1; + this.Str2 = str2; + this.Int1 = int1; + this.Double1 = double1; + } + + #region properties + + public double Double1 { get; set; } + public int Int1 { get; set; } + public string Name { get; set; } + public string Str1 { get; set; } + public string Str2 { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/RemoteOrDbDataSourceAsyncProxy.cs b/DataGridAsyncDemoMVVM/RemoteOrDbDataSourceAsyncProxy.cs new file mode 100644 index 0000000..85fcd51 --- /dev/null +++ b/DataGridAsyncDemoMVVM/RemoteOrDbDataSourceAsyncProxy.cs @@ -0,0 +1,144 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AlphaChiTech.Virtualization.Pageing; +using AlphaChiTech.VirtualizingCollection.Interfaces; +using DataGridAsyncDemoMVVM.filtersort; + +namespace DataGridAsyncDemoMVVM +{ + #region + + #endregion + + /// + /// Remote/disk async data proxy. + /// Also add a delay on calls to simulate network/disk delay. + /// + public class RemoteOrDbDataSourceAsyncProxy + : IPagedSourceProviderAsync, IFilteredSortedSourceProviderAsync + { + public RemoteOrDbDataSourceAsyncProxy(RemoteOrDbDataSourceEmulation remoteDatas) + { + this._remoteDatas = remoteDatas; + } + + #region properties + + public FilterDescriptionList FilterDescriptionList => this._remoteDatas.FilterDescriptionList; + + #endregion + + #region IFilteredSortedSourceProviderAsync Members + + public SortDescriptionList SortDescriptionList => this._remoteDatas.SortDescriptionList; + + #endregion + + public bool IsSynchronized { get; } + public object SyncRoot { get; } + + #region fields + + private readonly RemoteOrDbDataSourceEmulation _remoteDatas; + + private readonly Random _rand = new Random(); + + #endregion + + #region IPagedSourceProvider Members (synchronous not available members) + + int IPagedSourceProvider.IndexOf(RemoteOrDbDataItem item) + { + return this._remoteDatas.FilteredOrderedItems.IndexOf(item); + } + + public bool Contains(RemoteOrDbDataItem item) + { + return this._remoteDatas.Contains(item); + } + + public PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder) + { + Task.Delay(50 + (int) Math.Round(this._rand.NextDouble() * 100)).Wait(); // Just to slow it down ! + return new PagedSourceItemsPacket + { + LoadedAt = DateTime.Now, + Items = (from items in this._remoteDatas.FilteredOrderedItems select items).Skip(pageoffset).Take(count) + }; + } + + public int Count + { + get + { + Task.Delay(20 + (int) Math.Round(this._rand.NextDouble() * 30)).Wait(); // Just to slow it down ! + return this._remoteDatas.FilteredOrderedItems.Count; + ; + } + } + + #endregion + + #region public members + + public Task ContainsAsync(RemoteOrDbDataItem item) + { + throw new NotImplementedException(); + } + + public Task GetCountAsync() + { + return Task.Run(() => + { + Task.Delay(20 + (int) Math.Round(this._rand.NextDouble() * 30)).Wait(); // Just to slow it down ! + return this._remoteDatas.FilteredOrderedItems.Count; + }); + } + + public Task> GetItemsAtAsync(int pageoffset, int count, + bool usePlaceholder) + { + Console.WriteLine("Get"); + return Task.Run(() => + { + Task.Delay(50 + (int) Math.Round(this._rand.NextDouble() * 100)).Wait(); // Just to slow it down ! + return new PagedSourceItemsPacket + { + LoadedAt = DateTime.Now, + Items = (from items in this._remoteDatas.FilteredOrderedItems select items).Skip(pageoffset) + .Take(count) + }; + }); + } + + public RemoteOrDbDataItem GetPlaceHolder(int index, int page, int offset) + { + return new RemoteOrDbDataItem {Name = "Waiting [" + page + "/" + offset + "]"}; + } + + /// + /// This returns the index of a specific item. This method is optional – you can just return -1 if you + /// don’t need to use IndexOf. It’s not strictly required if don’t need to be able to seeking to a + /// specific item, but if you are selecting items implementing this method is recommended. + /// + /// + /// + public Task IndexOfAsync(RemoteOrDbDataItem item) + { + return Task.Run(() => this._remoteDatas.FilteredOrderedItems.IndexOf(item)); + } + + /// + /// This is a callback that runs when a Reset is called on a provider. Implementing this is also optional. + /// If you don’t need to do anything in particular when resets occur, you can leave this method body empty. + /// + /// + public void OnReset(int count) + { + // Do nothing for now + } + + #endregion + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/RemoteOrDbDataSourceEmulation.cs b/DataGridAsyncDemoMVVM/RemoteOrDbDataSourceEmulation.cs new file mode 100644 index 0000000..6385648 --- /dev/null +++ b/DataGridAsyncDemoMVVM/RemoteOrDbDataSourceEmulation.cs @@ -0,0 +1,261 @@ +#region + +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq.Dynamic; +using System.Text.RegularExpressions; +using DataGridAsyncDemoMVVM.filtersort; + +namespace DataGridAsyncDemoMVVM +{ + /// + /// Emulate a remote data repository (list of item + sort & filter values) + /// + public class RemoteOrDbDataSourceEmulation : IFilteredSortedSourceProviderAsync + { + public RemoteOrDbDataSourceEmulation(int itemsCount) + { + for (var i = 0; i < itemsCount; i++) + this._items.Add(new RemoteOrDbDataItem("Name_" + i, "Str1_" + i, "Str1_" + i, i, i)); + + this.SortDescriptionList.CollectionChanged += this.SortDescriptionListOnCollectionChanged; + this.FilterDescriptionList.CollectionChanged += this.FilterDescriptionListOnCollectionChanged; + } + + public bool Contains(RemoteOrDbDataItem item) + { + return this._items.Contains(item); + } + + #region fields + + private readonly List _items = new List(); + private readonly List _orderedItems = new List(); + private bool _isFilteredItemsValid; + private string _orderByLinqExpression = ""; + private string _whereLinqExpression = ""; + + #endregion + + #region properties + + public IList FilteredOrderedItems + { + get + { + if (this._isFilteredItemsValid) return this._orderedItems; + + lock (this) + { + this._orderedItems.Clear(); + + try + { + if (string.IsNullOrWhiteSpace(this.WhereLinqExpression) && + string.IsNullOrWhiteSpace(this.OrderByLinqExpression)) + this._orderedItems.AddRange(this._items); + else if (!string.IsNullOrWhiteSpace(this.WhereLinqExpression) && + string.IsNullOrWhiteSpace(this.OrderByLinqExpression)) + this._orderedItems.AddRange(this._items.Where(this.WhereLinqExpression)); + else if (string.IsNullOrWhiteSpace(this.WhereLinqExpression) && + !string.IsNullOrWhiteSpace(this.OrderByLinqExpression)) + this._orderedItems.AddRange(this._items.OrderBy(this.OrderByLinqExpression)); + else if (!string.IsNullOrWhiteSpace(this.WhereLinqExpression) && + !string.IsNullOrWhiteSpace(this.OrderByLinqExpression)) + this._orderedItems.AddRange(this._items.Where(this.WhereLinqExpression) + .OrderBy(this.OrderByLinqExpression)); + } + catch + { + } + + this._isFilteredItemsValid = true; + } + + return this._orderedItems; + } + } + + public string OrderByLinqExpression + { + get => this._orderByLinqExpression; + set + { + if (!string.Equals(this._orderByLinqExpression, value)) + { + this._orderByLinqExpression = value; + this._isFilteredItemsValid = false; + } + } + } + + public string WhereLinqExpression + { + get => this._whereLinqExpression; + set + { + if (!string.Equals(this._whereLinqExpression, value)) + { + this._whereLinqExpression = value; + this._isFilteredItemsValid = false; + } + } + } + + #endregion + + #region public members + + public void OrderBy(string orderByExpression) + { + if (!string.Equals(orderByExpression, this.OrderByLinqExpression)) + this.OrderByLinqExpression = orderByExpression; + } + + public void Where(string whereExpression) + { + if (!string.Equals(whereExpression, this.WhereLinqExpression)) + this.WhereLinqExpression = whereExpression; + } + + #endregion + + #region filter & sort Descrioption list + + public SortDescriptionList SortDescriptionList { get; } = new SortDescriptionList(); + + public FilterDescriptionList FilterDescriptionList { get; } = new FilterDescriptionList(); + + private void SortDescriptionListOnCollectionChanged(object sender, + NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + var sort = ""; + + var sortFound = false; + foreach (var sortDescription in this.SortDescriptionList) + { + if (sortFound) + sort += ", "; + + sortFound = true; + + sort += sortDescription.PropertyName; + sort += sortDescription.Direction == ListSortDirection.Ascending ? " ASC" : " DESC"; + } + + //if ((!sortFound) && (!string.IsNullOrWhiteSpace( primaryKey ))) + // sort += primaryKey + " ASC"; + + this.OrderByLinqExpression = sort; + } + + private void FilterDescriptionListOnCollectionChanged(object sender, + NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Reset) + { + var filter = ""; + + var filterFound = false; + foreach (var filterDescription in this.FilterDescriptionList) + { + var subFilter = GetLinqQueryString(filterDescription); + if (!string.IsNullOrWhiteSpace(subFilter)) + { + if (filterFound) + filter += " and "; + filterFound = true; + filter += " " + subFilter + " "; + } + } + + this.WhereLinqExpression = filter; + } + } + + #region query builder + + private static readonly Regex _regexSplit = new Regex( + @"(and)|(or)|(==)|(<>)|(!=)|(<=)|(>=)|(&&)|(\|\|)|(=)|(>)|(<)|(\*[\-_a-zA-Z0-9]+)|([\-_a-zA-Z0-9]+\*)|([\-_a-zA-Z0-9]+)", + RegexOptions.IgnoreCase); + + private static readonly Regex _regexOp = + new Regex(@"(and)|(or)|(==)|(<>)|(!=)|(<=)|(>=)|(&&)|(\|\|)|(=)|(>)|(<)", RegexOptions.IgnoreCase); + + private static readonly Regex _regexComparOp = + new Regex(@"(==)|(<>)|(!=)|(<=)|(>=)|(=)|(>)|(<)", RegexOptions.None); + + private static string GetLinqQueryString(FilterDescription filterDescription) + { + var ret = ""; + + if (!string.IsNullOrWhiteSpace(filterDescription.Filter)) + try + { + // xceed syntax : empty (contains), AND (uppercase), OR (uppercase), <>, * (end with), =, >, >=, <, <=, * (start with) + // see http://doc.xceedsoft.com/products/XceedWpfDataGrid/Filter_Row.html + // linq.dynamic syntax : =, ==, <>, !=, <, >, <=, >=, &&, and, ||, or, x.m(…) (where x is the attrib and m the function (ex: Contains, StartsWith, EndsWith ...) + // see D:\DevC#\VirtualisingCollectionTest1\DynamicQuery\Dynamic Expressions.html + // ex : RemoteOrDbDataSourceEmulation.Instance.Items.Where( "Name.Contains(\"e_1\") or Name.Contains(\"e_2\")" ); + + var exp = filterDescription.Filter; + + // arrange expression + + var previousTermIsOperator = false; + foreach (Match match in _regexSplit.Matches(exp)) + if (match.Success) + if (_regexOp.IsMatch(match.Value)) + { + if (_regexComparOp.IsMatch(match.Value)) + { + // simple operator >, <, ==, != ... + ret += " " + filterDescription.PropertyName + " " + match.Value; + previousTermIsOperator = true; + } + else + { + // and, or ... + ret += " " + match.Value; + previousTermIsOperator = false; + } + } + else + { + // Value + if (previousTermIsOperator) + { + ret += " " + match.Value; + previousTermIsOperator = false; + } + else + { + if (match.Value.StartsWith("*")) + ret += " " + filterDescription.PropertyName + ".EndsWith( \"" + + match.Value.Substring(1) + "\" )"; + else if (match.Value.EndsWith("*")) + ret += " " + filterDescription.PropertyName + ".StartsWith( \"" + + match.Value.Substring(0, match.Value.Length - 1) + "\" )"; + else + ret += " " + filterDescription.PropertyName + ".Contains( \"" + match.Value + + "\" )"; + previousTermIsOperator = false; + } + } + } + catch (Exception) + { + } + + return ret; + } + + #endregion query builder + + #endregion filter & sort Descrioption list + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/converters/DataGridSortingEventArgsToMemberPathSortDirection.cs b/DataGridAsyncDemoMVVM/converters/DataGridSortingEventArgsToMemberPathSortDirection.cs new file mode 100644 index 0000000..cfb410f --- /dev/null +++ b/DataGridAsyncDemoMVVM/converters/DataGridSortingEventArgsToMemberPathSortDirection.cs @@ -0,0 +1,36 @@ +using System.ComponentModel; +using System.Windows.Controls; +using DataGridAsyncDemoMVVM.filtersort; +using GalaSoft.MvvmLight.Command; + +namespace DataGridAsyncDemoMVVM.converters +{ + public class DataGridSortingEventArgsToMemberPathSortDirection : IEventArgsConverter + { + public object Convert(object value, object parameter) + { + if (!(value is DataGridSortingEventArgs sortingEventArgs)) return null; + + var sortMemberPath = sortingEventArgs.Column.SortMemberPath; + var sortDirection = sortingEventArgs.Column.SortDirection; + + switch (sortDirection) + { + case null: + sortDirection = ListSortDirection.Ascending; + break; + case ListSortDirection.Ascending: + sortDirection = ListSortDirection.Descending; + break; + case ListSortDirection.Descending: + sortDirection = null; + break; + } + + sortingEventArgs.Column.SortDirection = sortDirection; + sortingEventArgs.Handled = true; + + return new MemberPathSortingDirection {MemberPath = sortMemberPath, SortDirection = sortDirection}; + } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/converters/TextChangedEventArgsToMemberPathAndText.cs b/DataGridAsyncDemoMVVM/converters/TextChangedEventArgsToMemberPathAndText.cs new file mode 100644 index 0000000..94ef675 --- /dev/null +++ b/DataGridAsyncDemoMVVM/converters/TextChangedEventArgsToMemberPathAndText.cs @@ -0,0 +1,27 @@ +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using DataGridAsyncDemoMVVM.filtersort; +using GalaSoft.MvvmLight.Command; + +namespace DataGridAsyncDemoMVVM.converters +{ + public class TextChangedEventArgsToMemberPathAndText : IEventArgsConverter + { + public object Convert(object value, object parameter) + { + if (value is TextChangedEventArgs eventArgs) + if (eventArgs.Source is TextBox textBox) + { + var dataGridColumnHeader = textBox.ParentOfType(); + if (dataGridColumnHeader != null) + { + var columnSortMemberPath = dataGridColumnHeader.Column.SortMemberPath; + var filterText = textBox.Text; + return new MemberPathFilterText {MemberPath = columnSortMemberPath, FilterText = filterText}; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/DescriptionList.cs b/DataGridAsyncDemoMVVM/filtersort/DescriptionList.cs new file mode 100644 index 0000000..2e735c9 --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/DescriptionList.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace DataGridAsyncDemoMVVM.filtersort +{ + public class DescriptionList : IEnumerable, IEnumerable, INotifyCollectionChanged + where T : IFilterOrderDescription + { + private readonly List _filterDescriptions = new List(); + + IEnumerator IEnumerable.GetEnumerator() + { + return this._filterDescriptions.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this._filterDescriptions.GetEnumerator(); + } + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + /// + /// If it exist, remove existing filter that apply on same property name. The add item arg at first position into + /// filter list. + /// + /// + public void Add(T item) + { + var index = this._filterDescriptions.FindIndex(description => + description.PropertyName.Equals(item.PropertyName, StringComparison.Ordinal)); + if (index >= 0) + { + var removed = this._filterDescriptions[index]; + this._filterDescriptions.RemoveAt(index); + //OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, removed, index ) ); + this._filterDescriptions.Insert(0, item); + //OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Add, item, 0 ) ); + + this.OnCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, removed, 0, index)); + } + else + { + this._filterDescriptions.Insert(0, item); + this.OnCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, 0)); + } + } + + /// + /// If it exist, remove existing filter that apply on same property name. The add item arg at first position into + /// filter list. + /// + /// + public void Remove(string propertyName) + { + var index = this._filterDescriptions.FindIndex(description => + description.PropertyName.Equals(propertyName, StringComparison.Ordinal)); + if (index >= 0) + { + var removed = this._filterDescriptions[index]; + this._filterDescriptions.RemoveAt(index); + this.OnCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed, index)); + } + } + + protected void OnCollectionChanged(NotifyCollectionChangedEventArgs arg) + { + var evnt = this.CollectionChanged; + + evnt?.Invoke(this, arg); + } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/FilterDescription.cs b/DataGridAsyncDemoMVVM/filtersort/FilterDescription.cs new file mode 100644 index 0000000..5957684 --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/FilterDescription.cs @@ -0,0 +1,14 @@ +namespace DataGridAsyncDemoMVVM.filtersort +{ + public class FilterDescription : IFilterOrderDescription + { + public FilterDescription(string propertyName, string filter) + { + this.PropertyName = propertyName; + this.Filter = filter; + } + + public string Filter { get; set; } + public string PropertyName { get; set; } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/FilterDescriptionList.cs b/DataGridAsyncDemoMVVM/filtersort/FilterDescriptionList.cs new file mode 100644 index 0000000..b2b0ff4 --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/FilterDescriptionList.cs @@ -0,0 +1,12 @@ +using System.Collections.Specialized; + +namespace DataGridAsyncDemoMVVM.filtersort +{ + public class FilterDescriptionList : DescriptionList + { + public void OnCollectionReset() + { + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/IFilterOrderDescription.cs b/DataGridAsyncDemoMVVM/filtersort/IFilterOrderDescription.cs new file mode 100644 index 0000000..6d91312 --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/IFilterOrderDescription.cs @@ -0,0 +1,7 @@ +namespace DataGridAsyncDemoMVVM.filtersort +{ + public interface IFilterOrderDescription + { + string PropertyName { get; set; } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/IFilteredSortedSourceProviderAsync.cs b/DataGridAsyncDemoMVVM/filtersort/IFilteredSortedSourceProviderAsync.cs new file mode 100644 index 0000000..0131f5e --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/IFilteredSortedSourceProviderAsync.cs @@ -0,0 +1,7 @@ +namespace DataGridAsyncDemoMVVM.filtersort +{ + public interface IFilteredSortedSourceProviderAsync + { + SortDescriptionList SortDescriptionList { get; } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/MemberPathFilterText.cs b/DataGridAsyncDemoMVVM/filtersort/MemberPathFilterText.cs new file mode 100644 index 0000000..bb0c15b --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/MemberPathFilterText.cs @@ -0,0 +1,8 @@ +namespace DataGridAsyncDemoMVVM.filtersort +{ + public class MemberPathFilterText + { + public string FilterText { get; set; } + public string MemberPath { get; set; } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/MemberPathSortingDirection.cs b/DataGridAsyncDemoMVVM/filtersort/MemberPathSortingDirection.cs new file mode 100644 index 0000000..6581366 --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/MemberPathSortingDirection.cs @@ -0,0 +1,10 @@ +using System.ComponentModel; + +namespace DataGridAsyncDemoMVVM.filtersort +{ + public class MemberPathSortingDirection + { + public string MemberPath { get; set; } + public ListSortDirection? SortDirection { get; set; } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/ParentOfTypeExtensions.cs b/DataGridAsyncDemoMVVM/filtersort/ParentOfTypeExtensions.cs new file mode 100644 index 0000000..a97234b --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/ParentOfTypeExtensions.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Windows; +using System.Windows.Media; + +namespace DataGridAsyncDemoMVVM.filtersort +{ + internal static class ArgumentVerificationExtensions + { + public static void TestNotEmptyString(this string parameter, string parameterName) + { + if (string.IsNullOrEmpty(parameter)) + throw new ArgumentException($@"The parameter '{parameterName}' should not be empty string.", + parameterName); + } + + public static void TestNotNull(this object parameter, string parameterName) + { + if (parameter == null) + throw new ArgumentNullException(parameterName); + } + } + + /// + /// Contains extension methods for enumerating the parents of an element. + /// + public static class ParentOfTypeExtensions + { + /// + /// This recurses the visual tree for ancestors of a specific type. + /// + public static IEnumerable GetAncestors(this DependencyObject element) where T : class + { + return element.GetParents().OfType(); + } + + /// + /// This recurses the visual tree for a parent of a specific type. + /// + public static T GetParent(this DependencyObject element) where T : FrameworkElement + { + return element.ParentOfType(); + } + + /// + /// Enumerates through element's parents in the visual tree. + /// + public static IEnumerable GetParents(this DependencyObject element) + { + if (element == null) + throw new ArgumentNullException(nameof(element)); + + while ((element = element.GetParent()) != null) + yield return element; + } + + /// + /// Searches up in the visual tree for parent element of the specified type. + /// + /// + /// The type of the parent that will be searched up in the visual object hierarchy. + /// The type should be . + /// + /// + /// The target which visual parents will be traversed. + /// + /// Visual parent of the specified type if there is any, otherwise null. + [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] + public static T GetVisualParent(this DependencyObject element) where T : DependencyObject + { + return element.ParentOfType(); + } + + + /// + /// Determines whether the element is an ancestor of the descendant. + /// + /// true if the visual object is an ancestor of descendant; otherwise, false. + public static bool IsAncestorOf(this DependencyObject element, DependencyObject descendant) + { + element.TestNotNull("element"); + descendant.TestNotNull("descendant"); + + return Equals(descendant, element) || descendant.GetParents().Contains(element); + } + + /// + /// Gets the parent element from the visual tree by given type. + /// + public static T ParentOfType(this DependencyObject element) where T : DependencyObject + { + return element?.GetParents().OfType().FirstOrDefault(); + } + + private static DependencyObject GetParent(this DependencyObject element) + { + var parent = VisualTreeHelper.GetParent(element); + if (parent == null) + if (element is FrameworkElement frameworkElement) + parent = frameworkElement.Parent; + return parent; + } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/SortDescription.cs b/DataGridAsyncDemoMVVM/filtersort/SortDescription.cs new file mode 100644 index 0000000..3d8374a --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/SortDescription.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; + +namespace DataGridAsyncDemoMVVM.filtersort +{ + public class SortDescription : IFilterOrderDescription + { + public SortDescription(string propertyName, ListSortDirection? direction) + { + this.Direction = direction; + this.PropertyName = propertyName; + } + + public ListSortDirection? Direction { get; set; } + public string PropertyName { get; set; } + } +} \ No newline at end of file diff --git a/DataGridAsyncDemoMVVM/filtersort/SortDescriptionList.cs b/DataGridAsyncDemoMVVM/filtersort/SortDescriptionList.cs new file mode 100644 index 0000000..32ed9be --- /dev/null +++ b/DataGridAsyncDemoMVVM/filtersort/SortDescriptionList.cs @@ -0,0 +1,6 @@ +namespace DataGridAsyncDemoMVVM.filtersort +{ + public class SortDescriptionList : DescriptionList + { + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c6c7e0a --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# VirtualizingObservableCollection +* Based on [AlphaChi Technology 2015](https://github.com/evgenekov/VirtualizingObservableCollection) +* Theory explained by [Virtualizing Data with XAML](https://alphachitech.wordpress.com/2015/01/31/virtualizing-observable-collection/) diff --git a/VirtualizingCollection.Observable/Interfaces/IObservableReactiveCollection.cs b/VirtualizingCollection.Observable/Interfaces/IObservableReactiveCollection.cs new file mode 100644 index 0000000..c5fd7f3 --- /dev/null +++ b/VirtualizingCollection.Observable/Interfaces/IObservableReactiveCollection.cs @@ -0,0 +1,10 @@ +using System; + +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IObservableReactiveCollection : IObservableCollection, IObservable + { + new int Count { get; } + new bool Remove(object item); + } +} \ No newline at end of file diff --git a/VirtualizingCollection.Observable/Interfaces/IReplaySubjectImplementation.cs b/VirtualizingCollection.Observable/Interfaces/IReplaySubjectImplementation.cs new file mode 100644 index 0000000..13e7e5a --- /dev/null +++ b/VirtualizingCollection.Observable/Interfaces/IReplaySubjectImplementation.cs @@ -0,0 +1,11 @@ +using System; + +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + internal interface IReplaySubjectImplementation : ISubject, ISubject, IObserver, IObservable, IDisposable + { + bool HasObservers { get; } + + void Unsubscribe(IObserver observer); + } +} \ No newline at end of file diff --git a/VirtualizingCollection.Observable/VirtualizingCollection.Observable.csproj b/VirtualizingCollection.Observable/VirtualizingCollection.Observable.csproj new file mode 100644 index 0000000..ed7ee82 --- /dev/null +++ b/VirtualizingCollection.Observable/VirtualizingCollection.Observable.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/VirtualizingCollection.Observable/VirtualizingObservableCollection.cs b/VirtualizingCollection.Observable/VirtualizingObservableCollection.cs new file mode 100644 index 0000000..62eb2f0 --- /dev/null +++ b/VirtualizingCollection.Observable/VirtualizingObservableCollection.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Threading; +using AlphaChiTech.Virtualization.Pageing; +using AlphaChiTech.VirtualizingCollection; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.Virtualization +{ + public class VirtualizingObservableReactiveCollection : VirtualizingObservableCollection, IReplaySubjectImplementation where T : class + { + #region Ctors Etc + /// + /// Initializes a new instance of the class. + /// + /// The provider. + public VirtualizingObservableReactiveCollection(IItemSourceProvider provider) : base(provider) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The asynchronous provider. + public VirtualizingObservableReactiveCollection(IItemSourceProviderAsync asyncProvider) : base(asyncProvider) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The provider. + /// The optional reclaimer. + /// The optional expiry comparer. + /// Size of the page. + /// The maximum pages. + /// The maximum deltas. + /// The maximum distance. + public VirtualizingObservableReactiveCollection( + IPagedSourceProvider provider, + IPageReclaimer reclaimer = null, + IPageExpiryComparer expiryComparer = null, + int pageSize = 100, + int maxPages = 100, + int maxDeltas = -1, + int maxDistance = -1) : base(provider,reclaimer, pageSize, maxPages, maxDeltas, maxDistance) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The provider. + /// The optional reclaimer. + /// The optional expiry comparer. + /// Size of the page. + /// The maximum pages. + /// The maximum deltas. + /// The maximum distance. + /// //TODO Check implementation + public VirtualizingObservableReactiveCollection( + IPagedSourceObservableProvider provider, + IPageReclaimer reclaimer = null, + IPageExpiryComparer expiryComparer = null, + int pageSize = 100, + int maxPages = 100, + int maxDeltas = -1, + int maxDistance = -1) : base(provider, reclaimer, pageSize, maxPages, maxDeltas, maxDistance) + { + (this.Provider as PaginationManager).CollectionChanged += this.VirtualizingObservableCollection_CollectionChanged; + this.IsSourceObservable = true; + + } + #endregion Ctors Etc + + private bool IsSourceObservable { get; } = false; + + + + #region Extended CRUD operators that take into account the DateTime of the change + + + /// + /// Adds the range. + /// + /// The new values. + /// The updatedat object. + /// Index of the last appended object + public int AddRange(IEnumerable newValues, object timestamp = null) + { + var edit = this.GetProviderAsEditable(); + + var index = -1; + var items = new List(); + + foreach(var item in newValues) + { + items.Add(item); + index = edit.OnAppend(item, timestamp); + + if(!this.IsSourceObservable) + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); + this.RaiseCollectionChangedEvent(args); + } + } + + + this.OnCountTouched(); + + + return index; + } + #endregion Extended CRUD operators that take into account the DateTime of the change + + + #region Internal implementation + + internal void ReplaceAt(int index, T oldValue, T newValue, object timestamp) + { + var edit = this.GetProviderAsEditable(); + + if(edit != null) + { + edit.OnReplace(index, oldValue, newValue, timestamp); + //TODO check this code + if (!this.IsSourceObservable) + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue, oldValue, index); + this.RaiseCollectionChangedEvent(args); + } + } + } + + private T InternalSetValue(int index, T newValue) + { + var oldValue = this.InternalGetValue(index, this.DefaultSelectionContext); + var edit = this.GetProviderAsEditable(); + edit.OnReplace(index, oldValue, newValue, null); + + var newItems = new List {newValue}; + var oldItems = new List {oldValue}; + + //TODO check + if(!this.IsSourceObservable) + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItems, oldItems, index); + this.RaiseCollectionChangedEvent(args); + } + + return oldValue; + } + + private int InternalAdd(T newValue, object timestamp) + { + var edit = this.GetProviderAsEditable(); + var index = edit.OnAppend(newValue, timestamp); + + if (!this.IsSourceObservable) + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newValue, index); + this.RaiseCollectionChangedEvent(args); + } + this.OnCountTouched(); + return index; + } + + + private void InternalInsertAt(int index, T item, object timestamp = null) + { + var edit = this.GetProviderAsEditable(); + edit.OnInsert(index, item, timestamp); + + this.OnCountTouched(); + if (!this.IsSourceObservable) + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); + this.RaiseCollectionChangedEvent(args); + } + + + } + + private bool InternalRemoveAt(int index, object timestamp = null) + { + var edit = this.Provider as IEditableProviderIndexBased; + if (edit == null) return false; + var oldValue = edit.OnRemove(index, timestamp); + + this.OnCountTouched(); + if (!this.IsSourceObservable) + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldValue, index); + this.RaiseCollectionChangedEvent(args); + } + + + return true; + } + + private bool InternalRemove(T item, object timestamp = null) + { + if (item == null) { return false; } + var edit = this.Provider as IEditableProviderItemBased; + if(edit == null) + return false; + var index = edit.OnRemove(item, timestamp); + + this.OnCountTouched(); + if (!this.IsSourceObservable) + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index); + this.RaiseCollectionChangedEvent(args); + } + + + return true; + } + + + #endregion Internal implementation + + //#region Implementation of IObservable + ///// + ///// Notifies the provider that an observer is to receive notifications. + ///// + ///// + ///// A reference to an interface that allows observers to stop receiving notifications before the provider has finished sending them. + ///// + ///// The object that is to receive notifications. + //public IDisposable Subscribe(IObserver observer) { throw new NotImplementedException(); } + //#endregion + + #region Implementation of IObservable + private readonly object _gate = new object(); + + private Exception _error; + private bool _isDisposed; + private bool _isStopped; + private ImmutableList> _observers = new ImmutableList>(); + + + public bool HasObservers + { + get + { + var immutableList = this._observers; + return immutableList?.Data.Length > 0; + } + } + + #region Implementation of IDisposable + public void Dispose() { this.Dispose(true); } + #endregion + + public IDisposable Subscribe(IObserver observer) + { + if (observer == null) { throw new ArgumentNullException(nameof(observer)); } + var subscription = new Subscription((IReplaySubjectImplementation)this, observer); + lock (this._gate) + { + this.CheckDisposed(); + this._observers = this._observers.Add(observer); + this.ReplayBuffer(observer); + if (this._error != null) { observer.OnError(this._error); } + else if (this._isStopped) { observer.OnCompleted(); } + } + return subscription; + } + + public void OnCompleted() + { + lock (this._gate) + { + this.CheckDisposed(); + if (this._isStopped) { return; } + this._isStopped = true; + foreach (var item_0 in this._observers.Data) { item_0.OnCompleted(); } + this._observers = new ImmutableList>(); + } + } + + public void OnError(Exception error) + { + if (error == null) { throw new ArgumentNullException("error"); } + lock (this._gate) + { + this.CheckDisposed(); + if (this._isStopped) { return; } + this._isStopped = true; + this._error = error; + foreach (var item_0 in this._observers.Data) { item_0.OnError(error); } + this._observers = new ImmutableList>(); + } + } + + public void OnNext(T value) + { + lock (this._gate) + { + this.CheckDisposed(); + if (this._isStopped) { return; } + foreach (var item_0 in this._observers.Data) { item_0.OnNext(value); } + } + } + + public void Dispose(bool disposing) + { + lock (this._gate) + { + this._isDisposed = true; + this._observers = null; + } + } + + public void Unsubscribe(IObserver observer) + { + lock (this._gate) + { + if (this._isDisposed) { return; } + this._observers = this._observers.Remove(observer); + } + } + + + protected void ReplayBuffer(IObserver observer) { foreach (var obj in this) { observer.OnNext(obj); } } + + + private void CheckDisposed() { if (this._isDisposed) { throw new ObjectDisposedException(string.Empty); } } + + + private class Subscription : IDisposable + { + private IObserver _observer; + private IReplaySubjectImplementation _subject; + + public Subscription(IReplaySubjectImplementation subject, IObserver observer) + { + this._subject = subject; + this._observer = observer; + } + + public void Dispose() + { + var observer = Interlocked.Exchange(ref this._observer, null); + if (observer == null) { return; } + this._subject.Unsubscribe(observer); + this._subject = null; + } + } + + private class ImmutableList + { + public ImmutableList() { this.Data = new TT[0]; } + + private ImmutableList(TT[] data) { this.Data = data; } + + public TT[] Data { get; } + + public ImmutableList Add(TT value) + { + var newData = new TT[this.Data.Length + 1]; + Array.Copy(this.Data, newData, this.Data.Length); + newData[this.Data.Length] = value; + return new ImmutableList(newData); + } + + private int IndexOf(TT value) + { + for (var i = 0; i < this.Data.Length; ++i) { if (this.Data[i].Equals(value)) { return i; } } + return -1; + } + + public ImmutableList Remove(TT value) + { + var i = this.IndexOf(value); + if (i < 0) { return this; } + var newData = new TT[this.Data.Length - 1]; + Array.Copy(this.Data, 0, newData, 0, i); + Array.Copy(this.Data, i + 1, newData, i, this.Data.Length - i - 1); + return new ImmutableList(newData); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/VirtualizingCollection.Tests/ItemSourceProvider.cs b/VirtualizingCollection.Tests/ItemSourceProvider.cs new file mode 100644 index 0000000..b5f652b --- /dev/null +++ b/VirtualizingCollection.Tests/ItemSourceProvider.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Linq; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace VirtualizingCollection.Tests +{ + public class ItemSourceProvider : IItemSourceProvider + { + private readonly IList _source; + + public ItemSourceProvider(IEnumerable source) + { + this._source = source.ToList(); + } + + public void OnReset(int count) + { + this._source.Clear(); + ; + } + + public bool Contains(T item) + { + return this._source.Contains(item); + } + + public T GetAt(int index, object voc, bool usePlaceholder) + { + return this._source[index]; + } + + public int GetCount(bool asyncOk) + { + return this._source.Count; + } + + public int IndexOf(T item) + { + return this._source.IndexOf(item); + } + + public bool IsSynchronized { get; } = false; + public object SyncRoot => this; + } +} \ No newline at end of file diff --git a/VirtualizingCollection.Tests/Properties/AssemblyInfo.cs b/VirtualizingCollection.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0c3b896 --- /dev/null +++ b/VirtualizingCollection.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("VirtualizingCollection.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("VirtualizingCollection.Tests")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("da20f632-94b1-48b6-a4bb-5262e3ec2973")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/VirtualizingCollection.Tests/TestItem.cs b/VirtualizingCollection.Tests/TestItem.cs new file mode 100644 index 0000000..f0faba9 --- /dev/null +++ b/VirtualizingCollection.Tests/TestItem.cs @@ -0,0 +1,10 @@ +using System.Threading; + +namespace VirtualizingCollection.Tests +{ + public class TestItem + { + private static int _root = -1; + public int Index { get; set; } = Interlocked.Increment(ref _root); + } +} \ No newline at end of file diff --git a/VirtualizingCollection.Tests/VirtualizingCollection.Tests.csproj b/VirtualizingCollection.Tests/VirtualizingCollection.Tests.csproj new file mode 100644 index 0000000..b554195 --- /dev/null +++ b/VirtualizingCollection.Tests/VirtualizingCollection.Tests.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {DA20F632-94B1-48B6-A4BB-5262E3EC2973} + Library + Properties + VirtualizingCollection.Tests + VirtualizingCollection.Tests + v4.7.1 + 512 + 1.0.0.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + {D8D58C1D-2596-45AD-83BD-E0D5C9FA7F69} + VirtualizingCollection + + + + + + + \ No newline at end of file diff --git a/VirtualizingCollection.Tests/VirtualizingObservableCollectionTest.cs b/VirtualizingCollection.Tests/VirtualizingObservableCollectionTest.cs new file mode 100644 index 0000000..1d467de --- /dev/null +++ b/VirtualizingCollection.Tests/VirtualizingObservableCollectionTest.cs @@ -0,0 +1,33 @@ +using System.Linq; +using AlphaChiTech.Virtualization; +using NUnit.Framework; + +namespace VirtualizingCollection.Tests +{ + [TestFixture] + public class VirtualizingObservableCollectionTest + { + private readonly VirtualizingObservableCollection _vc; + + public VirtualizingObservableCollectionTest() + { + this._vc = new VirtualizingObservableCollection( + new ItemSourceProvider(Enumerable.Range(0, 100).Select(i => new TestItem()))); + } + + [Test] + public void _Count_100() + { + Assert.AreEqual(100, this._vc.Count); + } + + [Test] + public void _GetEnumerator_() + { + for (int i = 0; i < 100; i++) + { + Assert.AreEqual(i, this._vc[i].Index); + } + } + } +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization.Net4/ActionVirtualizationWrapper.cs b/VirtualizingCollection/Actions/ActionVirtualizationWrapper.cs similarity index 67% rename from AlphaChiTech.Virtualization.Net4/ActionVirtualizationWrapper.cs rename to VirtualizingCollection/Actions/ActionVirtualizationWrapper.cs index 7189d0e..95a546f 100644 --- a/AlphaChiTech.Virtualization.Net4/ActionVirtualizationWrapper.cs +++ b/VirtualizingCollection/Actions/ActionVirtualizationWrapper.cs @@ -1,19 +1,16 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.Virtualization.Actions { /// - /// This is a VirtualAction that wraps an Action, optionally with a repeating schedule. + /// This is a VirtualAction that wraps an Action, optionally with a repeating schedule. /// public class ActionVirtualizationWrapper : BaseRepeatableActionVirtualization { - private Action _Action = null; + private readonly Action _action; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The action. /// The thread model. @@ -24,23 +21,21 @@ public ActionVirtualizationWrapper(Action action, bool isRepeating = false, TimeSpan? repeatingSchedule = null) : base(threadModel, isRepeating, repeatingSchedule) { - _Action = action; + this._action = action; } /// - /// Does the action. + /// Does the action. /// public override void DoAction() { - var a = _Action; - _LastRun = DateTime.Now; + var a = this._action; + this.LastRun = DateTime.Now; if (a != null) { a.Invoke(); } } - } - -} +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization.Net4/BaseActionVirtualization.cs b/VirtualizingCollection/Actions/BaseActionVirtualization.cs similarity index 59% rename from AlphaChiTech.Virtualization.Net4/BaseActionVirtualization.cs rename to VirtualizingCollection/Actions/BaseActionVirtualization.cs index 81d2e8f..9bd5bd6 100644 --- a/AlphaChiTech.Virtualization.Net4/BaseActionVirtualization.cs +++ b/VirtualizingCollection/Actions/BaseActionVirtualization.cs @@ -1,25 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using AlphaChiTech.VirtualizingCollection.Interfaces; -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.Virtualization.Actions { /// - /// Base Class that does an action on the dispatcher thread. Simply implement the DoAction method. + /// Base Class that does an action on the dispatcher thread. Simply implement the DoAction method. /// public abstract class BaseActionVirtualization : IVirtualizationAction { /// - /// Gets or sets the thread model. - /// - /// - /// The thread model. - /// - public VirtualActionThreadModelEnum ThreadModel { get; set; } - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The thread model. public BaseActionVirtualization(VirtualActionThreadModelEnum threadModel) @@ -28,5 +17,13 @@ public BaseActionVirtualization(VirtualActionThreadModelEnum threadModel) } public abstract void DoAction(); + + /// + /// Gets or sets the thread model. + /// + /// + /// The thread model. + /// + public VirtualActionThreadModelEnum ThreadModel { get; set; } } -} +} \ No newline at end of file diff --git a/VirtualizingCollection/Actions/BaseRepeatableActionVirtualization.cs b/VirtualizingCollection/Actions/BaseRepeatableActionVirtualization.cs new file mode 100644 index 0000000..6981d2b --- /dev/null +++ b/VirtualizingCollection/Actions/BaseRepeatableActionVirtualization.cs @@ -0,0 +1,71 @@ +using System; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.Virtualization.Actions +{ + /// + /// Base class there the Action repeats on a periodic basis (the RepeatingSchedule) like BaseActionVirtualization + /// simply implement the DoAction method. + /// + public abstract class BaseRepeatableActionVirtualization : BaseActionVirtualization, IRepeatingVirtualizationAction + { + protected DateTime LastRun = DateTime.MinValue; + + /// + /// Initializes a new instance of the class. + /// + /// The thread model. + /// if set to true [is repeating]. + /// The repeating schedule. + public BaseRepeatableActionVirtualization( + VirtualActionThreadModelEnum threadModel = VirtualActionThreadModelEnum.UseUIThread, + bool isRepeating = false, TimeSpan? repeatingSchedule = null) + : base(threadModel) + { + this.IsRepeating = isRepeating; + if (repeatingSchedule.HasValue) + { + this.RepeatingSchedule = repeatingSchedule.Value; + } + } + + /// + /// Gets or sets the repeating schedule. + /// + /// + /// The repeating schedule. + /// + public TimeSpan RepeatingSchedule { get; set; } = TimeSpan.FromSeconds(1); + + /// + /// Gets or sets a value indicating whether [is repeating]. + /// + /// + /// true if [is repeating]; otherwise, false. + /// + protected bool IsRepeating { get; set; } + + /// + /// Determines whether [is due to run]. + /// + /// + public virtual bool IsDueToRun() + { + if (DateTime.Now >= this.LastRun.Add(this.RepeatingSchedule)) + { + return true; + } + + return false; + } + + /// + /// check to see if the action should be kept. + /// + /// + public virtual bool KeepInActionsList() + { + return this.IsRepeating; + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Actions/ExecuteResetWA.cs b/VirtualizingCollection/Actions/ExecuteResetWA.cs new file mode 100644 index 0000000..a5069dc --- /dev/null +++ b/VirtualizingCollection/Actions/ExecuteResetWA.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Specialized; + +namespace AlphaChiTech.Virtualization.Actions +{ + public class ExecuteResetWA : BaseActionVirtualization where T : class + { + readonly WeakReference _voc; + + public ExecuteResetWA(VirtualizingObservableCollection voc) + : base(VirtualActionThreadModelEnum.UseUIThread) + { + this._voc = new WeakReference(voc); + } + + public override void DoAction() + { + var voc = (VirtualizingObservableCollection) this._voc.Target; + + if (voc != null && this._voc.IsAlive) + { + voc.RaiseCollectionChangedEvent( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Actions/PlaceholderReplaceWA.cs b/VirtualizingCollection/Actions/PlaceholderReplaceWA.cs new file mode 100644 index 0000000..7a94257 --- /dev/null +++ b/VirtualizingCollection/Actions/PlaceholderReplaceWA.cs @@ -0,0 +1,32 @@ +using System; + +namespace AlphaChiTech.Virtualization.Actions +{ + public class PlaceholderReplaceWA : BaseActionVirtualization where T : class + { + private readonly int _index; + private readonly T _newValue; + private readonly T _oldValue; + + readonly WeakReference _voc; + + public PlaceholderReplaceWA(VirtualizingObservableCollection voc, T oldValue, T newValue, int index) + : base(VirtualActionThreadModelEnum.UseUIThread) + { + this._voc = new WeakReference(voc); + this._oldValue = oldValue; + this._newValue = newValue; + this._index = index; + } + + public override void DoAction() + { + var voc = (VirtualizingObservableCollection) this._voc.Target; + + if (voc != null && this._voc.IsAlive) + { + voc.ReplaceAt(this._index, this._oldValue, this._newValue, null); + } + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Actions/ReclaimPagesWA.cs b/VirtualizingCollection/Actions/ReclaimPagesWA.cs new file mode 100644 index 0000000..ff7b0a8 --- /dev/null +++ b/VirtualizingCollection/Actions/ReclaimPagesWA.cs @@ -0,0 +1,39 @@ +using System; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.Virtualization.Actions +{ + public class ReclaimPagesWA : BaseRepeatableActionVirtualization + { + readonly string _sectionContext = ""; + + readonly WeakReference _wrProvider; + + public ReclaimPagesWA(IReclaimableService provider, string sectionContext) + : base(VirtualActionThreadModelEnum.Background, true, TimeSpan.FromMinutes(1)) + { + this._wrProvider = new WeakReference(provider); + } + + public override void DoAction() + { + this.LastRun = DateTime.Now; + + var reclaimer = this._wrProvider.Target as IReclaimableService; + + if (reclaimer != null) + { + reclaimer.RunClaim(this._sectionContext); + } + } + + public override bool KeepInActionsList() + { + var ret = base.KeepInActionsList(); + + if (!this._wrProvider.IsAlive) ret = false; + + return ret; + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Actions/VirtualActionThreadModelEnum.cs b/VirtualizingCollection/Actions/VirtualActionThreadModelEnum.cs new file mode 100644 index 0000000..5321fbf --- /dev/null +++ b/VirtualizingCollection/Actions/VirtualActionThreadModelEnum.cs @@ -0,0 +1,8 @@ +namespace AlphaChiTech.Virtualization.Actions +{ + public enum VirtualActionThreadModelEnum + { + UseUIThread, + Background + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/CountChangedEventArgs.cs b/VirtualizingCollection/Interfaces/CountChangedEventArgs.cs new file mode 100644 index 0000000..38e215e --- /dev/null +++ b/VirtualizingCollection/Interfaces/CountChangedEventArgs.cs @@ -0,0 +1,10 @@ +using System; + +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public class CountChangedEventArgs : EventArgs + { + public int Count { get; set; } + public bool NeedsReset { get; set; } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IBaseSourceProvider.cs b/VirtualizingCollection/Interfaces/IBaseSourceProvider.cs new file mode 100644 index 0000000..b085240 --- /dev/null +++ b/VirtualizingCollection/Interfaces/IBaseSourceProvider.cs @@ -0,0 +1,7 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IBaseSourceProvider : ISynchronized + { + void OnReset(int count); + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IEditableProvider.cs b/VirtualizingCollection/Interfaces/IEditableProvider.cs new file mode 100644 index 0000000..6e7fc73 --- /dev/null +++ b/VirtualizingCollection/Interfaces/IEditableProvider.cs @@ -0,0 +1,9 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IEditableProvider + { + int OnAppend(T item, object timestamp); + void OnInsert(int index, T item, object timestamp); + void OnReplace(int index, T oldItem, T newItem, object timestamp); + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IEditableProviderIndexBased.cs b/VirtualizingCollection/Interfaces/IEditableProviderIndexBased.cs new file mode 100644 index 0000000..d90ffce --- /dev/null +++ b/VirtualizingCollection/Interfaces/IEditableProviderIndexBased.cs @@ -0,0 +1,8 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IEditableProviderIndexBased : IEditableProvider + { + T OnRemove(int index, object timestamp); + T OnReplace(int index, T newItem, object timestamp); + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IEditableProviderItemBased.cs b/VirtualizingCollection/Interfaces/IEditableProviderItemBased.cs new file mode 100644 index 0000000..0d80df9 --- /dev/null +++ b/VirtualizingCollection/Interfaces/IEditableProviderItemBased.cs @@ -0,0 +1,8 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IEditableProviderItemBased : IEditableProvider + { + int OnRemove(T item, object timestamp); + int OnReplace(T oldItem, T newItem, object timestamp); + } +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization/IItemSourceProvider.cs b/VirtualizingCollection/Interfaces/IItemSourceProvider.cs similarity index 50% rename from AlphaChiTech.Virtualization/IItemSourceProvider.cs rename to VirtualizingCollection/Interfaces/IItemSourceProvider.cs index a57b416..d966507 100644 --- a/AlphaChiTech.Virtualization/IItemSourceProvider.cs +++ b/VirtualizingCollection/Interfaces/IItemSourceProvider.cs @@ -1,15 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.VirtualizingCollection.Interfaces { - public interface IItemSourceProvider : IBaseSourceProvider + public interface IItemSourceProvider : IBaseSourceProvider { + bool Contains(T item); T GetAt(int index, object voc, bool usePlaceholder); - int GetCount(bool asyncOK); + int GetCount(bool asyncOk); int IndexOf(T item); } -} +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization/IItemSourceProviderAsync.cs b/VirtualizingCollection/Interfaces/IItemSourceProviderAsync.cs similarity index 60% rename from AlphaChiTech.Virtualization/IItemSourceProviderAsync.cs rename to VirtualizingCollection/Interfaces/IItemSourceProviderAsync.cs index c9358eb..69daa07 100644 --- a/AlphaChiTech.Virtualization/IItemSourceProviderAsync.cs +++ b/VirtualizingCollection/Interfaces/IItemSourceProviderAsync.cs @@ -1,19 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.VirtualizingCollection.Interfaces { - public interface IItemSourceProviderAsync : IBaseSourceProvider + public interface IItemSourceProviderAsync : IBaseSourceProvider { + Task Count { get; } Task GetAt(int index, object voc, bool usePlaceholder); T GetPlaceHolder(int index); - Task Count { get; } - Task IndexOf(T item); } -} +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/INotifyCountChanged.cs b/VirtualizingCollection/Interfaces/INotifyCountChanged.cs new file mode 100644 index 0000000..cffffdc --- /dev/null +++ b/VirtualizingCollection/Interfaces/INotifyCountChanged.cs @@ -0,0 +1,7 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface INotifyCountChanged + { + event OnCountChanged CountChanged; + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/INotifyImmediately.cs b/VirtualizingCollection/Interfaces/INotifyImmediately.cs new file mode 100644 index 0000000..4805146 --- /dev/null +++ b/VirtualizingCollection/Interfaces/INotifyImmediately.cs @@ -0,0 +1,7 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface INotifyImmediately + { + bool IsNotifyImmidiately { get; set; } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IObservableCollection.cs b/VirtualizingCollection/Interfaces/IObservableCollection.cs new file mode 100644 index 0000000..24b51c7 --- /dev/null +++ b/VirtualizingCollection/Interfaces/IObservableCollection.cs @@ -0,0 +1,19 @@ +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IObservableCollection : INotifyCollectionChanged, ICollection, IObservableCollection + { + new int Count { get; } + new bool Remove(object item); + } + + public interface IObservableCollection : ICollection, INotifyCollectionChanged + { + void Add(object item); + void Clear(); + bool Remove(object item); + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IPageExpiryComparer.cs b/VirtualizingCollection/Interfaces/IPageExpiryComparer.cs new file mode 100644 index 0000000..f41e94d --- /dev/null +++ b/VirtualizingCollection/Interfaces/IPageExpiryComparer.cs @@ -0,0 +1,7 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IPageExpiryComparer + { + bool IsUpdateValid(object pageUpdateAt, object updateAt); + } +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization/IPageReclaimer.cs b/VirtualizingCollection/Interfaces/IPageReclaimer.cs similarity index 68% rename from AlphaChiTech.Virtualization/IPageReclaimer.cs rename to VirtualizingCollection/Interfaces/IPageReclaimer.cs index 9df1099..8716705 100644 --- a/AlphaChiTech.Virtualization/IPageReclaimer.cs +++ b/VirtualizingCollection/Interfaces/IPageReclaimer.cs @@ -1,38 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.VirtualizingCollection.Interfaces { public interface IPageReclaimer { /// - /// Reclaims the pages. + /// Makes the page. /// - /// The pages. - /// The pages needed. + /// The page. + /// The size. /// - IEnumerable> ReclaimPages(IEnumerable> pages, int pagesNeeded, string sectionContext); + ISourcePage MakePage(int page, int size); /// - /// Called when [page touched]. + /// Called when [page released]. /// /// The page. - void OnPageTouched(ISourcePage page); + void OnPageReleased(ISourcePage page); /// - /// Called when [page released]. + /// Called when [page touched]. /// /// The page. - void OnPageReleased(ISourcePage page); + void OnPageTouched(ISourcePage page); /// - /// Makes the page. + /// Reclaims the pages. /// - /// The page. - /// The size. + /// The pages. + /// The pages needed. + /// /// - ISourcePage MakePage(int page, int size); + IEnumerable> ReclaimPages(IEnumerable> pages, int pagesNeeded, + string sectionContext); } -} +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IPagedSourceObservableProvider.cs b/VirtualizingCollection/Interfaces/IPagedSourceObservableProvider.cs new file mode 100644 index 0000000..258809a --- /dev/null +++ b/VirtualizingCollection/Interfaces/IPagedSourceObservableProvider.cs @@ -0,0 +1,9 @@ +using System.Collections.Specialized; + +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IPagedSourceObservableProvider : IPagedSourceProvider, INotifyCollectionChanged, + IEditableProvider + { + } +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization/IPagedSourceProvider.cs b/VirtualizingCollection/Interfaces/IPagedSourceProvider.cs similarity index 61% rename from AlphaChiTech.Virtualization/IPagedSourceProvider.cs rename to VirtualizingCollection/Interfaces/IPagedSourceProvider.cs index 722d037..0506af8 100644 --- a/AlphaChiTech.Virtualization/IPagedSourceProvider.cs +++ b/VirtualizingCollection/Interfaces/IPagedSourceProvider.cs @@ -1,16 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using AlphaChiTech.Virtualization.Pageing; -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.VirtualizingCollection.Interfaces { - public interface IPagedSourceProvider : IBaseSourceProvider + public interface IPagedSourceProvider : IBaseSourceProvider { - PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder); - int Count { get; } + bool Contains(T item); + PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder); int IndexOf(T item); } -} +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IPagedSourceProviderAsync.cs b/VirtualizingCollection/Interfaces/IPagedSourceProviderAsync.cs new file mode 100644 index 0000000..81b9dd5 --- /dev/null +++ b/VirtualizingCollection/Interfaces/IPagedSourceProviderAsync.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using AlphaChiTech.Virtualization.Pageing; + +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IPagedSourceProviderAsync : IPagedSourceProvider + { + Task ContainsAsync(T item); + + Task GetCountAsync(); + Task> GetItemsAtAsync(int pageoffset, int count, bool usePlaceholder); + + T GetPlaceHolder(int index, int page, int offset); + + Task IndexOfAsync(T item); + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IReclaimableService.cs b/VirtualizingCollection/Interfaces/IReclaimableService.cs new file mode 100644 index 0000000..f32c83c --- /dev/null +++ b/VirtualizingCollection/Interfaces/IReclaimableService.cs @@ -0,0 +1,7 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IReclaimableService + { + void RunClaim(string sectionContext); + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/IRepeatingVirtualizationAction.cs b/VirtualizingCollection/Interfaces/IRepeatingVirtualizationAction.cs new file mode 100644 index 0000000..47f5b66 --- /dev/null +++ b/VirtualizingCollection/Interfaces/IRepeatingVirtualizationAction.cs @@ -0,0 +1,8 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface IRepeatingVirtualizationAction + { + bool IsDueToRun(); + bool KeepInActionsList(); + } +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization.Net4/ISourcePage.cs b/VirtualizingCollection/Interfaces/ISourcePage.cs similarity index 56% rename from AlphaChiTech.Virtualization.Net4/ISourcePage.cs rename to VirtualizingCollection/Interfaces/ISourcePage.cs index d78c6a4..6e05acf 100644 --- a/AlphaChiTech.Virtualization.Net4/ISourcePage.cs +++ b/VirtualizingCollection/Interfaces/ISourcePage.cs @@ -1,47 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Text; +using System.Collections.Generic; +using AlphaChiTech.Virtualization.Pageing; -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.VirtualizingCollection.Interfaces { - public class SourcePagePendingUpdates - { - public INotifyCollectionChanged Args { get; set; } - public Object UpdatedAt { get; set; } - } - public interface ISourcePage { - int Page { get; set; } + bool CanReclaimPage { get; } + + int ItemsCount { get; } int ItemsPerPage { get; set; } - bool CanReclaimPage { get; } + object LastTouch { get; set; } + int Page { get; set; } - Object LastTouch { get; set; } + PageFetchStateEnum PageFetchState { get; set; } - T GetAt(int offset); + List PendingUpdates { get; } + + object WiredDateTime { get; set; } int Append(T item, object updatedAt, IPageExpiryComparer comparer); + T GetAt(int offset); + int IndexOf(T item); void InsertAt(int offset, T item, object updatedAt, IPageExpiryComparer comparer); bool RemoveAt(int offset, object updatedAt, IPageExpiryComparer comparer); - void ReplaceAt(int offset, T oldItem, T newItem, object updatedAt, IPageExpiryComparer comparer); - - PageFetchStateEnum PageFetchState { get; set; } + T ReplaceAt(int offset, T newValue, object updatedAt, IPageExpiryComparer comparer); - Object WiredDateTime { get; set; } + T ReplaceAt(T oldValue, T newValue, object updatedAt, IPageExpiryComparer comparer); bool ReplaceNeeded(int offset); - - List PendingUpdates { get; } - - int ItemsCount { get; } } -} +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/ISynchronized.cs b/VirtualizingCollection/Interfaces/ISynchronized.cs new file mode 100644 index 0000000..51f2ba0 --- /dev/null +++ b/VirtualizingCollection/Interfaces/ISynchronized.cs @@ -0,0 +1,8 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public interface ISynchronized + { + bool IsSynchronized { get; } + object SyncRoot { get; } + } +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization/IVirtualizationAction.cs b/VirtualizingCollection/Interfaces/IVirtualizationAction.cs similarity index 52% rename from AlphaChiTech.Virtualization/IVirtualizationAction.cs rename to VirtualizingCollection/Interfaces/IVirtualizationAction.cs index 3443d49..0c94a5a 100644 --- a/AlphaChiTech.Virtualization/IVirtualizationAction.cs +++ b/VirtualizingCollection/Interfaces/IVirtualizationAction.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using AlphaChiTech.Virtualization.Actions; -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.VirtualizingCollection.Interfaces { public interface IVirtualizationAction { @@ -11,4 +8,4 @@ public interface IVirtualizationAction void DoAction(); } -} +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/OnCountChanged.cs b/VirtualizingCollection/Interfaces/OnCountChanged.cs new file mode 100644 index 0000000..a2f4386 --- /dev/null +++ b/VirtualizingCollection/Interfaces/OnCountChanged.cs @@ -0,0 +1,4 @@ +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public delegate void OnCountChanged(object sender, CountChangedEventArgs args); +} \ No newline at end of file diff --git a/VirtualizingCollection/Interfaces/SourcePagePendingUpdates.cs b/VirtualizingCollection/Interfaces/SourcePagePendingUpdates.cs new file mode 100644 index 0000000..40dce45 --- /dev/null +++ b/VirtualizingCollection/Interfaces/SourcePagePendingUpdates.cs @@ -0,0 +1,10 @@ +using System.Collections.Specialized; + +namespace AlphaChiTech.VirtualizingCollection.Interfaces +{ + public class SourcePagePendingUpdates + { + public INotifyCollectionChanged Args { get; set; } + public object UpdatedAt { get; set; } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/BasePagedSourceProvider.cs b/VirtualizingCollection/Pageing/BasePagedSourceProvider.cs new file mode 100644 index 0000000..d0d5d71 --- /dev/null +++ b/VirtualizingCollection/Pageing/BasePagedSourceProvider.cs @@ -0,0 +1,110 @@ +using System; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.Virtualization.Pageing +{ + public class BasePagedSourceProvider : IPagedSourceProvider + { + public BasePagedSourceProvider() + { + } + + public BasePagedSourceProvider( + Func> funcGetItemsAt = null, + Func funcGetCount = null, + Func funcIndexOf = null, + Func funcContains = null, + Action actionOnReset = null + ) + { + this.FuncGetItemsAt = funcGetItemsAt; + this.FuncGetCount = funcGetCount; + this.FuncIndexOf = funcIndexOf; + this.FuncContains = funcContains; + this.ActionOnReset = actionOnReset; + } + + public Action ActionOnReset { get; set; } + public Func FuncContains { get; set; } + + public Func FuncGetCount { get; set; } + + public Func> FuncGetItemsAt { get; set; } + + public Func FuncIndexOf { get; set; } + + public virtual void OnReset(int count) + { + this.ActionOnReset?.Invoke(count); + } + + public bool Contains(T item) + { + return this.FuncContains?.Invoke(item) ?? false; + } + + public virtual int Count + { + get + { + var ret = 0; + + if (this.FuncGetCount != null) ret = this.FuncGetCount.Invoke(); + + return ret; + } + } + + public virtual PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder) + { + return this.FuncGetItemsAt?.Invoke(pageoffset, count); + } + + public virtual int IndexOf(T item) + { + return this.FuncIndexOf?.Invoke(item) ?? -1; + } + + /// + /// Gets a value indicating whether access to the is synchronized + /// (thread safe). + /// + /// + /// true if access to the is synchronized (thread safe); otherwise, + /// false. + /// + public bool IsSynchronized { get; } = false; + + /// + /// Gets an object that can be used to synchronize access to the . + /// + /// + /// An object that can be used to synchronize access to the . + /// + public object SyncRoot => this; + + /// + /// Copies the elements of the to an , + /// starting at a particular index. + /// + /// + /// The one-dimensional that is the destination of the elements copied + /// from . The must have zero-based + /// indexing. + /// + /// The zero-based index in at which copying begins. + /// is null. + /// is less than zero. + /// + /// is multidimensional.-or- The number of elements + /// in the source is greater than the available space from + /// to the end of the destination .-or-The type of the source + /// cannot be cast automatically to the type of the destination + /// . + /// + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/DateBasedPageExpiryComparer.cs b/VirtualizingCollection/Pageing/DateBasedPageExpiryComparer.cs new file mode 100644 index 0000000..ccadfa5 --- /dev/null +++ b/VirtualizingCollection/Pageing/DateBasedPageExpiryComparer.cs @@ -0,0 +1,33 @@ +using System; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.Virtualization.Pageing +{ + /// + /// An implementation of a IPageExiryComparer that uses DateTime to see if the update should be applied + /// + public class DateBasedPageExpiryComparer : IPageExpiryComparer + { + /// + /// Gets the default instance. + /// + /// + /// The default instance. + /// + public static DateBasedPageExpiryComparer DefaultInstance { get; } = new DateBasedPageExpiryComparer(); + + /// + /// Determines whether [is update valid] [the specified page based on the updateAt]. + /// + /// The page update at - null or a DateTime. + /// The update at - null or a DateTime. + /// + public bool IsUpdateValid(object pageUpdateAt, object updateAt) + { + if (pageUpdateAt == null || updateAt == null || !(pageUpdateAt is DateTime) || !(updateAt is DateTime)) + return true; + + return ((DateTime) pageUpdateAt) < ((DateTime) updateAt); + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/IAsyncResetProvider.cs b/VirtualizingCollection/Pageing/IAsyncResetProvider.cs new file mode 100644 index 0000000..070169e --- /dev/null +++ b/VirtualizingCollection/Pageing/IAsyncResetProvider.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace AlphaChiTech.Virtualization.Pageing +{ + public interface IAsyncResetProvider + { + Task GetCountAsync(); + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/IProviderPreReset.cs b/VirtualizingCollection/Pageing/IProviderPreReset.cs new file mode 100644 index 0000000..88dbbc4 --- /dev/null +++ b/VirtualizingCollection/Pageing/IProviderPreReset.cs @@ -0,0 +1,7 @@ +namespace AlphaChiTech.Virtualization.Pageing +{ + public interface IProviderPreReset + { + void OnBeforeReset(); + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/PageDelta.cs b/VirtualizingCollection/Pageing/PageDelta.cs new file mode 100644 index 0000000..891375a --- /dev/null +++ b/VirtualizingCollection/Pageing/PageDelta.cs @@ -0,0 +1,23 @@ +using System; + +namespace AlphaChiTech.Virtualization.Pageing +{ + internal class PageDelta + { + private int _page; + + public int Delta { get; set; } + + public int Page + { + get => this._page; + set + { + if (value < 0) + throw new ArgumentOutOfRangeException( + $"Page number value for PageDelta must be >= 0, but {value} was provided."); + this._page = value; + } + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/PageFetchStateEnum.cs b/VirtualizingCollection/Pageing/PageFetchStateEnum.cs new file mode 100644 index 0000000..e810e69 --- /dev/null +++ b/VirtualizingCollection/Pageing/PageFetchStateEnum.cs @@ -0,0 +1,8 @@ +namespace AlphaChiTech.Virtualization.Pageing +{ + public enum PageFetchStateEnum + { + Fetched, + Placeholders + } +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization.Net4/PageReclaimOnTouched.cs b/VirtualizingCollection/Pageing/PageReclaimOnTouched.cs similarity index 60% rename from AlphaChiTech.Virtualization.Net4/PageReclaimOnTouched.cs rename to VirtualizingCollection/Pageing/PageReclaimOnTouched.cs index 75f92df..b41639d 100644 --- a/AlphaChiTech.Virtualization.Net4/PageReclaimOnTouched.cs +++ b/VirtualizingCollection/Pageing/PageReclaimOnTouched.cs @@ -1,64 +1,60 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; +using AlphaChiTech.VirtualizingCollection.Interfaces; -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.Virtualization.Pageing { /// - /// PageReclainOnTouched is a Page Reclaimer implementation that releases pages based on when - /// they where last touched. + /// PageReclainOnTouched is a Page Reclaimer implementation that releases pages based on when + /// they where last touched. /// /// public class PageReclaimOnTouched : IPageReclaimer { - + /// /// - /// Reclaims the pages. + /// Makes the page. /// - /// The pages. - /// The pages needed. - /// The section context. + /// The page. + /// The size. /// - public IEnumerable> ReclaimPages(IEnumerable> pages, int pagesNeeded, string sectionContext) + public ISourcePage MakePage(int page, int size) { - List> ret = new List>(); - - var candiadates = (from p in pages where p.CanReclaimPage == true orderby p.LastTouch select p).Take(pagesNeeded); - - foreach (var c in candiadates) ret.Add(c); - - return ret; + return new SourcePage {Page = page, ItemsPerPage = size}; } + /// /// - /// Called when [page touched]. + /// Called when [page released]. /// /// The page. - public void OnPageTouched(ISourcePage page) + public void OnPageReleased(ISourcePage page) { - page.LastTouch = DateTime.Now; } + /// /// - /// Called when [page released]. + /// Called when [page touched]. /// /// The page. - public void OnPageReleased(ISourcePage page) + public void OnPageTouched(ISourcePage page) { + page.LastTouch = DateTime.Now; } + /// /// - /// Makes the page. + /// Reclaims the pages. /// - /// The page. - /// The size. + /// The pages. + /// The pages needed. + /// The section context. /// - public ISourcePage MakePage(int page, int size) + public IEnumerable> ReclaimPages(IEnumerable> pages, int pagesNeeded, + string sectionContext) { - return new SourcePage() { Page = page, ItemsPerPage = size }; + return (from p in pages where p.CanReclaimPage orderby p.LastTouch select p).Take(pagesNeeded).ToList(); } - } - -} +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/PagedSourceItemsPacket.cs b/VirtualizingCollection/Pageing/PagedSourceItemsPacket.cs new file mode 100644 index 0000000..05d198a --- /dev/null +++ b/VirtualizingCollection/Pageing/PagedSourceItemsPacket.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace AlphaChiTech.Virtualization.Pageing +{ + public class PagedSourceItemsPacket + { + public IEnumerable Items { get; set; } + + public object LoadedAt { get; set; } = DateTime.Now; + } +} \ No newline at end of file diff --git a/AlphaChiTech.Virtualization/PagedSourceProviderMakeAsync.cs b/VirtualizingCollection/Pageing/PagedSourceProviderMakeAsync.cs similarity index 51% rename from AlphaChiTech.Virtualization/PagedSourceProviderMakeAsync.cs rename to VirtualizingCollection/Pageing/PagedSourceProviderMakeAsync.cs index c178c23..02bc1d4 100644 --- a/AlphaChiTech.Virtualization/PagedSourceProviderMakeAsync.cs +++ b/VirtualizingCollection/Pageing/PagedSourceProviderMakeAsync.cs @@ -1,17 +1,13 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; +using AlphaChiTech.VirtualizingCollection.Interfaces; -namespace AlphaChiTech.Virtualization +namespace AlphaChiTech.Virtualization.Pageing { - public class PagedSourceProviderMakeAsync : BasePagedSourceProvider, IPagedSourceProviderAsync, IProviderPreReset + public class PagedSourceProviderMakeAsync : BasePagedSourceProvider, IPagedSourceProviderAsync, + IProviderPreReset { - private Func> _FuncIndexOfAsync = null; - private Func _FuncGetPlaceHolder = null; - - public PagedSourceProviderMakeAsync() + public PagedSourceProviderMakeAsync() { } @@ -19,44 +15,45 @@ public PagedSourceProviderMakeAsync( Func> funcGetItemsAt = null, Func funcGetCount = null, Func> funcIndexOfAsync = null, + Func funcContains = null, + Func> funcContainsAsync = null, Action actionOnReset = null, Func funcGetPlaceHolder = null, Action actionOnBeforeReset = null - ) - : base(funcGetItemsAt, funcGetCount, null, actionOnReset) - //: base(funcGetItemsAt, funcGetCount, funcIndexOf, actionOnReset) + ) : base(funcGetItemsAt, funcGetCount, null, funcContains, actionOnReset) { this.FuncGetPlaceHolder = funcGetPlaceHolder; this.ActionOnBeforeReset = actionOnBeforeReset; - _FuncIndexOfAsync = funcIndexOfAsync; + this.FuncIndexOfAsync = funcIndexOfAsync; + this.FuncContainsAsync = funcContainsAsync; } - public virtual void OnBeforeReset() - { - if (this.ActionOnBeforeReset != null) - { - this.ActionOnBeforeReset.Invoke(); - } - } + public Action ActionOnBeforeReset { get; set; } + public Func> FuncContainsAsync { get; set; } - Action _ActionOnBeforeReset = null; + public Func FuncGetPlaceHolder { get; set; } - public Action ActionOnBeforeReset - { - get { return _ActionOnBeforeReset; } - set { _ActionOnBeforeReset = value; } - } + public Func> FuncIndexOfAsync { get; set; } - public Func FuncGetPlaceHolder + public Task ContainsAsync(T item) { - get { return _FuncGetPlaceHolder; } - set { _FuncGetPlaceHolder = value; } + return this.FuncContainsAsync?.Invoke(item) ?? default(Task); } - public Func> FuncIndexOfAsync + public Task GetCountAsync() { - get { return _FuncIndexOfAsync; } - set { _FuncIndexOfAsync = value; } + var tcs = new TaskCompletionSource(); + + try + { + tcs.SetResult(this.Count); + } + catch (Exception e) + { + tcs.SetException(e); + } + + return tcs.Task; } public Task> GetItemsAtAsync(int pageoffset, int count, bool usePlaceholder) @@ -77,37 +74,22 @@ public Task> GetItemsAtAsync(int pageoffset, int count public virtual T GetPlaceHolder(int index, int page, int offset) { - T ret = default(T); + var ret = default(T); - if (_FuncGetPlaceHolder != null) - ret = _FuncGetPlaceHolder.Invoke(index, page, offset); + if (this.FuncGetPlaceHolder != null) + ret = this.FuncGetPlaceHolder.Invoke(index, page, offset); return ret; } - public Task GetCountAsync() + public Task IndexOfAsync(T item) { - var tcs = new TaskCompletionSource(); - - try - { - tcs.SetResult(this.Count); - } catch(Exception e) - { - tcs.SetException(e); - } - - return tcs.Task; + return this.FuncIndexOfAsync?.Invoke(item) ?? default(Task); } - public Task IndexOfAsync( T item ) - { - var ret = default( Task ); - - if (_FuncIndexOfAsync != null) - ret = _FuncIndexOfAsync.Invoke( item ); - - return ret; - } + public virtual void OnBeforeReset() + { + this.ActionOnBeforeReset?.Invoke(); + } } -} +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/PagedSourceProviderMakeSync.cs b/VirtualizingCollection/Pageing/PagedSourceProviderMakeSync.cs new file mode 100644 index 0000000..32ef909 --- /dev/null +++ b/VirtualizingCollection/Pageing/PagedSourceProviderMakeSync.cs @@ -0,0 +1,171 @@ +using System; +using System.Threading.Tasks; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.Virtualization.Pageing +{ + public class PagedSourceProviderMakeSync : IPagedSourceProviderAsync, IProviderPreReset + { + public PagedSourceProviderMakeSync() + { + } + + public PagedSourceProviderMakeSync( + Func>> funcGetItemsAtAsync = null, + Func> funcGetCountAsync = null, + Func funcIndexOf = null, + Func> funcIndexOfAsync = null, + Func funcContains = null, + Func> funcContainsAsync = null, + Action actionOnReset = null, + Func funcGetPlaceHolder = null, + Action actionOnBeforeReset = null + ) + { + this.FuncGetItemsAtAsync = funcGetItemsAtAsync; + this.FuncGetCountAsync = funcGetCountAsync; + this.FuncIndexOf = funcIndexOf; + this.FuncIndexOfAsync = funcIndexOfAsync; + this.FuncContains = funcContains; + this.FuncContainsAsync = funcContainsAsync; + this.ActionOnReset = actionOnReset; + this.FuncGetPlaceHolder = funcGetPlaceHolder; + this.ActionOnBeforeReset = actionOnBeforeReset; + } + + public Action ActionOnBeforeReset { get; set; } + + public Action ActionOnReset { get; set; } + public Func FuncContains { get; set; } + public Func> FuncContainsAsync { get; set; } + + public Func> FuncGetCountAsync { get; set; } + + public Func>> FuncGetItemsAtAsync { get; set; } + + public Func FuncGetPlaceHolder { get; set; } + + public Func FuncIndexOf { get; set; } + + public Func> FuncIndexOfAsync { get; set; } + + public virtual void OnReset(int count) + { + this.ActionOnReset?.Invoke(count); + } + + public bool Contains(T item) + { + var ret = false; + + if (this.FuncContains != null) + { + ret = this.FuncContains.Invoke(item); + } + else if (this.FuncContainsAsync != null) + { + ret = Task.Run(() => this.FuncContainsAsync.Invoke(item)).GetAwaiter().GetResult(); + } + else + { + ret = Task.Run(() => this.FuncContainsAsync(item)).GetAwaiter().GetResult(); + } + + return ret; + } + + public int Count => Task.Run(this.GetCountAsync).GetAwaiter().GetResult(); + + public PagedSourceItemsPacket GetItemsAt(int pageoffset, int count, bool usePlaceholder) + { + return Task.Run(() => this.GetItemsAtAsync(pageoffset, count, usePlaceholder)).GetAwaiter().GetResult(); + } + + public virtual int IndexOf(T item) + { + if (this.FuncIndexOf != null) + { + return this.FuncIndexOf.Invoke(item); + } + + if (this.FuncIndexOfAsync != null) + { + return Task.Run(() => this.FuncIndexOfAsync.Invoke(item)).GetAwaiter().GetResult(); + } + + return Task.Run(() => this.IndexOfAsync(item)).GetAwaiter().GetResult(); + } + + public Task ContainsAsync(T item) + { + return this.FuncContainsAsync?.Invoke(item); + } + + public virtual Task GetCountAsync() + { + return this.FuncGetCountAsync?.Invoke(); + } + + public virtual Task> GetItemsAtAsync(int pageoffset, int count, bool usePlaceholder) + { + return this.FuncGetItemsAtAsync?.Invoke(pageoffset, count); + } + + public virtual T GetPlaceHolder(int index, int page, int offset) + { + return this.FuncGetPlaceHolder != null ? this.FuncGetPlaceHolder.Invoke(index, page, offset) : default(T); + } + + public virtual async Task IndexOfAsync(T item) + { + return await Task.FromResult(-1); + } + + public virtual void OnBeforeReset() + { + this.ActionOnBeforeReset?.Invoke(); + } + + /// + /// Gets a value indicating whether access to the is synchronized + /// (thread safe). + /// + /// + /// true if access to the is synchronized (thread safe); otherwise, + /// false. + /// + public bool IsSynchronized { get; } = false; + + /// + /// Gets an object that can be used to synchronize access to the . + /// + /// + /// An object that can be used to synchronize access to the . + /// + public object SyncRoot => this; + + /// + /// Copies the elements of the to an , + /// starting at a particular index. + /// + /// + /// The one-dimensional that is the destination of the elements copied + /// from . The must have zero-based + /// indexing. + /// + /// The zero-based index in at which copying begins. + /// is null. + /// is less than zero. + /// + /// is multidimensional.-or- The number of elements + /// in the source is greater than the available space from + /// to the end of the destination .-or-The type of the source + /// cannot be cast automatically to the type of the destination + /// . + /// + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/PaginationManager.cs b/VirtualizingCollection/Pageing/PaginationManager.cs new file mode 100644 index 0000000..1537c4f --- /dev/null +++ b/VirtualizingCollection/Pageing/PaginationManager.cs @@ -0,0 +1,1469 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AlphaChiTech.Virtualization.Actions; +using AlphaChiTech.VirtualizingCollection; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.Virtualization.Pageing +{ + public class PaginationManager : IItemSourceProvider, INotifyImmediately, IEditableProvider, + IEditableProviderIndexBased, IEditableProviderItemBased, IReclaimableService, + IAsyncResetProvider, IProviderPreReset, INotifyCountChanged, INotifyCollectionChanged, + ICollection where T : class + { + private readonly Dictionary _deltas = new Dictionary(); + private readonly Dictionary> _pages = new Dictionary>(); + private readonly IPageReclaimer _reclaimer; + + private readonly Dictionary _tasks = + new Dictionary(); + + protected object PageLock = new object(); + private int _basePage; + private bool _hasGotCount; + + private int _localCount; + private int _pageSize = 100; + + + /// + /// Initializes a new instance of the class. + /// + /// The provider. + /// The reclaimer. + /// The expiry comparer. + /// Size of the page. + /// The maximum pages. + /// The maximum deltas. + /// The maximum distance. + /// The section context. + public PaginationManager( + IPagedSourceProvider provider, + IPageReclaimer reclaimer = null, + IPageExpiryComparer expiryComparer = null, + int pageSize = 100, + int maxPages = 100, + int maxDeltas = -1, + int maxDistance = -1, + string sectionContext = "") + { + this.PageSize = pageSize; + this.MaxPages = maxPages; + this.MaxDeltas = maxDeltas; + this.MaxDistance = maxDistance; + if (provider is IPagedSourceProviderAsync async) + { + this.ProviderAsync = async; + } + else + { + this.Provider = provider; + } + + this._reclaimer = reclaimer ?? new PageReclaimOnTouched(); + + this.ExpiryComparer = expiryComparer; + + VirtualizationManager.Instance.AddAction(new ReclaimPagesWA(this, sectionContext)); + } + + /// + /// Initializes a new instance of the class. + /// + /// The provider. + /// The reclaimer. + /// The expiry comparer. + /// Size of the page. + /// The maximum pages. + /// The maximum deltas. + /// The maximum distance. + /// The section context. + public PaginationManager( + IPagedSourceObservableProvider provider, + IPageReclaimer reclaimer = null, + IPageExpiryComparer expiryComparer = null, + int pageSize = 100, + int maxPages = 100, + int maxDeltas = -1, + int maxDistance = -1, + string sectionContext = "") : this(provider as IPagedSourceProvider, reclaimer, expiryComparer, pageSize, + maxPages, maxDeltas, maxDistance, sectionContext) + { + provider.CollectionChanged += this.OnProviderCollectionChanged; + } + + public IPageExpiryComparer ExpiryComparer { get; set; } + + /// + /// Gets or sets the maximum deltas. + /// + /// + /// The maximum deltas. + /// + public int MaxDeltas { get; set; } = -1; + + /// + /// Gets or sets the maximum distance. + /// + /// + /// The maximum distance. + /// + public int MaxDistance { get; set; } = -1; + + /// + /// Gets or sets the maximum pages. + /// + /// + /// The maximum pages. + /// + public int MaxPages { get; set; } = 100; + + /// + /// Gets or sets the size of the page. + /// + /// + /// The size of the page. + /// + public int PageSize + { + get => this._pageSize; + set + { + this.DropAllDeltasAndPages(); + this._pageSize = value; + } + } + + /// + /// Gets or sets the provider. + /// + /// + /// The provider. + /// + public IPagedSourceProvider Provider { get; set; } + + /// + /// Gets or sets the provider asynchronous. + /// + /// + /// The provider asynchronous. + /// + public IPagedSourceProviderAsync ProviderAsync { get; set; } + + public int StepToJumpThreashold { get; set; } = 10; + + private int AddNotificationsCount { get; set; } + + private int LocalCount + { + get => this._localCount; + set => this._localCount = value; + } + + public async Task GetCountAsync() + { + this._hasGotCount = true; + if (!this.IsAsync) + { + return this.Provider.Count; + } + + return await this.ProviderAsync.GetCountAsync(); + } + + + /// + /// Resets the specified count. + /// + /// The count. + public void OnReset(int count) + { + this.CancelAllRequests(); + + lock (this.PageLock) + { + this.DropAllDeltasAndPages(); + } + + if (count < 0) + { + this._hasGotCount = false; + } + else + { + //TODO <-lock (this.SyncRoot) + lock (this.SyncRoot) + { + this.LocalCount = count; + this._hasGotCount = true; + } + } + + if (!this.IsAsync) + { + this.Provider.OnReset(count); + } + else + { + this.ProviderAsync.OnReset(count); + } + + if (count >= -1) + { + this.RaiseCountChanged(true, count); + } + } + + + #region Implementation of IEnumerable + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + #endregion + + public bool Contains(T item) + { + // Attempt to get the item from the pages, else call the provider to get it.. + lock (this.PageLock) + { + foreach (var p in this._pages) + { + var o = p.Value.IndexOf(item); + if (o >= 0) + { + return true; + } + } + } + + return !this.IsAsync + ? this.Provider.Contains(item) + : this.ProviderAsync.ContainsAsync(item).GetAwaiter().GetResult(); + } + + public T GetAt(int index, object voc, bool usePlaceholder = true) + { + return this.GetAt(index, voc, usePlaceholder, 10); + } + + /// + /// Gets the count. + /// + /// + /// The count. + /// + public int GetCount(bool asyncOk) + { + if (this._hasGotCount) return this.LocalCount; + + //TODO<-lock(this.SyncRoot) + lock (this) + { + if (!this.IsAsync) + { + this.LocalCount = this.Provider.Count; + } + else if (!asyncOk) + { + this.LocalCount = this.ProviderAsync.GetCountAsync().GetAwaiter().GetResult(); + } + else + { + var cts = this.StartPageRequest(int.MinValue); + this.GetCountAsync(cts); + } + } + + this._hasGotCount = true; + return this.LocalCount; + } + + /// + /// Gets the Index of item. + /// + /// The item. + /// the index of the item, or -1 if not found + public int IndexOf(T item) + { + // Attempt to get the item from the pages, else call the provider to get it.. + lock (this.PageLock) + { + foreach (var p in this._pages) + { + var o = p.Value.IndexOf(item); + if (o >= 0) + { + return o + ((p.Key - this._basePage) * this.PageSize) + (from d in this._deltas.Values + where d.Page < p.Key + select d.Delta).Sum(); + } + } + } + + return !this.IsAsync + ? this.Provider.IndexOf(item) + : this.ProviderAsync.IndexOfAsync(item).GetAwaiter().GetResult(); + + //return this.ProviderAsync.IndexOf(item); + } + + #region Implementation of INotifyCollectionChanged + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + #endregion + + /// + /// Occurs when [count changed]. + /// + public event OnCountChanged CountChanged; + + #region Implementation of INotifyImmediately + + public bool IsNotifyImmidiately + { + get => this.Provider is INotifyImmediately iNotifyImmediatelyProvider && + iNotifyImmediatelyProvider.IsNotifyImmidiately; + set + { + if (this.Provider is INotifyImmediately iNotifyImmediatelyProvider) + { + iNotifyImmediatelyProvider.IsNotifyImmidiately = value; + } + } + } + + #endregion + + + public void OnBeforeReset() + { + if (!this.IsAsync) + { + (this.Provider as IProviderPreReset)?.OnBeforeReset(); + } + else + { + (this.ProviderAsync as IProviderPreReset)?.OnBeforeReset(); + } + } + + public void RunClaim(string sectionContext = "") + { + if (this._reclaimer == null) return; + lock (this.PageLock) + { + var needed = Math.Max(0, this._pages.Count - this.MaxPages); + if (needed == 0) return; + var reclaimedPages = this._reclaimer.ReclaimPages(this._pages.Values, needed, sectionContext).ToList(); + + foreach (var reclaimedPage in reclaimedPages) + { + if (reclaimedPage.Page == this._basePage) continue; + lock (this._pages) + { + if (!this._pages.ContainsKey(reclaimedPage.Page)) continue; + this._pages.Remove(reclaimedPage.Page); + this._reclaimer.OnPageReleased(reclaimedPage); + } + } + } + } + + /// + /// Adds the or update adjustment. + /// + /// The page. + /// The offset change. + public int AddOrUpdateAdjustment(int page, int offsetChange) + { + var ret = 0; + + lock (this.PageLock) + { + if (!this._deltas.ContainsKey(page)) + { + if (this.MaxDeltas == -1 || this._deltas.Count < this.MaxDeltas) + { + ret = offsetChange; + this._deltas.Add(page, new PageDelta {Page = page, Delta = offsetChange}); + } + else + { + this.DropAllDeltasAndPages(); + } + } + else + { + var adjustment = this._deltas[page]; + adjustment.Delta += offsetChange; + + if (adjustment.Delta == 0) + { + this._deltas.Remove(page); + } + + ret = adjustment.Delta; + } + } + + return ret; + } + + /// + /// Gets at. + /// + /// The index. + /// The voc. + /// if set to true [use placeholder]. + /// + public T GetAt(int index, object voc, bool usePlaceholder = true, int nullTryCount = 10) + { + var ret = default(T); + + this.CalculateFromIndex(index, out var page, out var offset); + + var datapage = this.SafeGetPage(page, usePlaceholder, voc, index); + + if (datapage != null) + { + ret = datapage.GetAt(offset); + } + + if (ret == null) + { + //return this.ProviderAsync.GetPlaceHolder(0, 0,0); + Debugger.Break(); + //TODO <- + if (nullTryCount <= 0) //inconsistency, notify reset collection + { + this.OnProviderCollectionChanged(this.Provider, + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + return ret; + } + + Thread.Sleep(100); + return this.GetAt(index, voc, usePlaceholder, --nullTryCount); + } + + return ret; + } + + + protected void CalculateFromIndex(int index, out int page, out int inneroffset) + { + // First work out the base page from the index and the offset inside that page + var basepage = page = (index / this.PageSize) + this._basePage; + inneroffset = (index + (this._basePage * this.PageSize)) - (page * this.PageSize); + + // We only need to do the rest if there have been modifications to the page sizes on pages (deltas) + if (this._deltas.Count <= 0) return; + + // Get the adjustment BEFORE checking for a short page, because we are going to adjust for that first.. + var adjustment = 0; + + lock (this.PageLock) + { + // First, get the total adjustments for any pages BEFORE the current page.. + adjustment = (from d in this._deltas.Values + where d.Page < basepage + select d.Delta).Sum(); + } + + // Now check to see if we are currently in a short page - in which case we need to adjust for that + if (this._deltas.ContainsKey(page)) + { + var delta = this._deltas[page].Delta; + + if (delta < 0) + { + // In a short page, are we over the edge ? + if (inneroffset >= this.PageSize + delta) + { + var step = inneroffset - (this.PageSize + delta - 1); + inneroffset -= step; + this.DoStepForward(ref page, ref inneroffset, step); + } + } + } + + // If we do have adjustments... + if (adjustment == 0) return; + + if (adjustment > 0) + { + // items have been added to earlier pages, so we need to step back + this.DoStepBackwards(ref page, ref inneroffset, adjustment); + } + else + { + // items have been removed from earlier pages, so we need to step forward + this.DoStepForward(ref page, ref inneroffset, Math.Abs(adjustment)); + } + } + + protected void CancelAllRequests() + { + lock (this.PageLock) + { + var cancellationTokenSources = this._tasks.Values.ToList(); + foreach (var cancellationTokenSource in cancellationTokenSources) + { + try + { + cancellationTokenSource.Cancel(false); + } + catch (Exception) + { + Debugger.Break(); + } + } + + this._tasks.Clear(); + } + } + + + protected void CancelPageRequest(int page) + { + lock (this.PageLock) + { + if (!this._tasks.ContainsKey(page)) + { + return; + } + + try + { + this._tasks[page].Cancel(); + } + catch (Exception) + { + Debugger.Break(); + } + + try + { + this._tasks.Remove(page); + } + catch (Exception) + { + Debugger.Break(); + } + } + } + + /// + /// Drops all deltas and pages. + /// + protected void DropAllDeltasAndPages() + { + lock (this.PageLock) + { + this._deltas.Clear(); + this._pages.Clear(); + this._basePage = 0; + this.CancelAllRequests(); + } + } + + + /// + /// Gets the provider as editable. + /// + /// + /// + protected IEditableProvider GetProviderAsEditable() + { + if (this.Provider != null) + { + return this.Provider as IEditableProvider; + } + + return this.ProviderAsync as IEditableProvider; + } + + + /// + /// Raises the count changed. + /// + /// The count. + protected void RaiseCountChanged(bool needsReset, int count) + { + //TODO<-this._hasGotCount = false; + var evnt = this.CountChanged; + evnt?.Invoke(this, new CountChangedEventArgs + { + NeedsReset = needsReset, + Count = count + }); + } + + protected void RemovePageRequest(int page) + { + lock (this.PageLock) + { + if (!this._tasks.ContainsKey(page)) return; + try + { + this._tasks.Remove(page); + } + catch (Exception) + { + Debugger.Break(); + } + } + } + + protected CancellationTokenSource StartPageRequest(int page) + { + var cts = new CancellationTokenSource(); + + this.CancelPageRequest(page); + + lock (this.PageLock) + { + if (!this._tasks.ContainsKey(page)) + { + this._tasks.Add(page, cts); + } + else + { + this._tasks[page] = cts; + } + } + + return cts; + } + + + private void DoStepBackwards(ref int page, ref int offset, int stepAmount) + { + var done = false; + var ignoreSteps = -1; + //TODO <-lock (this.PageLock) + //{ + while (!done) + { + if (stepAmount > this.PageSize * this.StepToJumpThreashold && ignoreSteps <= 0) + { + var targetPage = page - stepAmount / this.PageSize; + var sourcePage = page; + var adj = (from a in this._deltas.Values + where a.Page >= targetPage && a.Page <= sourcePage + orderby a.Page + select a).ToArray(); + if (!adj.Any()) + { + page = targetPage; + stepAmount -= (sourcePage - targetPage) * this.PageSize; + + if (stepAmount == 0) + { + done = true; + } + } + else if (adj.Last().Page < page - 2) + { + targetPage = adj.Last().Page + 1; + page = targetPage; + stepAmount -= (sourcePage - targetPage) * this.PageSize; + + if (stepAmount == 0) + { + done = true; + } + } + else + { + ignoreSteps = sourcePage - adj.Last().Page; + } + } + + if (done) continue; + + if (offset - stepAmount < 0) + { + stepAmount -= (offset + 1); + page--; + var items = this.PageSize; + if (this._deltas.ContainsKey(page)) + { + items += this._deltas[page].Delta; + } + + offset = items - 1; + } + else + { + offset -= stepAmount; + done = true; + } + + ignoreSteps--; + } + + // } + } + + private void DoStepForward(ref int page, ref int offset, int stepAmount) + { + var done = false; + var ignoreSteps = -1; + //TODO <-lock (this.PageLock) + //{ + while (!done) + { + if (stepAmount > this.PageSize * this.StepToJumpThreashold && ignoreSteps <= 0) + { + var targetPage = page + stepAmount / this.PageSize; + var sourcePage = page; + var adj = (from a in this._deltas.Values + where a.Page <= targetPage && a.Page >= sourcePage + orderby a.Page + select a).ToArray(); + if (!adj.Any()) + { + page = targetPage; + stepAmount -= (targetPage - sourcePage) * this.PageSize; + + if (stepAmount == 0) + { + done = true; + } + } + else if (adj.Last().Page > page - 2) + { + targetPage = adj.Last().Page - 1; + page = targetPage; + stepAmount -= (targetPage - sourcePage) * this.PageSize; + + if (stepAmount == 0) + { + done = true; + } + } + else + { + ignoreSteps = adj.Last().Page - sourcePage; + } + } + + if (done) continue; + + var items = this.PageSize; + if (this._deltas.ContainsKey(page)) + { + items += this._deltas[page].Delta; + } + + if (items <= offset + stepAmount) + { + stepAmount -= (items) - offset; + offset = 0; + page++; + } + else + { + offset += stepAmount; + done = true; + } + + ignoreSteps--; + } + + //} + } + + /// + /// Fills the page. + /// + /// The new page. + /// The page offset. + private void FillPage(ISourcePage newPage, int pageOffset) + { + var data = this.Provider.GetItemsAt(pageOffset, newPage.ItemsPerPage, false); + newPage.WiredDateTime = data.LoadedAt; + foreach (var o in data.Items) + { + newPage.Append(o, null, this.ExpiryComparer); + } + + newPage.PageFetchState = PageFetchStateEnum.Fetched; + } + + /// + /// Fills the page from asynchronous provider. + /// + /// The new page. + /// The page offset. + private void FillPageFromAsyncProvider(ISourcePage newPage, int pageOffset) + { + var data = this.ProviderAsync.GetItemsAt(pageOffset, newPage.ItemsPerPage, false); + newPage.WiredDateTime = data.LoadedAt; + foreach (var o in data.Items) + { + newPage.Append(o, null, this.ExpiryComparer); + } + + newPage.PageFetchState = PageFetchStateEnum.Fetched; + } + + private async void GetCountAsync(CancellationTokenSource cts) + { + if (!cts.IsCancellationRequested) + { + var ret = await this.ProviderAsync.GetCountAsync(); + + if (!cts.IsCancellationRequested) + { + //TODO<-lock (this.SyncRoot) + lock (this) + { + this._hasGotCount = true; + this.LocalCount = ret; + } + } + + if (!cts.IsCancellationRequested) + { + this.RaiseCountChanged(true, this.LocalCount); + } + } + + this.RemovePageRequest(int.MinValue); + } + + + private void OnProviderCollectionChanged(object sender, + NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + //lock(this._addLock) + //{ + switch (notifyCollectionChangedEventArgs.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (var item in notifyCollectionChangedEventArgs.NewItems) + { + if (!(item is T newItem)) continue; + + this.AddNotificationsCount++; + this.OnAppend(newItem, DateTime.Now, true, true); + } + + this.CollectionChanged?.Invoke(sender, + notifyCollectionChangedEventArgs); // check if this.OnAppend does not raise collection change as well + //this.RaiseCountChanged(true, this._localCount); + break; + case NotifyCollectionChangedAction.Reset: + case NotifyCollectionChangedAction.Remove: //TODO + lock (this.PageLock) + { + this._hasGotCount = false; + this.CancelAllRequests(); + this.DropAllDeltasAndPages(); + } + + this.CollectionChanged?.Invoke(sender, notifyCollectionChangedEventArgs); + break; + + case NotifyCollectionChangedAction.Replace: //TODO + case NotifyCollectionChangedAction.Move: //TODO + default: + break; + } + + //} + } + + + #region IEditableProvider Implementation + + /// + /// Called when [append]. + /// + /// The item. + /// The timestamp. + /// + /// + /// + public int OnAppend(T item, object timestamp, bool isAlreadyInSourceCollection = false, + bool createPageIfNotExist = false) + { + var index = this.LocalCount; + + if (!this._hasGotCount) + { + lock (this.SyncRoot) + { + this.EnsureCount(); + if (isAlreadyInSourceCollection) + { + Interlocked.Decrement(ref this._localCount); + } + } + } + + this.CalculateFromIndex(index, out var page, out _); + + if (this.IsPageWired(page)) + { + var shortpage = false; + var dataPage = this.SafeGetPage(page, false, null, index); + if (dataPage.ItemsPerPage < this.PageSize) + { + shortpage = true; + } + + dataPage.Append(item, timestamp, this.ExpiryComparer); + + if (shortpage) + { + dataPage.ItemsPerPage++; + } + else + { + this.AddOrUpdateAdjustment(page, 1); + } + } + else if (createPageIfNotExist) + { + var dataPage = this.CreateNewPage(page, out _, out _); + dataPage.Append(item, timestamp, this.ExpiryComparer); + } + + Interlocked.Increment(ref this._localCount); + + if (this.IsAsync) + { + var test = this.GetAt(index, this, false); + } + + var edit = this.GetProviderAsEditable(); + if (edit != null && !isAlreadyInSourceCollection) + { + //==>edit.OnInsert(index, item, timestamp); + //TODO<-edit.OnAppend(item, timestamp); + edit.OnAppend(item, timestamp); + } + else if (!isAlreadyInSourceCollection) + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); + this.CollectionChanged?.Invoke(this, args); + } + + return index; + } + + /// + /// Gets the page, if use placeholders is false - then gets page sync else async. + /// + /// The page. + /// if set to true [allow placeholders]. + /// The voc. + /// The index that this page refers to (effectively the pageoffset. + /// + protected ISourcePage SafeGetPage(int page, bool allowPlaceholders, object voc, int index) + { + ISourcePage ret = null; + + lock (this.PageLock) + { + if (this._pages.ContainsKey(page)) + { + ret = this._pages[page]; + this._reclaimer.OnPageTouched(ret); + } + else + { + var newPage = this.CreateNewPage(page, out var pageSize, out var pageOffset); + + if (!this.IsAsync) + { + this.FillPage(newPage, pageOffset); + + ret = newPage; + } + else + { + if (allowPlaceholders && voc != null) + { + // Fill with placeholders + for (var loop = 0; loop < pageSize; loop++) + { + var placeHolder = this.ProviderAsync.GetPlaceHolder(newPage.Page * pageSize + loop, + newPage.Page, loop); + newPage.Append(placeHolder, null, this.ExpiryComparer); + } + + ret = newPage; + + var cts = this.StartPageRequest(newPage.Page); + Task.Run(() => this.DoRealPageGet(voc, newPage, pageOffset, index, cts), cts.Token) + .ConfigureAwait(false); + } + else + { + this.FillPageFromAsyncProvider(newPage, pageOffset); + ret = newPage; + } + } + } + } + + return ret; + } + + private ISourcePage CreateNewPage(int page, out int pageSize, out int pageOffset) + { + PageDelta delta = null; + if (this._deltas.ContainsKey(page)) + { + delta = this._deltas[page]; + } + + pageOffset = (page - this._basePage) * this.PageSize + (from d in this._deltas.Values + where d.Page < page + select d.Delta).Sum(); + pageSize = Math.Min(this.PageSize, this.GetCount(false) - pageOffset); + if (delta != null) + { + pageSize += delta.Delta; + } + + var newPage = this._reclaimer.MakePage(page, pageSize); + this._pages.Add(page, newPage); + return newPage; + } + + private async Task DoRealPageGet(object voc, ISourcePage page, int pageOffset, int index, + CancellationTokenSource cts) + { + var realVoc = (VirtualizingObservableCollection) voc; + var listOfReplaces = new List>(); + + if (realVoc != null) + { + if (cts.IsCancellationRequested) + { + return; + } + + var data = await this.ProviderAsync.GetItemsAtAsync(pageOffset, page.ItemsPerPage, false); + + if (cts.IsCancellationRequested) + { + return; + } + + page.WiredDateTime = data.LoadedAt; + + var i = 0; + foreach (var item in data.Items) + { + if (cts.IsCancellationRequested) + { + this.RemovePageRequest(page.Page); + return; + } + + if (page.ReplaceNeeded(i)) + { + var oldItem = page.ReplaceAt(i, item, null, null); + listOfReplaces.Add(new PlaceholderReplaceWA(realVoc, oldItem, item, pageOffset + i)); + } + else + { + page.ReplaceAt(i, item, null, null); + } + + i++; + } + } + + page.PageFetchState = PageFetchStateEnum.Fetched; + + foreach (var replace in listOfReplaces) + { + if (cts.IsCancellationRequested) + { + this.RemovePageRequest(page.Page); + return; + } + + VirtualizationManager.Instance.RunOnUi(replace); + } + + this.RemovePageRequest(page.Page); + } + + protected bool IsPageWired(int page) + { + var wired = false; + + lock (this.PageLock) + { + if (this._pages.ContainsKey(page)) + { + wired = true; + } + } + + return wired; + } + + public int OnAppend(T item, object timestamp) => this.OnAppend(item, timestamp, false, false); + + public void OnInsert(int index, T item, object timestamp) + { + if (!this._hasGotCount) + { + this.EnsureCount(); + } + + this.CalculateFromIndex(index, out var page, out var offset); + + if (this.IsPageWired(page)) + { + var dataPage = this.SafeGetPage(page, false, null, index); + dataPage.InsertAt(offset, item, timestamp, this.ExpiryComparer); + } + + var adj = this.AddOrUpdateAdjustment(page, 1); + + if (page == this._basePage && adj == this.PageSize * 2) + { + lock (this.PageLock) + { + if (this.IsPageWired(page)) + { + var dataPage = this.SafeGetPage(page, false, null, index); + ISourcePage newdataPage = null; + if (this.IsPageWired(page - 1)) + { + newdataPage = this.SafeGetPage(page - 1, false, null, index); + } + else + { + newdataPage = this._reclaimer.MakePage(page - 1, this.PageSize); + this._pages.Add(page - 1, newdataPage); + } + + for (var loop = 0; loop < this.PageSize; loop++) + { + var i = dataPage.GetAt(0); + + dataPage.RemoveAt(0, null, null); + newdataPage.Append(i, null, null); + } + } + + this.AddOrUpdateAdjustment(page, -this.PageSize); + + this._basePage--; + } + } + + if (this.IsAsync) + { + var test = this.GetAt(index, this, false); + } + + var edit = this.GetProviderAsEditable(); + if (edit != null) + { + edit.OnInsert(index, item, timestamp); + } + else + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); + this.CollectionChanged?.Invoke(this, args); + } + + Interlocked.Increment(ref this._localCount); + } + + public void OnReplace(int index, T oldItem, T newItem, object timestamp) + { + this.CalculateFromIndex(index, out var page, out var offset); + + if (this.IsPageWired(page)) + { + var dataPage = this.SafeGetPage(page, false, null, index); + dataPage.ReplaceAt(offset, newItem, timestamp, this.ExpiryComparer); + } + else + { + oldItem = this.Provider.GetItemsAt(index, 1, false).Items.FirstOrDefault(); + if (oldItem != default(T)) Debugger.Break(); + } + + if (this.Provider is IEditableProvider editableProvider) + { + editableProvider.OnReplace(index, oldItem, newItem, timestamp); + } + else + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, + index); + this.CollectionChanged?.Invoke(this, args); + } + } + + private void EnsureCount() + { + this.GetCount(false); + } + + protected bool IsAsync => this.ProviderAsync != null ? true : false; + + #endregion IEditableProvider Implementation + + #region Implementation of ICollection + + /// + /// + /// Copies the elements of the to an , + /// starting at a particular index. + /// + /// + /// The one-dimensional that is the destination of the elements copied + /// from . The must have zero-based + /// indexing. + /// + /// The zero-based index in at which copying begins. + /// is null. + /// is less than zero. + /// + /// is multidimensional.-or- The number of elements + /// in the source is greater than the available space from + /// to the end of the destination .-or-The type of the source + /// cannot be cast automatically to the type of the destination + /// . + /// + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + /// + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + /// + public int Count => this.GetCount(false); + + /// + /// + /// Gets an object that can be used to synchronize access to the . + /// + /// + /// An object that can be used to synchronize access to the . + /// + public object SyncRoot => this.Provider?.SyncRoot ?? this.ProviderAsync.SyncRoot; + //public object SyncRoot => this.PageLock ?? this.ProviderAsync.SyncRoot; + + /// + /// Gets a value indicating whether access to the is synchronized + /// (thread safe). + /// + /// + /// true if access to the is synchronized (thread safe); otherwise, + /// false. + /// + public bool IsSynchronized => this.Provider.IsSynchronized; + + #endregion + + #region Implementation of IEditableProviderIndexBased + + public T OnRemove(int index, object timestamp) + { + T item; + + if (!this._hasGotCount) + { + this.EnsureCount(); + } + + this.CalculateFromIndex(index, out var page, out var offset); + + if (this.IsPageWired(page)) + { + var dataPage = this.SafeGetPage(page, false, null, index); + dataPage.RemoveAt(offset, timestamp, this.ExpiryComparer); + } + + this.AddOrUpdateAdjustment(page, -1); + + if (page == this._basePage) + { + var items = this.PageSize; + if (this._deltas.ContainsKey(page)) + { + items += this._deltas[page].Delta; + } + + if (items == 0) + { + this._deltas.Remove(page); + this._basePage++; + } + } + + if (this.IsAsync) + { + var test = this.GetAt(index, this, false); + } + + if (this.Provider is IEditableProviderIndexBased editableProvider) + { + item = editableProvider.OnRemove(index, timestamp); + } + else + { + item = this.GetAt(index, this.Provider, false); + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index); + this.CollectionChanged?.Invoke(this, args); + } + + Interlocked.Decrement(ref this._localCount); + + return item; + } + + public T OnReplace(int index, T newItem, object timestamp) + { + T oldItem; + + this.CalculateFromIndex(index, out var page, out var offset); + + if (this.IsPageWired(page)) + { + var dataPage = this.SafeGetPage(page, false, null, index); + oldItem = dataPage.ReplaceAt(offset, newItem, timestamp, this.ExpiryComparer); + } + else + { + oldItem = this.Provider.GetItemsAt(index, 1, false).Items.FirstOrDefault(); + if (oldItem != default(T)) Debugger.Break(); + } + + if (this.Provider is IEditableProviderIndexBased editableProvider) + { + editableProvider.OnReplace(index, newItem, timestamp); + } + else + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, + index); + this.CollectionChanged?.Invoke(this, args); + } + + return oldItem; + } + + #endregion + + #region Implementation of IEditableProviderItemBased + + public int OnRemove(T item, object timestamp) + { + if (!this._hasGotCount) + { + this.EnsureCount(); + } + + var index = this.Provider.IndexOf(item); + this.CalculateFromIndex(index, out var page, out var offset); + + if (this.IsPageWired(page)) + { + var dataPage = this.SafeGetPage(page, false, null, index); + dataPage.RemoveAt(offset, timestamp, this.ExpiryComparer); + } + + this.AddOrUpdateAdjustment(page, -1); + + if (page == this._basePage) + { + var items = this.PageSize; + if (this._deltas.ContainsKey(page)) + { + items += this._deltas[page].Delta; + } + + if (items == 0) + { + this._deltas.Remove(page); + this._basePage++; + } + } + + if (this.IsAsync) + { + var test = this.GetAt(index, this, false); + } + + if (this.Provider is IEditableProviderItemBased editableProvider) + { + editableProvider.OnRemove(item, timestamp); + } + else + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index); + this.CollectionChanged?.Invoke(this, args); + } + + Interlocked.Decrement(ref this._localCount); + + return index; + } + + public int OnReplace(T oldItem, T newItem, object timestamp) + { + var index = this.Provider.IndexOf(oldItem); + + this.CalculateFromIndex(index, out var page, out var offset); + + if (this.IsPageWired(page)) + { + var dataPage = this.SafeGetPage(page, false, null, index); + dataPage.ReplaceAt(offset, newItem, timestamp, this.ExpiryComparer); + } + + + if (this.Provider is IEditableProviderItemBased editableProvider) + { + editableProvider.OnReplace(oldItem, newItem, timestamp); + } + else + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, + index); + this.CollectionChanged?.Invoke(this, args); + } + + return index; + } + + #endregion + } +} \ No newline at end of file diff --git a/VirtualizingCollection/Pageing/SourcePage.cs b/VirtualizingCollection/Pageing/SourcePage.cs new file mode 100644 index 0000000..583fb23 --- /dev/null +++ b/VirtualizingCollection/Pageing/SourcePage.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.Virtualization.Pageing +{ + public class SourcePage : ISourcePage + { + private readonly List _replaceNeededList = new List(); + protected List Items = new List(); + + /// + /// Appends the specified item. + /// + /// The item. + /// + /// + /// + public int Append(T item, object updatedAt, IPageExpiryComparer comparer) + { + this.Items.Add(item); + + this.LastTouch = DateTime.Now; + + return this.Items.IndexOf(item); + //TODO<-return this.Items.Count; + } + + /// + /// Gets a value indicating whether [can reclaim page]. + /// + /// + /// true if [can reclaim page]; otherwise, false. + /// + public bool CanReclaimPage => this.PageFetchState != PageFetchStateEnum.Placeholders; + + /// + /// Gets at. + /// + /// The offset. + /// + public T GetAt(int offset) + { + var ret = default(T); + + if (this.PageFetchState == PageFetchStateEnum.Placeholders) this._replaceNeededList.Add(offset); + + if (this.Items.Count > offset) ret = this.Items[offset]; + + this.LastTouch = DateTime.Now; + + return ret; + } + + /// + /// Indexes the of. + /// + /// The item. + /// + public int IndexOf(T item) + { + this.LastTouch = DateTime.Now; + + return this.Items.IndexOf(item); + } + + /// + /// Inserts at. + /// + /// The offset. + /// The item. + /// The updated at. + /// + public void InsertAt(int offset, T item, object updatedAt, IPageExpiryComparer comparer) + { + if (!this.IsSafeToUpdate(comparer, updatedAt)) return; + + if (this.Items.Count > offset) + { + this.Items.Insert(offset, item); + } + else + { + this.Items.Add(item); + } + } + + public int ItemsCount => this.Items.Count; + + /// + /// Gets or sets the items per page. + /// + /// + /// The items per page. + /// + public int ItemsPerPage { get; set; } + + /// + /// Gets or sets the last touch. + /// + /// + /// The last touch. + /// + public object LastTouch { get; set; } + + + /// + /// Gets or sets the page. + /// + /// + /// The page. + /// + public int Page { get; set; } + + /// + /// Gets or sets the state of the page fetch state. + /// + /// + /// The state of the page fetch. + /// + public PageFetchStateEnum PageFetchState { get; set; } = PageFetchStateEnum.Placeholders; + + public List PendingUpdates { get; } = new List(); + + /// + /// Removes at. + /// + /// The offset. + /// The updated at. + /// + /// + public bool RemoveAt(int offset, object updatedAt, IPageExpiryComparer comparer) + { + if (this.IsSafeToUpdate(comparer, updatedAt)) + { + this.Items.RemoveAt(offset); + } + + return true; + } + + + /// + /// Replaces at. + /// + /// The offset. + /// The new value. + /// The updated at. + /// + public T ReplaceAt(int offset, T newValue, object updatedAt, IPageExpiryComparer comparer) + { + var oldValue = this.Items[offset]; + if (!this.IsSafeToUpdate(comparer, updatedAt)) + { + if (this.Items.Count > offset) this.Items[offset] = newValue; + return oldValue; + } + + this.Items[offset] = newValue; + return oldValue; + } + + /// + /// Replaces at. + /// + /// The old value. + /// The new value. + /// The updated at. + /// + public T ReplaceAt(T oldValue, T newValue, object updatedAt, IPageExpiryComparer comparer) + { + if (!this.IsSafeToUpdate(comparer, updatedAt)) + { + return oldValue; + } + + var offset = this.Items.IndexOf(oldValue); + this.Items[offset] = newValue; + return oldValue; + } + + public bool ReplaceNeeded(int offset) + { + return this._replaceNeededList.Contains(offset); + } + + /// + /// Gets or sets the wired date time. + /// + /// + /// The wired date time. + /// + public object WiredDateTime { get; set; } = DateTime.MinValue; + + /// + /// Determines whether it is safe to update into a page where the pending update was generated at a given time. + /// + /// + /// The updated happened at this datetime. + /// + public bool IsSafeToUpdate(IPageExpiryComparer comparer, object updatedAt) + { + return comparer == null || comparer.IsUpdateValid(this.WiredDateTime, updatedAt); + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/VirtualizationManager.cs b/VirtualizingCollection/VirtualizationManager.cs new file mode 100644 index 0000000..e54c163 --- /dev/null +++ b/VirtualizingCollection/VirtualizationManager.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AlphaChiTech.Virtualization.Actions; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.VirtualizingCollection +{ + public class VirtualizationManager + { + private readonly object _actionLock = new object(); + + private readonly List _actions = new List(); + + private bool _processing; + + private Action _uiThreadExcecuteAction; + + public static VirtualizationManager Instance { get; } = new VirtualizationManager(); + + public static bool IsInitialized { get; private set; } + + public Action UiThreadExcecuteAction + { + get => this._uiThreadExcecuteAction; + set + { + this._uiThreadExcecuteAction = value; + IsInitialized = true; + } + } + + public void AddAction(IVirtualizationAction action) + { + lock (this._actionLock) + { + this._actions.Add(action); + } + } + + public void AddAction(Action action) + { + this.AddAction(new ActionVirtualizationWrapper(action)); + } + + public void ProcessActions() + { + if (this._processing) return; + + this._processing = true; + + List lst; + lock (this._actionLock) + { + lst = this._actions.ToList(); + } + + foreach (var action in lst) + { + var bdo = true; + + if (action is IRepeatingVirtualizationAction) + { + bdo = (action as IRepeatingVirtualizationAction).IsDueToRun(); + } + + if (!bdo) continue; + switch (action.ThreadModel) + { + case VirtualActionThreadModelEnum.UseUIThread: + if (this.UiThreadExcecuteAction == null) // PLV + throw new Exception( + "VirtualizationManager isn’t already initialized ! set the VirtualizationManager’s UIThreadExcecuteAction (VirtualizationManager.Instance.UIThreadExcecuteAction = a => Dispatcher.Invoke( a );)"); + this.UiThreadExcecuteAction.Invoke(() => action.DoAction()); + break; + case VirtualActionThreadModelEnum.Background: + Task.Run(() => action.DoAction()).ConfigureAwait(false); + break; + default: + break; + } + + if (action is IRepeatingVirtualizationAction) + { + if ((action as IRepeatingVirtualizationAction).KeepInActionsList()) continue; + lock (this._actionLock) + { + this._actions.Remove(action); + } + } + else + { + lock (this._actionLock) + { + this._actions.Remove(action); + } + } + } + + this._processing = false; + } + + public void RunOnUi(IVirtualizationAction action) + { + if (this.UiThreadExcecuteAction == null) // PLV + throw new Exception( + "VirtualizationManager isn’t already initialized ! set the VirtualizationManager’s UIThreadExcecuteAction (VirtualizationManager.Instance.UIThreadExcecuteAction = a => Dispatcher.Invoke( a );)"); + this.UiThreadExcecuteAction.Invoke(action.DoAction); + } + + public void RunOnUi(Action action) + { + this.RunOnUi(new ActionVirtualizationWrapper(action)); + } + } +} \ No newline at end of file diff --git a/VirtualizingCollection/VirtualizingCollection.csproj b/VirtualizingCollection/VirtualizingCollection.csproj new file mode 100644 index 0000000..81ed38d --- /dev/null +++ b/VirtualizingCollection/VirtualizingCollection.csproj @@ -0,0 +1,20 @@ + + + + net451;netstandard2.0; + AlphaChiTech.VirtualizingCollection + AlphaChiTech.VirtualizingCollection + 1.2.0 + Copyright © 2015 AlphaChi technology + AlphaChi Technology + + AlphaChiTech.VirtualizingCollection + AlphaChiTech.VirtualizingCollection + + + + + + + + diff --git a/VirtualizingCollection/VirtualizingObservableCollection.cs b/VirtualizingCollection/VirtualizingObservableCollection.cs new file mode 100644 index 0000000..8a6258e --- /dev/null +++ b/VirtualizingCollection/VirtualizingObservableCollection.cs @@ -0,0 +1,1028 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; +using AlphaChiTech.Virtualization.Pageing; +using AlphaChiTech.VirtualizingCollection; +using AlphaChiTech.VirtualizingCollection.Interfaces; + +namespace AlphaChiTech.Virtualization +{ + public class VirtualizingObservableCollection : IEnumerable, IEnumerable, ICollection, ICollection, IList, + IList, IObservableCollection, INotifyCollectionChanged, INotifyPropertyChanged where T : class + { + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + public IEnumerator GetEnumerator() => new VirtualizingObservableCollectionEnumerator(this); + + public class VirtualizingObservableCollectionEnumerator : IEnumerator where TT : class + { + private int _iLoop; + + /// + /// Initializes a new instance of the class. + /// + public VirtualizingObservableCollectionEnumerator(VirtualizingObservableCollection baseCollection) + { + this.BaseCollection = baseCollection; + } + + public VirtualizingObservableCollection BaseCollection { get; } + + #region Implementation of IDisposable + + public void Dispose() + { + } + + #endregion + + #region Implementation of IEnumerator + + public bool MoveNext() + { + var sc = new Guid().ToString(); + + this.BaseCollection.EnsureCountIsGotNonaSync(); + + try + { + var count = this.BaseCollection.InternalGetCount(); + if (this._iLoop >= count) return false; + + this.Current = this.BaseCollection.InternalGetValue(this._iLoop++, sc); + if (this.Current == null) Debugger.Break(); + return true; + } + catch (Exception) + { + return false; + } + } + + public void Reset() + { + this._iLoop = 0; + this.Current = null; + } + + /// + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// + /// The element in the collection at the current position of the enumerator. + /// + public TT Current { get; private set; } + + object IEnumerator.Current => this.Current; + + #endregion + } + + + #region Ctors Etc + + /// + /// Initializes a new instance of the class. + /// + /// The provider. + public VirtualizingObservableCollection(IItemSourceProvider provider) : this() + { + this.Provider = provider; + } + + /// + /// Initializes a new instance of the class. + /// + /// The asynchronous provider. + public VirtualizingObservableCollection(IItemSourceProviderAsync asyncProvider) : this() + { + this.ProviderAsync = asyncProvider; + } + + /// + /// Initializes a new instance of the class. + /// + /// The provider. + /// The optional reclaimer. + /// The optional expiry comparer. + /// Size of the page. + /// The maximum pages. + /// The maximum deltas. + /// The maximum distance. + public VirtualizingObservableCollection( + IPagedSourceProvider provider, + IPageReclaimer reclaimer = null, + IPageExpiryComparer expiryComparer = null, + int pageSize = 100, + int maxPages = 100, + int maxDeltas = -1, + int maxDistance = -1) : this() + { + this.Provider = new PaginationManager(provider, reclaimer, expiryComparer, pageSize, maxPages, maxDeltas, + maxDistance); + } + + /// + /// Initializes a new instance of the class. + /// + /// The provider. + /// The optional reclaimer. + /// The optional expiry comparer. + /// Size of the page. + /// The maximum pages. + /// The maximum deltas. + /// The maximum distance. + public VirtualizingObservableCollection( + IPagedSourceObservableProvider provider, + IPageReclaimer reclaimer = null, + IPageExpiryComparer expiryComparer = null, + int pageSize = 100, + int maxPages = 100, + int maxDeltas = -1, + int maxDistance = -1) : this() + { + this.Provider = new PaginationManager(provider, reclaimer, expiryComparer, pageSize, maxPages, maxDeltas, + maxDistance); + ((PaginationManager) this.Provider).CollectionChanged += + this.VirtualizingObservableCollection_CollectionChanged; + this.IsSourceObservable = true; + } + + public bool IsSourceObservable { get; set; } + + + protected VirtualizingObservableCollection() + { + //To enable reset in case that noone set UiThreadExcecuteAction + if (VirtualizationManager.Instance.UiThreadExcecuteAction == null) + VirtualizationManager.Instance.UiThreadExcecuteAction = a => Dispatcher.CurrentDispatcher.Invoke(a); + } + + #endregion Ctors Etc + + + #region ICollection Implementation + + public void CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + /// + /// + /// Gets the number of elements contained in the . + /// + /// The number of elements contained in the . + public int Count => this.InternalGetCount(); + + /// + /// + /// Gets a value indicating whether access to the is synchronized + /// (thread safe). + /// + /// + /// true if access to the is synchronized (thread safe); + /// otherwise, false. + /// + public bool IsSynchronized => false; + + /// + /// + /// Gets an object that can be used to synchronize access to the . + /// + /// An object that can be used to synchronize access to the . + public object SyncRoot { get; } = new object(); + + #endregion ICollection Implementation + + #region ICollection Implementation + + /// + /// Adds an item to the . + /// + /// The object to add to the . + public void Add(T item) + { + this.InternalAdd(item, null); + } + + /// + /// + /// Resets the collection - aka forces a get all data, including the count + /// . + /// + public void Clear() + { + this.InternalClear(); + } + + /// + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; + /// otherwise, false. + /// + public bool Contains(T item) + { + return this.Provider.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + foreach (var item in this) + { + array[arrayIndex++] = item; + } + } + + /// + /// + /// Gets a value indicating whether the is read-only. + /// + /// true if the is read-only; otherwise, false. + public bool IsReadOnly => false; + + /// + /// + /// Removes the first occurrence of a specific object from the + /// . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the + /// ; otherwise, false. This method also returns false if + /// is not found in the original . + /// + public bool Remove(T item) + { + return this.InternalRemove(item); + } + + #endregion ICollection Implementation + + #region Extended CRUD operators that take into account the DateTime of the change + + /// + /// Removes the specified item - extended to only remove the item if the page was not pulled before the updatedat + /// DateTime. + /// + /// The item. + /// The updated at. + /// + public bool Remove(T item, object updatedAt) + { + return this.InternalRemove(item, updatedAt); + } + + /// + /// Removes at the given index - extended to only remove the item if the page was not pulled before the updatedat + /// DateTime. + /// + /// The index. + /// The updated at. + /// + public bool RemoveAt(int index, object updatedAt) + { + return this.InternalRemoveAt(index, updatedAt); + } + + /// + /// Adds (appends) the specified item - extended to only add the item if the page was not pulled before the updatedat + /// DateTime. + /// + /// The item. + /// The updated at. + /// + public int Add(T item, object updatedAt) + { + return this.InternalAdd(item, updatedAt); + } + + /// + /// Inserts the specified index - extended to only insert the item if the page was not pulled before the updatedat + /// DateTime. + /// + /// The index. + /// The item. + /// The updated at. + public void Insert(int index, T item, object updatedAt) + { + this.InternalInsertAt(index, item, updatedAt); + } + + /// + /// Adds the range. + /// + /// The new values. + /// The updatedat object. + /// Index of the last appended object + public int AddRange(IEnumerable newValues, object timestamp = null) + { + var edit = this.GetProviderAsEditable(); + + var index = -1; + var items = new List(); + + foreach (var item in newValues) + { + items.Add(item); + index = edit.OnAppend(item, timestamp); + if (this.IsSourceObservable) continue; + + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); + this.RaiseCollectionChangedEvent(args); + } + + this.OnCountTouched(); + + return index; + } + + #endregion Extended CRUD operators that take into account the DateTime of the change + + #region IList Implementation + + /// + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// + /// The position into which the new element was inserted, or -1 to indicate that the item was not inserted into the + /// collection. + /// + public int Add(object value) + { + return this.InternalAdd((T) value, null); + } + + bool IObservableCollection.Remove(object item) + { + this.Remove(item); + return true; + } + + bool IObservableCollection.Remove(object item) + { + this.Remove(item); + return true; + } + + /// + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if the is found in the ; otherwise, + /// false. + /// + public bool Contains(object value) + { + return value != null && this.Contains((T) value); + } + + /// + /// + /// Determines the index of a specific item in the . + /// + /// The object to locate in the . + /// + /// The index of if found in the list; otherwise, -1. + /// + public int IndexOf(object value) + { + return this.IndexOf((T) value); + } + + /// + /// + /// Inserts an item to the at the specified index. + /// + /// The zero-based index at which should be inserted. + /// The object to insert into the . + public void Insert(int index, object value) + { + this.Insert(index, (T) value); + } + + /// + /// + /// Gets a value indicating whether the has a fixed size. + /// + /// true if the has a fixed size; otherwise, false. + public bool IsFixedSize => false; + + void IObservableCollection.Add(object item) + { + this.Add(item); + } + + /// + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + public void Remove(object value) + { + this.Remove((T) value); + } + + /// + /// + /// Removes the item at the specified index. + /// + /// The zero-based index of the item to remove. + public void RemoveAt(int index) + { + this.InternalRemoveAt(index); + } + + /// + /// + /// Gets or sets the element at the specified index. + /// + /// The index. + /// + object IList.this[int index] + { + get => this.InternalGetValue(index, this.DefaultSelectionContext); + set => this.InternalSetValue(index, (T) value); + } + + #endregion IList Implementation + + #region IList Implementation + + /// + /// + /// Determines the index of a specific item in the . + /// + /// The object to locate in the . + /// + /// The index of if found in the list; otherwise, -1. + /// + public int IndexOf(T item) + { + return this.InternalIndexOf(item); + } + + /// + /// + /// Inserts an item to the at the specified index. + /// + /// The zero-based index at which should be inserted. + /// The object to insert into the . + public void Insert(int index, T item) + { + this.InternalInsertAt(index, item); + } + + /// + /// + /// Gets or sets the element at the specified index. + /// + /// The index. + /// + public T this[int index] + { + get => this.InternalGetValue(index, this.DefaultSelectionContext); + set => this.InternalSetValue(index, value); + } + + #endregion IList Implementation + + #region Public Properties + + /// + /// Gets or sets the provider if its asynchronous. + /// + /// + /// The provider asynchronous. + /// + public IItemSourceProviderAsync ProviderAsync + { + get => this._providerAsync; + set + { + this.ClearCountChangedHooks(); + this._providerAsync = value; + if (this._providerAsync is INotifyCountChanged) + { + (this._providerAsync as INotifyCountChanged).CountChanged += + this.VirtualizingObservableCollection_CountChanged; + } + } + } + + private void VirtualizingObservableCollection_CountChanged(object sender, CountChangedEventArgs args) + { + if (args.NeedsReset) + { + // Send a reset.. + this.RaiseCollectionChangedEvent(CcResetArgs); + } + + this.OnCountTouched(); + } + + /// + /// Gets or sets the provider if its not asynchronous. + /// + /// + /// The provider. + /// + public IItemSourceProvider Provider + { + get => this._provider; + set + { + this.ClearCountChangedHooks(); + this._provider = value; + + if (this._provider is INotifyCountChanged changed) + { + changed.CountChanged += + this.VirtualizingObservableCollection_CountChanged; + } //TODO check this commented code + + //if(this._provider is INotifyCollectionChanged) { + // (this._provider as INotifyCollectionChanged).CollectionChanged += this.VirtualizingObservableCollection_CollectionChanged; + //} + } + } + + private void VirtualizingObservableCollection_CollectionChanged(object sender, + NotifyCollectionChangedEventArgs e) + { + this.RaiseCollectionChangedEvent(e); + this.OnCountTouched(); + } + + private void ClearCountChangedHooks() + { + if (this._provider is INotifyCountChanged changed) + { + changed.CountChanged -= + this.VirtualizingObservableCollection_CountChanged; + } + + if (this._providerAsync is INotifyCountChanged) + { + (this._providerAsync as INotifyCountChanged).CountChanged -= + this.VirtualizingObservableCollection_CountChanged; + } + + //TODO check this commented code + //if(this._provider is INotifyCollectionChanged) { + // (this._provider as INotifyCollectionChanged).CollectionChanged -= this.VirtualizingObservableCollection_CollectionChanged; + //} + //if(this._providerAsync is INotifyCollectionChanged) { + // (this._providerAsync as INotifyCollectionChanged).CollectionChanged -= this.VirtualizingObservableCollection_CollectionChanged; + //} + } + + #endregion Public Properties + + #region INotifyCollectionChanged Implementation + + public bool SupressEventErrors { get; set; } = false; + + public event NotifyCollectionChangedEventHandler CollectionChanged; + + event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged + { + add => this.CollectionChanged += value; + remove => this.CollectionChanged -= value; + } + + /// + /// Raises the collection changed event. + /// + /// The instance containing the event data. + internal void RaiseCollectionChangedEvent(NotifyCollectionChangedEventArgs args) + { + if (this._bulkCount > 0) + { + return; + } + + var eventHandler = this.CollectionChanged; + if (eventHandler == null) + { + return; + } + + // Walk thru invocation list. + var delegates = eventHandler.GetInvocationList(); + + foreach (var @delegate in delegates) + { + var handler = (NotifyCollectionChangedEventHandler) @delegate; + + // If the subscriber is a DispatcherObject and different thread. + var dispatcherObject = handler.Target as DispatcherObject; + try + { + if (dispatcherObject != null && !dispatcherObject.CheckAccess()) + { + // Invoke handler in the target dispatcher's thread... + // asynchronously for better responsiveness. + dispatcherObject.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, handler, this, args); + } + else + { + // Execute handler as is. + handler(this, args); + } + } + catch (Exception ex + ) //WTF? exception catch during remove operations with collection, try add and remove investigation + { + Debug.WriteLine(ex.Message); + Debugger.Break(); + } + } + } + + #endregion INotifyCollectionChanged Implementation + + #region INotifyPropertyChanged implementation + + public event PropertyChangedEventHandler PropertyChanged; + + event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged + { + add => this.PropertyChanged += value; + remove => this.PropertyChanged -= value; + } + + private static readonly PropertyChangedEventArgs PcCountArgs = new PropertyChangedEventArgs("Count"); + + private static readonly NotifyCollectionChangedEventArgs CcResetArgs = + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); + + private void OnCountTouched() + { + this.RaisePropertyChanged(PcCountArgs); + } + + protected void RaisePropertyChanged(PropertyChangedEventArgs args) + { + if (this._bulkCount > 0) + { + return; + } + + var evnt = this.PropertyChanged; + evnt?.Invoke(this, args); + } + + #endregion INotifyPropertyChanged implementation + + #region Bulk Operation implementation + + /// + /// Releases the bulk mode. + /// + internal void ReleaseBulkMode() + { + if (this._bulkCount > 0) + { + this._bulkCount--; + } + + if (this._bulkCount != 0) return; + + this.RaiseCollectionChangedEvent(CcResetArgs); + this.RaisePropertyChanged(PcCountArgs); + } + + /// + /// Enters the bulk mode. + /// + /// + public BulkMode EnterBulkMode() + { + this._bulkCount++; + + return new BulkMode(this); + } + + /// + /// + /// The Bulk mode IDisposable proxy + /// + public class BulkMode : IDisposable + { + private readonly VirtualizingObservableCollection _voc; + private bool _isDisposed; + + public BulkMode(VirtualizingObservableCollection voc) + { + this._voc = voc; + } + + public void Dispose() + { + this.OnDispose(); + } + + private void OnDispose() + { + if (this._isDisposed) return; + + this._isDisposed = true; + this._voc?.ReleaseBulkMode(); + } + + ~BulkMode() + { + this.OnDispose(); + } + } + + #endregion Bulk Operation implementation + + #region Private Properties + + protected string DefaultSelectionContext = new Guid().ToString(); + private IItemSourceProvider _provider; + private IItemSourceProviderAsync _providerAsync; + private int _bulkCount; + + #endregion Private Properties + + #region Internal implementation + + /// + /// Gets the provider as editable. + /// + /// + /// + protected IEditableProvider GetProviderAsEditable() + { + IEditableProvider ret = null; + + if (this.Provider != null) + { + ret = this.Provider as IEditableProvider; + } + else + { + ret = this.ProviderAsync as IEditableProvider; + } + + if (ret == null) + { + throw new NotSupportedException(); + } + + return ret; + } + + /// + /// Replaces oldValue with newValue at index if updatedat is newer or null. + /// + /// The index. + /// The old value. + /// The new value. + /// The timestamp. + internal void ReplaceAt(int index, T oldValue, T newValue, object timestamp) + { + var edit = this.GetProviderAsEditable(); + + if (edit == null) return; + edit.OnReplace(index, oldValue, newValue, timestamp); + + if (this.IsSourceObservable) return; + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue, + oldValue, index); + this.RaiseCollectionChangedEvent(args); + } + + private void InternalClear() + { + if (this.Provider != null) + { + (this.Provider as IProviderPreReset)?.OnBeforeReset(); + + this.Provider.OnReset(-1); + } + else + { + if (this.ProviderAsync is IProviderPreReset) + { + (this.ProviderAsync as IProviderPreReset).OnBeforeReset(); + } + + this.ProviderAsync.OnReset(-1); + } + } + + private CancellationTokenSource _resetToken; + + public void Reset() + { + this.ResetAsync().Wait(); + } + + public async Task ResetAsync() + { + CancellationTokenSource cts = null; + + lock (this) + { + if (this._resetToken != null) + { + this._resetToken.Cancel(); + this._resetToken = null; + } + + cts = this._resetToken = new CancellationTokenSource(); + } + + if (this.Provider != null) + { + if (this.Provider is IProviderPreReset reset) + { + reset.OnBeforeReset(); + if (cts.IsCancellationRequested) + { + return; + } + } + //TODO find why this was commented on git? + //this.Provider.OnReset(-2); + + await Task.Run(async () => + { + if (this.Provider is IAsyncResetProvider provider) + { + var count = await provider.GetCountAsync(); + if (!cts.IsCancellationRequested) + { + VirtualizationManager.Instance.RunOnUi(() => + this.Provider.OnReset(count)); + } + } + else + { + var count = this.Provider.GetCount(false); + if (!cts.IsCancellationRequested) + { + VirtualizationManager.Instance.RunOnUi(() => + this.Provider.OnReset(count)); + } + } + }, cts.Token); + } + else + { + if (this.ProviderAsync is IProviderPreReset) + { + (this.ProviderAsync as IProviderPreReset).OnBeforeReset(); + } + + this.ProviderAsync.OnReset(await this.ProviderAsync.Count); + } + + lock (this) + { + if (this._resetToken == cts) + { + this._resetToken = null; + } + } + } + + private T InternalGetValue(int index, string selectionContext) + { + var allowPlaceholder = selectionContext == this.DefaultSelectionContext; + + if (this.Provider != null) + { + return this.Provider.GetAt(index, this, allowPlaceholder); + } + + return Task.Run(() => this.ProviderAsync.GetAt(index, this, allowPlaceholder)).GetAwaiter().GetResult(); + } + + private T InternalSetValue(int index, T newValue) + { + var oldValue = this.InternalGetValue(index, this.DefaultSelectionContext); + var edit = this.GetProviderAsEditable(); + edit.OnReplace(index, oldValue, newValue, null); + + var newItems = new List {newValue}; + var oldItems = new List {oldValue}; + + if (this.IsSourceObservable) return oldValue; + + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItems, + oldItems, index); + this.RaiseCollectionChangedEvent(args); + + return oldValue; + } + + private int InternalAdd(T newValue, object timestamp) + { + var edit = this.GetProviderAsEditable(); + var index = edit.OnAppend(newValue, timestamp); + this.OnCountTouched(); + + if (this.IsSourceObservable) return index; + + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newValue, index); + this.RaiseCollectionChangedEvent(args); + + return index; + } + + private int InternalGetCount() + { + var ret = 0; + + ret = this.Provider?.GetCount(true) ?? this.ProviderAsync.Count.GetAwaiter().GetResult(); + + return ret; + } + + private void InternalInsertAt(int index, T item, object timestamp = null) + { + var edit = this.GetProviderAsEditable(); + edit.OnInsert(index, item, timestamp); + + this.OnCountTouched(); + + if (!this.IsSourceObservable) + { + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index); + this.RaiseCollectionChangedEvent(args); + } + } + + private bool InternalRemoveAt(int index, object timestamp = null) + { + if (!(this.Provider is IEditableProviderIndexBased edit)) return false; + + var oldValue = edit.OnRemove(index, timestamp); + + this.OnCountTouched(); + + if (this.IsSourceObservable) return true; + + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldValue, index); + this.RaiseCollectionChangedEvent(args); + + return true; + } + + private bool InternalRemove(T item, object timestamp = null) + { + if (item == null) + { + return false; + } + + if (!(this.Provider is IEditableProviderItemBased edit)) + return false; + var index = edit.OnRemove(item, timestamp); + this.OnCountTouched(); + + if (this.IsSourceObservable) return true; + + var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index); + this.RaiseCollectionChangedEvent(args); + + return true; + } + + private int InternalIndexOf(T item) + { + return this.Provider?.IndexOf(item) ?? + Task.Run(() => this.ProviderAsync.IndexOf(item)).GetAwaiter().GetResult(); + } + + private void EnsureCountIsGotNonaSync() + { + this.Provider?.GetCount(false); + } + + #endregion Internal implementation + } +} \ No newline at end of file