/// <summary>
        /// Refreshes the work items in the document.
        /// </summary>
        /// <remarks>
        /// <para>Deletes work items that are no longer returned, adds work items that are now returned, and re-orders work items that have changed position in query results.</para>
        /// <para>This call will also update the rich text content controls which are not bound to the Custom XML Parts.</para>
        /// </remarks>
        /// <param name="refreshData">The data needed for a refresh.</param>
        /// <param name="bookmarkNamingFunction">A function to return the bookmark to use for the given work item, first parameter is query index, second is work item id.</param>
        /// <param name="cancellationToken">Used to cancel the refresh.</param>
        public void RefreshWorkItems(FormatterRefreshData refreshData, Func <int, int, string> bookmarkNamingFunction, CancellationToken cancellationToken)
        {
            if (refreshData == null)
            {
                throw new ArgumentNullException("refreshData");
            }

            Debug.Assert(refreshData.QueryWorkItemsBefore.Count() == refreshData.QueryWorkItemsAfter.Count(), "Before and after data have different numbers of queries");
            Debug.Assert(refreshData.QueryWorkItemsBefore.Count() == refreshData.Layouts.Count(), "Number of layouts does not match number of queries");
            Debug.Assert(refreshData.QueryWorkItemsBefore.Count() == refreshData.QueryIsFlat.Count(), "Number of QueryIsFlat values does not match number of queries");

            DateTime start = DateTime.Now;

            CustomXMLPart part = this.wordDocument.GetXmlPart(Constants.WorkItemNamespace);

            int n = refreshData.QueryWorkItemsBefore.Count();

            QueryWorkItems[]    queryWorkItemsBefore = refreshData.QueryWorkItemsBefore.ToArray();
            QueryWorkItems[]    queryWorkItemsAfter  = refreshData.QueryWorkItemsAfter.ToArray();
            bool[]              queryIsFlat          = refreshData.QueryIsFlat.ToArray();
            LayoutInformation[] layouts = refreshData.Layouts.ToArray();
            for (int queryIndex = 0; queryIndex < n; queryIndex++)
            {
                Func <int, string> queryBookmarkNamingFunction = (int id) => bookmarkNamingFunction(queryIndex, id);
                this.logger.Log(TraceEventType.Verbose, "Refreshing query {0}", queryIndex);

                // Compute the actual before work items that are still in the document.
                QueryWorkItems actualBeforeWorkItems = new QueryWorkItems(queryIndex, queryWorkItemsBefore[queryIndex].WorkItemIds.Where(id => this.FindBookmark(queryBookmarkNamingFunction(id)) != null).ToArray());

                bool isManuallySorted = this.IsManuallySorted(queryWorkItemsBefore[queryIndex].WorkItemIds, queryBookmarkNamingFunction);
                if (!queryIsFlat[queryIndex] || (queryIsFlat[queryIndex] && !isManuallySorted))
                {
                    this.SortExistingWorkItems(actualBeforeWorkItems, queryWorkItemsAfter[queryIndex], queryBookmarkNamingFunction, cancellationToken);
                    isManuallySorted = false;
                }

                IWorkItemLayout workItemLayout = new WorkItemLayout(layouts[queryIndex], this.teamProjectTemplate);
                this.AddNewWorkItems(actualBeforeWorkItems, queryWorkItemsAfter[queryIndex], workItemLayout, part, queryBookmarkNamingFunction, isManuallySorted, cancellationToken);
            }

            this.RefreshDeleteRemovedWorkItems(refreshData.QueryWorkItemsBefore.ToArray(), refreshData.QueryWorkItemsAfter.ToArray(), bookmarkNamingFunction, cancellationToken);
            this.RefreshRichTextContentControls(refreshData.WorkItemManager, cancellationToken);

            DateTime end = DateTime.Now;

            this.logger.Log(TraceEventType.Information, "Elapsed time to refresh was {0} seconds", (end - start).TotalSeconds);
        }
        /// <summary>
        /// Maps work items into the document using the given layout. Each work item is bookmarked.
        /// </summary>
        /// <param name="workItems">The work items to be mapped into the document.</param>
        /// <param name="layout">The layout to be used to map the saved work items.</param>
        /// <param name="bookmarkNamingFunction">A function to return the bookmark to use for the given work item id.</param>
        /// <param name="cancellationToken">Used to cancel the save.</param>
        /// <remarks>
        /// It is assumed that the document already contains the XML data for the work items.
        /// </remarks>
        public void MapWorkItemsIntoDocument(WorkItemTree workItems, LayoutInformation layout, Func <int, string> bookmarkNamingFunction, CancellationToken cancellationToken)
        {
            if (workItems == null)
            {
                throw new ArgumentNullException("workItems");
            }

            if (layout == null)
            {
                throw new ArgumentNullException("layout");
            }

            if (bookmarkNamingFunction == null)
            {
                throw new ArgumentNullException("bookmarkNamingFunction");
            }

            DateTime start = DateTime.Now;
            int      count = 0;

            CustomXMLPart part = this.wordDocument.GetXmlPart(Constants.WorkItemNamespace);

            if (this.wordDocument.IsAtStart())
            {
                this.wordDocument.InsertParagraph(ModelResources.FormatterStartOfImportBoilerPlate, Constants.NormalStyleName);
            }

            foreach (WorkItemTreeNode node in workItems.DepthFirstNodes())
            {
                cancellationToken.ThrowIfCancellationRequested();

                count++;
                IWorkItemLayout workItemLayout = new WorkItemLayout(layout, this.teamProjectTemplate);

                this.MapWorkItemIntoDocument(node, workItemLayout, bookmarkNamingFunction, part, InsertionPoint.CurrentLocation, 0);
            }

            if (this.wordDocument.IsAtEnd())
            {
                this.wordDocument.InsertParagraph(ModelResources.FormatterEndOfImportBoilerPlate, Constants.NormalStyleName);
            }

            DateTime end = DateTime.Now;

            this.logger.Log(TraceEventType.Information, "Elapsed time to insert {0} items was {1} seconds", count, (end - start).TotalSeconds);
        }