// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using System.Windows.Input; using NuGet; namespace ICSharpCode.PackageManagement { public abstract class PackagesViewModel : ViewModelBase { DelegateCommand showNextPageCommand; DelegateCommand showPreviousPageCommand; DelegateCommand showPageCommand; DelegateCommand searchCommand; ObservableCollection packageViewModels = new ObservableCollection(); Pages pages = new Pages(); IPackageManagementService packageManagementService; IPackageViewModelFactory packageViewModelFactory; ITaskFactory taskFactory; IEnumerable allPackages; string searchTerms; bool isReadingPackages; ITask task; int totalItems; bool hasError; string errorMessage = String.Empty; public PackagesViewModel(IPackageManagementService packageManagementService, ITaskFactory taskFactory) : this( packageManagementService, new LicenseAcceptanceService(), taskFactory) { } public PackagesViewModel( IPackageManagementService packageManagementService, ILicenseAcceptanceService licenseAcceptanceService, ITaskFactory taskFactory) : this( packageManagementService, new PackageViewModelFactory(packageManagementService, licenseAcceptanceService), taskFactory) { } public PackagesViewModel( IPackageManagementService packageManagementService, IPackageViewModelFactory packageViewModelFactory, ITaskFactory taskFactory) { this.packageManagementService = packageManagementService; this.packageViewModelFactory = packageViewModelFactory; this.taskFactory = taskFactory; CreateCommands(); } void CreateCommands() { showNextPageCommand = new DelegateCommand(param => ShowNextPage()); showPreviousPageCommand = new DelegateCommand(param => ShowPreviousPage()); showPageCommand = new DelegateCommand(param => ExecuteShowPageCommand(param)); searchCommand = new DelegateCommand(param => Search()); } public ICommand ShowNextPageCommand { get { return showNextPageCommand; } } public ICommand ShowPreviousPageCommand { get { return showPreviousPageCommand; } } public ICommand ShowPageCommand { get { return showPageCommand; } } public ICommand SearchCommand { get { return searchCommand; } } public bool HasError { get { return hasError; } } public string ErrorMessage { get { return errorMessage; } } public ObservableCollection PackageViewModels { get { return packageViewModels; } set { packageViewModels = value; } } public IPackageManagementService PackageManagementService { get { return packageManagementService; } } public bool IsReadingPackages { get { return isReadingPackages; } } public void ReadPackages() { allPackages = null; pages.SelectedPageNumber = 1; UpdateRepositoryBeforeReadPackagesTaskStarts(); StartReadPackagesTask(); } void StartReadPackagesTask() { isReadingPackages = true; hasError = false; ClearPackages(); CancelReadPackagesTask(); CreateReadPackagesTask(); task.Start(); } protected virtual void UpdateRepositoryBeforeReadPackagesTaskStarts() { } void CancelReadPackagesTask() { if (task != null) { task.Cancel(); } } void CreateReadPackagesTask() { task = taskFactory.CreateTask( () => GetPackagesForSelectedPageResult(), (result) => OnPackagesReadForSelectedPage(result)); } PackagesForSelectedPageResult GetPackagesForSelectedPageResult() { IEnumerable packages = GetPackagesForSelectedPage(); return new PackagesForSelectedPageResult(packages, totalItems); } void OnPackagesReadForSelectedPage(ITask task) { isReadingPackages = false; if (task.IsFaulted) { SaveError(task.Exception); } else if (task.IsCancelled) { // Ignore } else { UpdatePackagesForSelectedPage(task.Result); } base.OnPropertyChanged(null); } protected void SaveError(AggregateException ex) { SaveError(ex.InnerException); } protected void SaveError(Exception ex) { hasError = true; errorMessage = ex.Message; } void UpdatePackagesForSelectedPage(PackagesForSelectedPageResult result) { pages.TotalItems = result.TotalPackages; pages.TotalItemsOnSelectedPage = result.TotalPackagesOnPage; UpdatePackageViewModels(result.Packages); } void PagesChanged(object sender, NotifyCollectionChangedEventArgs e) { StartReadPackagesTask(); base.OnPropertyChanged(null); } IEnumerable GetPackagesForSelectedPage() { IEnumerable filteredPackages = GetFilteredPackagesBeforePagingResults(); return GetPackagesForSelectedPage(filteredPackages); } IEnumerable GetFilteredPackagesBeforePagingResults() { if (allPackages == null) { IQueryable packages = GetAllPackages(); packages = OrderPackages(packages); packages = packages.Find(searchTerms); totalItems = packages.Count(); allPackages = GetFilteredPackagesBeforePagingResults(packages); } return allPackages; } IQueryable OrderPackages(IQueryable packages) { return packages .OrderBy(package => package.Id); } IEnumerable GetPackagesForSelectedPage(IEnumerable allPackages) { int packagesToSkip = pages.ItemsBeforeFirstPage; return allPackages .Skip(packagesToSkip) .Take(pages.PageSize); } /// /// Returns all the packages. /// protected virtual IQueryable GetAllPackages() { return null; } /// /// Allows filtering of the packages before paging the results. Call base class method /// to run default filtering. /// protected virtual IEnumerable GetFilteredPackagesBeforePagingResults(IQueryable allPackages) { IEnumerable bufferedPackages = GetBufferedPackages(allPackages); return bufferedPackages; } IEnumerable GetBufferedPackages(IQueryable allPackages) { return allPackages.AsBufferedEnumerable(30); } void UpdatePackageViewModels(IEnumerable packages) { IEnumerable currentViewModels = ConvertToPackageViewModels(packages); UpdatePackageViewModels(currentViewModels); } void UpdatePackageViewModels(IEnumerable newPackageViewModels) { ClearPackages(); PackageViewModels.AddRange(newPackageViewModels); } void ClearPackages() { PackageViewModels.Clear(); } public IEnumerable ConvertToPackageViewModels(IEnumerable packages) { foreach (IPackage package in packages) { yield return CreatePackageViewModel(package); } } PackageViewModel CreatePackageViewModel(IPackage package) { return packageViewModelFactory.CreatePackageViewModel(package); } public int SelectedPageNumber { get { return pages.SelectedPageNumber; } set { if (pages.SelectedPageNumber != value) { pages.SelectedPageNumber = value; StartReadPackagesTask(); base.OnPropertyChanged(null); } } } public int PageSize { get { return pages.PageSize; } set { pages.PageSize = value; } } public bool IsPaged { get { return pages.IsPaged; } } public ObservableCollection Pages { get { return pages; } } public bool HasPreviousPage { get { return pages.HasPreviousPage; } } public bool HasNextPage { get { return pages.HasNextPage; } } public int MaximumSelectablePages { get { return pages.MaximumSelectablePages; } set { pages.MaximumSelectablePages = value; } } public int TotalItems { get { return totalItems; } } public void ShowNextPage() { SelectedPageNumber += 1; } public void ShowPreviousPage() { SelectedPageNumber -= 1; } void ExecuteShowPageCommand(object param) { int pageNumber = (int)param; ShowPage(pageNumber); } public void ShowPage(int pageNumber) { SelectedPageNumber = pageNumber; } public bool IsSearchable { get; set; } public string SearchTerms { get { return searchTerms; } set { searchTerms = value; } } public void Search() { ReadPackages(); OnPropertyChanged(null); } public bool ShowPackageSources { get; set; } public IEnumerable PackageSources { get { return packageManagementService.Options.PackageSources; } } public PackageSource SelectedPackageSource { get { return packageManagementService.ActivePackageSource; } set { if (packageManagementService.ActivePackageSource != value) { packageManagementService.ActivePackageSource = value; ReadPackages(); OnPropertyChanged(null); } } } } }