Click here to Skip to main content
15,896,557 members
Articles / Desktop Programming / WPF

Wrap Panel Virtualization

Rate me:
Please Sign up or sign in to vote.
4.95/5 (18 votes)
2 Jan 2012CPOL2 min read 53.8K   5.6K   41  
WrapPanel doesn't support virtualization. But we can improve the performance by simulating virtualization.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Threading;

namespace MefBasic.Threading
{
    public class Job 
    {
        private int _maxWaitTimeToStop;
        public Action<Job> JobAction { get; private set; }
        public Action<JobProgressArgs> ProgressNotifyAction { get; private set; }
        public event EventHandler JobCompleted;
        public event CancelEventHandler StartingJob;
        protected List<string> DependentJobs { get; set; }
        protected void InvokeStartingJob(CancelEventArgs e)
        {
            if (StartingJob != null) 
                StartingJob(this, e);
        }

        public bool IsThreadSafe { get; set; }
        public JobStore Store { get; set; }
        public string Id { get; set; }
        public AutoResetEvent WaitEvent { private set; get; }

        public Job(Action<Job> jobAction)
            : this(jobAction, null)
        {
            MaxWaitTimeToStop = 5000;
        }
        public Job Dependency(params string [] dependentJobs)
        {
            DependentJobs.AddRange(dependentJobs);
            return this;
        }
        public Job(Action<Job> jobAction, Action<JobProgressArgs> progressNotifyAction)
        {
            ProgressNotifyAction = progressNotifyAction;
            JobAction = jobAction;
            Id = Guid.NewGuid().ToString();
            WaitEvent = new AutoResetEvent(false);
            Store = new JobStore {Status = JobStatus.NotStarted};
            ChildreenJob = new List<Job>();
            IsThreadSafe = true;
            DependentJobs=new List<string>();
        }

        
        protected void OnJobCompleted(EventArgs e)
        {
            switch (Store.Status)
            {
                case JobStatus.AbortRequested:
                    Store.Status = JobStatus.Aborted;
                    break;
                case JobStatus.Started:
                    Store.Status = JobStatus.Completed;
                    break;
            }
            IsComplete = true;
            if (JobCompleted != null)
                JobCompleted(this, e);
        }
        
        protected void OnProgressAction(JobProgressArgs args)
        {
            if (ProgressNotifyAction != null)
            {
                ProgressNotifyAction(args);
            }
        }

        public virtual void Start()
        {
            if(InvokeStartingJob())
                return;
            DoStart();
        }

        public void Resume()
        {
            if (Store.PauseRequested)
            {
                Store.PauseRequested = false;
                Store.Status = JobStatus.Started;
                return;
            }
        }

        protected void DoStart()
        {
            Store.Status = JobStatus.Started;
            WaitEvent.Reset();
            if (IsThreadSafe)
            {
                
                var worker = new BackgroundWorker();
                worker.DoWork += (s, e) => DoWork(e.Argument);
                worker.RunWorkerCompleted += (s, e) =>
                                                  {
                                                      WaitEvent.Set();
                                                      OnJobCompleted(new EventArgs());
                                                  };
                worker.RunWorkerAsync(this);
            }
            else
            {
                DoWork(this);
                WaitEvent.Set();
                OnJobCompleted(new EventArgs());
            }
        }

        protected bool InvokeStartingJob()
        {
            var cancelEventArgs = new CancelEventArgs(false);
            InvokeStartingJob(cancelEventArgs);
            var isCancelled = cancelEventArgs.Cancel;
            if(isCancelled)
            {
                Store.Status = JobStatus.Aborted;
                WaitEvent.Set();
                OnJobCompleted(new EventArgs());
            }
            return isCancelled;
        }

        private void DoWork(object args)
        {
            WaitForDependentJobs();
            var job = (Job) args;
            if (ChildreenJob.Count > 0)
            {
                foreach (var childJob in ChildreenJob)
                {
                    childJob.Start();
                }
                ChildreenJob.ForEach(c=>c.Wait());
            }
            if(JobAction!=null)
                JobAction(job);
        }

        protected void WaitForDependentJobs()
        {
            foreach (var dependentJob in DependentJobs)
            {
                Parent.GetChild(dependentJob).Wait();
            }
        }

        public bool IsComplete { get; protected set; }
        public void Wait()
        {
            if (IsComplete) return;
            if (IsComplete == false)
            {
                WaitEvent.WaitOne();
                WaitEvent.Set();
            }
        }

        protected List<Job> ChildreenJob { get; set; }

        public void AddChild(Job childJob)
        {
            childJob.Parent = this;
            ChildreenJob.Add(childJob);
        }

        public Job Parent { get; set; }

        public Job Root
        {
            get
            {
                return Parent == null ? this : Parent.Root;
            }
        }

        public int MaxWaitTimeToStop
        {
            get { return _maxWaitTimeToStop; }
            set { _maxWaitTimeToStop = value; }
        }

        public IEnumerable<Job> GetChildreen()
        {
            return ChildreenJob;
        }

        public void NotifyProgress()
        {
            OnProgressAction(new JobProgressArgs { Sender = this });
        }

        public void Abort()
        {
            Store.Status = JobStatus.AbortRequested;
            ChildreenJob.ForEach(c=>c.Abort());
        }

        public Job GetChild(string childId)
        {
            return GetChildreen().FirstOrDefault(child => child.Id == childId);
        }

        public void NotifyProgress(int progress)
        {
            OnProgressAction(new JobProgressArgs { Sender = this,Progress = progress});
        }

        public void StartBlocked()
        {
            var worker = new BackgroundWorkerEx();
            worker.DoWork += (s, e) =>
                                 {
                                     Start();
                                     Wait();
                                 };
            worker.RunWorkerBlocked();
        }

        public Job GetSibling(string id)
        {
            return Parent.GetChild(id);
        }

        public void Stop()
        {
            Store.StopRequested = true;
            var stopWatch = new Stopwatch();
            stopWatch.Start();
            while (!IsComplete&&stopWatch.ElapsedMilliseconds<MaxWaitTimeToStop)
            {
                DoEvents();
            }
        }
        private static void DoEvents()
        {
            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { }));
        }

        public void Pause()
        {
            Store.PauseRequested = true;
            var stopWatch = new Stopwatch();
            stopWatch.Start();
            while (Store.Status!=JobStatus.Paused && stopWatch.ElapsedMilliseconds < MaxWaitTimeToStop)
            {
                DoEvents();
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) KAZ Software Limited
Bangladesh Bangladesh
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions