Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / MS / Internal / documents / PageCache.cs / 1 / PageCache.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: PageCache caches information about individual pages in a document. // // History: // 10/21/04 - jdersch created // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Windows; using System.Windows.Threading; using System.Windows.Documents; namespace MS.Internal.Documents { ////// PageCache acts as both a page-dimension cache and a proxy for an DocumentPaginator document. /// It doles out pages to DocumentGrid and keeps the cache in [....] with the DocumentPaginator. /// ///http://d2/DRX/default.aspx internal class PageCache { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Default constructor for a PageCache object. /// Creates our internal cache, represented as a Generic List. /// public PageCache() { //Create the internal representation of our Cache with a default size. //This cache is a dynamic List which will expand to accommodate larger documents. _cache = new List(_defaultCacheSize); //Create the PageDestroyedWatcher which will keep track of what DocumentPages //have been destroyed. _pageDestroyedWatcher = new PageDestroyedWatcher(); } #endregion Constructors //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- #region Public Properties /// /// The DocumentPaginator Content tree we're interested in caching information about. /// ///public DynamicDocumentPaginator Content { set { //If the content is actually changing we update our paginator here. if (_documentPaginator != value) { //Reset our flags and default page size. _dynamicPageSizes = false; _defaultPageSize = _initialDefaultPageSize; _isDefaultSizeKnown = false; _isPaginationCompleted = false; //If the old DocumentPaginator is non-null, we need to //remove the old event handlers before we assign the new one. if (_documentPaginator != null) { _documentPaginator.PagesChanged -= new PagesChangedEventHandler(OnPagesChanged); _documentPaginator.GetPageCompleted -= new GetPageCompletedEventHandler(OnGetPageCompleted); _documentPaginator.PaginationProgress -= new PaginationProgressEventHandler(OnPaginationProgress); _documentPaginator.PaginationCompleted -= new EventHandler(OnPaginationCompleted); //Reset the Background Pagination flag to its original state. _documentPaginator.IsBackgroundPaginationEnabled = _originalIsBackgroundPaginationEnabled; } //Now assign the new paginator. _documentPaginator = value; //Clear our cache. ClearCache(); //Attach our event handlers and set relevant properties if the new content is non-null. if (_documentPaginator != null) { _documentPaginator.PagesChanged += new PagesChangedEventHandler(OnPagesChanged); _documentPaginator.GetPageCompleted += new GetPageCompletedEventHandler(OnGetPageCompleted); _documentPaginator.PaginationProgress += new PaginationProgressEventHandler(OnPaginationProgress); _documentPaginator.PaginationCompleted += new EventHandler(OnPaginationCompleted); //Set the paginator's PageSize so the new content will reflow to fit in the requested space. _documentPaginator.PageSize = _defaultPageSize; //We save off the original value so we can restore it when new content is assigned. _originalIsBackgroundPaginationEnabled = _documentPaginator.IsBackgroundPaginationEnabled; //Enable background pagination, and set the paginator's PageSize so the new content will //reflow to fit in the requested space. _documentPaginator.IsBackgroundPaginationEnabled = true; //Determine content flow direction, if the content has one specified. //We look for the FrameworkElement.FlowDirection property. //If it doesn't have one then we assume the content is Left-To-Right. //(Note: FlowDirection is a value type and thus cannot be null; //DependencyObject.GetValue will never return null for this even if the //DocumentPaginator does not have this property set -- it will just //return the default value for FlowDirectionProperty.) if (_documentPaginator.Source is DependencyObject) { FlowDirection flowDirection = (FlowDirection)((DependencyObject)_documentPaginator.Source).GetValue(FrameworkElement.FlowDirectionProperty); if (flowDirection == FlowDirection.LeftToRight) { _isContentRightToLeft = false; } else { _isContentRightToLeft = true; } } } //If the content is already paginated (as is the case for certain Fixed content) //we'll call OnPaginationProgress here to let the Cache (and any listeners) know that //some or all the pages are available. if ( _documentPaginator != null ) { if (_documentPaginator.PageCount > 0) { OnPaginationProgress(_documentPaginator, new PaginationProgressEventArgs(0, _documentPaginator.PageCount)); } if ( _documentPaginator.IsPageCountValid ) { OnPaginationCompleted(_documentPaginator, EventArgs.Empty); } } } } get { //Just return our current paginator. return _documentPaginator; } } /// /// The number of pages in the cache. /// ///public int PageCount { get { return _cache.Count; } } /// /// Based on current knowledge, reports whether the document consists /// entirely of pages with the same dimensions or whether page sizes vary /// (i.e. are 'Dynamic') /// ///public bool DynamicPageSizes { get { return _dynamicPageSizes; } } /// /// Indicates whether the content has an RTL flowdirection or not. /// ///public bool IsContentRightToLeft { get { return _isContentRightToLeft; } } public bool IsPaginationCompleted { get { return _isPaginationCompleted; } } #endregion Public Properties //------------------------------------------------------ // // Public Events // //------------------------------------------------------ #region Public Events /// /// Fired when one ore more pages in the document have been paginated. /// public event PaginationProgressEventHandler PaginationProgress; ////// Fired when the document is finished paginating. /// public event EventHandler PaginationCompleted; ////// Fired when one or more pages in the document have changed. /// public event PagesChangedEventHandler PagesChanged; ////// Fired when a requested page has been retrieved. /// public event GetPageCompletedEventHandler GetPageCompleted; ////// Fired when one or more entries in the PageCache have been updated for any reason /// (Due to pagination, a new default page size, page retrieval, etc...) /// public event PageCacheChangedEventHandler PageCacheChanged; #endregion Public Events //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ #region Public Methods ////// Retrieves the cached size of a page in the document, even if the page is /// marked as "dirty." Pages are only un-dirtied when GetPage() is called /// (that is, as pages are actually retrieved from the IDocumentFormatter). /// /// The pagenumber to return the dimensions of. ///The dimensions of the requested page, or (0,0) for nonexistent pages. public Size GetPageSize(int pageNumber) { if (pageNumber >= 0 && pageNumber < _cache.Count) { Size pageSize = _cache[pageNumber].PageSize; Invariant.Assert(pageSize != Size.Empty, "PageCache entry's PageSize is Empty."); return pageSize; } else { return new Size(0, 0); } } // ///// /// --- Commenting out this method as it is currently unused. --- // /// Retrieves a page from the DocumentPaginator Asynchronously. // /// Caller will receive a GetPageCompleted event when the page is actually available, // /// and the Cache will be updated to reflect the true page dimensions at that time. // /// // /// The page to retrieve from the DocumentPaginator // ///Nothing. // public void GetPage(int pageNumber) // { // if (_documentPaginator != null) // { // _documentPaginator.GetPageAsync(pageNumber, (object)pageNumber); // } // } ////// Retrieves the "Dirty" bit for the associated page. /// /// The page to retrive the Dirty bit for ///The dirty bit public bool IsPageDirty(int pageNumber) { if (pageNumber >= 0 && pageNumber < _cache.Count) { return _cache[pageNumber].Dirty; } else { return true; } } #endregion Public Methods //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- #region Private Methods ////// Handler for the OnPaginationProgress event fired by the DocumentPaginator. /// /// /// private void OnPaginationProgress(object sender, PaginationProgressEventArgs args) { //Since handling the PaginationProgress event entails a bit of work, we'll //have our dispatcher call the PaginationProgress event at Normal priority. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(PaginationProgressDelegate), args); } ////// Asynchronously handles the PaginationProgress event. /// This means that one or more pages have been added to the document, so we /// add any new pages to the cache, mark them as dirty, and fire off our PaginationProgress /// event. /// /// ///private object PaginationProgressDelegate(object parameter) { PaginationProgressEventArgs args = parameter as PaginationProgressEventArgs; if (args == null) { throw new InvalidOperationException("parameter"); } //Validate incoming parameters. ValidatePaginationArgs(args.Start, args.Count); if (_isPaginationCompleted) { if (args.Start == 0) { //Since we've started repaginating from the beginning of the document //after pagination was completed, we can't assume we know //the default page size anymore. _isDefaultSizeKnown = false; _dynamicPageSizes = false; } //Reset our IsPaginationCompleted flag since we just got a pagination event. _isPaginationCompleted = false; } //Check for integer overflow. if (args.Start + args.Count < 0) { throw new ArgumentOutOfRangeException("args"); } //Create our list of changes. We allocate space for 2 changes here //as we can have as many as two changes resulting from a Pagination event. List changes = new List (2); PageCacheChange change; //If we have pages to add or modify, do so now. if (args.Count > 0) { //If pagination has added new pages onto the end of the document, we //add new entries to our cache. if (args.Start >= _cache.Count) { //Completely new pages, so we add new cache entries change = AddRange(args.Start, args.Count); if (change != null) { changes.Add(change); } } else { //Pagination has updated some currently existing pages, so we'll //update our entries. if (args.Start + args.Count < _cache.Count) { //All pre-existing pages, so we just dirty the current cache entries. change = DirtyRange(args.Start, args.Count); if (change != null) { changes.Add(change); } } else { //Some pre-existing pages, some new. change = DirtyRange(args.Start, _cache.Count - args.Start); if (change != null) { changes.Add(change); } change = AddRange(_cache.Count, args.Count - (_cache.Count - args.Start) + 1); if (change != null) { changes.Add(change); } } } } //If the document's PageCount is now less than the size of our cache due to repagination //we remove the extra entries. int pageCount = _documentPaginator != null ? _documentPaginator.PageCount : 0; if (pageCount < _cache.Count) { change = new PageCacheChange(pageCount, _cache.Count - pageCount, PageCacheChangeType.Remove); changes.Add(change); //Remove the pages from the cache. _cache.RemoveRange(pageCount, _cache.Count - pageCount); } //Fire off our PageCacheChanged event. FirePageCacheChangedEvent(changes); //Fire the PaginationProgress event. if (PaginationProgress != null) { PaginationProgress(this, args); } return null; } /// /// Handler for the OnPaginationCompleted event. Merely fires off our own event. /// /// The sender of this event /// The arguments associated with this event private void OnPaginationCompleted(object sender, EventArgs args) { //Since handling the PaginationCompleted event entails a bit of work, we'll //have our dispatcher call the PaginationCompleted event at Normal priority. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(PaginationCompletedDelegate), args); } ////// Asynchronously handles the PaginationCompleted event. /// /// ///private object PaginationCompletedDelegate(object parameter) { EventArgs args = parameter as EventArgs; if (args == null) { throw new ArgumentOutOfRangeException("parameter"); } //set our IsPaginationCompleted flag since we're done paginating. _isPaginationCompleted = true; //Fire the PaginationProgress event. if (PaginationCompleted != null) { PaginationCompleted(this, args); } return null; } /// /// Handler for the OnPagesChanged event fired by the DocumentPaginator. /// /// /// private void OnPagesChanged(object sender, PagesChangedEventArgs args) { //Since handling the PagesChanged event entails a bit of work, we'll //have our dispatcher call the PagesChangedDelegate at Normal priority. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(PagesChangedDelegate), args); } ////// Asynchronously handles the PagesChanged event. /// This means that one or more pages have been invalidated so we /// dirty their cache entries and fire off our PagesChanged event. /// /// ///private object PagesChangedDelegate(object parameter) { PagesChangedEventArgs args = parameter as PagesChangedEventArgs; if (args == null) { throw new ArgumentOutOfRangeException("parameter"); } //Validate incoming parameters ValidatePaginationArgs(args.Start, args.Count); //Start values outside the range of current pages are invalid. //if (args.Start >= _cache.Count) //{ // throw new ArgumentOutOfRangeException("args"); //} //If the last page specified in the change is out of the range of currently-known //pages we make the assumption that the IDP means to invalidate all pages and so //we clip the count into range. //We also take into account integer overflow... if the sum of Start+Count is less than //zero then we've overflowed. int adjustedCount = args.Count; if (args.Start + args.Count >= _cache.Count || args.Start + args.Count < 0) { adjustedCount = _cache.Count - args.Start; } //Create our list of changes. We can have at most one. List changes = new List (1); //Now make the change if there is one to make. if (adjustedCount > 0) { PageCacheChange change = DirtyRange(args.Start, adjustedCount); if (change != null) { changes.Add(change); } //Fire off our PageCacheChanged event. FirePageCacheChangedEvent(changes); } //Fire the PagesChanged event. if (PagesChanged != null) { PagesChanged(this, args); } return null; } /// /// Handler for the GetPageCompleted event fired by the DocumentPaginator. /// /// /// private void OnGetPageCompleted(object sender, GetPageCompletedEventArgs args) { if (!args.Cancelled && args.Error == null && args.DocumentPage != DocumentPage.Missing) { //Add the page to the Watcher so we can determine if the page has been //destroyed in our Delegate. _pageDestroyedWatcher.AddPage(args.DocumentPage); //Since handling the GetPageCompleted event entails a bit of work, we'll //have our dispatcher call the GetPageCompletedDelegate at Normal priority. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(GetPageCompletedDelegate), args); } } ////// Asynchronously handles the GetPageCompleted event. /// This means that a requested page is available, so we /// update the page's cache entry, mark it as clean and fire off our /// GetPageCompleted event. /// /// ///private object GetPageCompletedDelegate(object parameter) { GetPageCompletedEventArgs args = parameter as GetPageCompletedEventArgs; if (args == null) { throw new ArgumentOutOfRangeException("parameter"); } //Check to see if the page has been destroyed, and remove it from the Watcher. bool pageDestroyed = _pageDestroyedWatcher.IsDestroyed(args.DocumentPage); _pageDestroyedWatcher.RemovePage(args.DocumentPage); //The page was destroyed, return early. if (pageDestroyed) { return null; } //We only update the entry if the GetPageAsync call was not canceled, //points to a valid page (i.e. is not DocumentPage.Missing) //and did not result in an Error condition. if (!args.Cancelled && args.Error == null && args.DocumentPage != DocumentPage.Missing ) { if (args.DocumentPage.Size == Size.Empty) { throw new ArgumentOutOfRangeException("args"); } //Update the cache. PageCacheEntry newEntry; newEntry.PageSize = args.DocumentPage.Size; newEntry.Dirty = false; //Create our list of changes. We can have at most two. List changes = new List (2); PageCacheChange change; //If we add pages such that there's going to be a gap //in the cache (for example if we get PaginationProgress events //for pages 1-3 and then 7-10), we need to fill in the gap with entries. if (args.PageNumber > _cache.Count - 1) { //Add the new page (this will cause any pages we //skipped over to be filled in) change = AddRange(args.PageNumber, 1); if (change != null) { changes.Add(change); } //Update the just-retrieved-page to reflect the actual page size. change = UpdateEntry(args.PageNumber, newEntry); if (change != null) { changes.Add(change); } } else { //Just update the retrieved page's cache entry. change = UpdateEntry(args.PageNumber, newEntry); if (change != null) { changes.Add(change); } } //If this page is a different size than the last-retrieved page, then we have a //dynamic document. if (_isDefaultSizeKnown && newEntry.PageSize != _lastPageSize) { _dynamicPageSizes = true; } _lastPageSize = newEntry.PageSize; //If this is the first page in the document that we've actually retrieved from //the DocumentPaginator, we'll use this page's size as the default size and //update any dirty pages in the cache with this default size. //(Technically there should be no non-dirty pages in the document at this point, but //if we want to change our heuristic (for example, use the "most common page size") //to define the default then we don't want to clobber known pages.) if (!_isDefaultSizeKnown) { _defaultPageSize = newEntry.PageSize; _isDefaultSizeKnown = true; SetDefaultPageSize(true); } //Fire off our PageCacheChanged event. FirePageCacheChangedEvent(changes); } //Fire the GetPageCompleted event. if (GetPageCompleted != null) { GetPageCompleted(this, args); } return null; } /// /// Checks that the start and count parameters passed by a /// Pagination event are valid. /// /// /// private void ValidatePaginationArgs(int start, int count) { if (start < 0) { throw new ArgumentOutOfRangeException("start"); } if (count <= 0) { throw new ArgumentOutOfRangeException("count"); } } ////// Updates entries in the cache to have the current default page size. /// private void SetDefaultPageSize(bool dirtyOnly) { //Create our list of changes. We can potentially have as many //changes as there are pages in the document. Listchanges = new List (PageCount); Invariant.Assert(_defaultPageSize != Size.Empty, "Default Page Size is Empty."); for (int i = 0; i < _cache.Count; i++) { if (_cache[i].Dirty || !dirtyOnly) { PageCacheEntry newEntry; newEntry.PageSize = _defaultPageSize; newEntry.Dirty = true; PageCacheChange change = UpdateEntry(i, newEntry); if (change != null) { changes.Add(change); } } } //Fire off our PageCacheChanged event. FirePageCacheChangedEvent(changes); } /// /// Fires the PageCacheChanged event with the specified changelist. /// /// The changes to pass along with the event. private void FirePageCacheChangedEvent(Listchanges) { Debug.Assert(changes != null, "Attempt to fire PageCacheChangedEvent with null change set."); //Fire off our PageCacheChangedEvent if we have any changes if (PageCacheChanged != null && changes != null && changes.Count > 0) { PageCacheChangedEventArgs args = new PageCacheChangedEventArgs(changes); PageCacheChanged(this, args); } } /// /// Adds a range of dirty cache entries to the cache. /// /// The starting index for the entries. /// The number of entries to add. private PageCacheChange AddRange(int start, int count) { //Make sure we're in range. if (start < 0 ) { throw new ArgumentOutOfRangeException("start"); } if (count < 1) { throw new ArgumentOutOfRangeException("count"); } Invariant.Assert(_defaultPageSize != Size.Empty, "Default Page Size is Empty."); //If we add pages such that there's going to be a gap //in the cache (for example if we get PaginationProgress events //for pages 1-3 and then 7-10), we need to fill in the gap with entries. if( start >= _cache.Count ) { count += (start - _cache.Count); start = _cache.Count; } //Add the new entries. //Each entry is marked as dirty and is assumed to have the default page size. for (int i = start; i < start+count; i++) { PageCacheEntry newEntry; newEntry.PageSize = _defaultPageSize; newEntry.Dirty = true; _cache.Add(newEntry); } return new PageCacheChange(start, count, PageCacheChangeType.Add); } ////// Updates the cache entry at the specified index with a new entry. /// /// /// private PageCacheChange UpdateEntry(int index, PageCacheEntry newEntry) { //Make sure we're in range. if (index >= _cache.Count || index < 0) { throw new ArgumentOutOfRangeException("index"); } Invariant.Assert(newEntry.PageSize != Size.Empty, "Updated entry newEntry has Empty PageSize."); //Check to see if the entry has changed. if (newEntry.PageSize != _cache[index].PageSize || newEntry.Dirty != _cache[index].Dirty) { //Update the cache entry _cache[index] = newEntry; return new PageCacheChange(index, 1, PageCacheChangeType.Update); } return null; } ////// Dirties the cache entries for a range of pages that have been /// modified. Adds new entries where necessary. /// /// /// private PageCacheChange DirtyRange(int start, int count) { //Make sure we're in range. if( start >= _cache.Count ) { throw new ArgumentOutOfRangeException("start"); } if (start + count > _cache.Count || count < 1 ) { throw new ArgumentOutOfRangeException("count"); } Invariant.Assert(_defaultPageSize != Size.Empty, "Default Page Size is Empty."); for (int i = start; i < start+count; i++) { //Dirty the pages in the range of invalidated pages. //This entails setting the dirty bit and //setting the page size to the default. //We'll add new entries if necessary. PageCacheEntry newEntry; newEntry.Dirty = true; newEntry.PageSize = _defaultPageSize; _cache[i] = newEntry; } return new PageCacheChange(start, count, PageCacheChangeType.Update); } ////// Clears out the current cache and lets listeners know of the change. /// private void ClearCache() { if (_cache.Count > 0) { //Build our list of changes Listchanges = new List (1); //This change indicates that all of the pages were removed from the cache. PageCacheChange change = new PageCacheChange(0, _cache.Count, PageCacheChangeType.Remove); changes.Add(change); //Clear the cache _cache.Clear(); //Fire the event. FirePageCacheChangedEvent(changes); } } #endregion Public Methods //----------------------------------------------------- // // Private Fields // //------------------------------------------------------ #region Private Fields //The List that contains our cache. private List _cache; //The PageDestroyedWatcher that keeps track of the Destroyed-state of DocumentPages. private PageDestroyedWatcher _pageDestroyedWatcher; //Our document private DynamicDocumentPaginator _documentPaginator; //The original state of IDP.IsBackgroundPaginationEnabled private bool _originalIsBackgroundPaginationEnabled; //Whether the page sizes are uniform or not private bool _dynamicPageSizes; //Whether our content has a FlowDirection of RTL or not. private bool _isContentRightToLeft; //Whether pagination has finished or is still in progress. private bool _isPaginationCompleted; //Flags related to the "default" size private bool _isDefaultSizeKnown; private Size _defaultPageSize; //The last page size retrieved from the DocumentPaginator. private Size _lastPageSize; //The _default_ default page size. We don't want the default to be (0,0) because: //a) (0,0) is nearly never a valid page size. //b) it causes page layout to end up with all pages initially visible which causes a huge perf hit. //Our initial default page size is 8.5"x11", which is 816x1056 pixels (at 1/96") private readonly Size _initialDefaultPageSize = new Size(816, 1056); //The default size of our List cache. private readonly int _defaultCacheSize = 64; #endregion Private Fields } #region PageDestroyedWatcher /// /// PageDestroyedWatcher is used to keep track of whether one or more /// DocumentPages have been Destroyed. This is necessary because /// DocumentPage does not expose a property that indicates whether it has /// been destroyed, only a PageDestroyed event. PageDestroyedWatcher /// listens for this event and keeps track of which DocumentPages have been /// destroyed. /// internal class PageDestroyedWatcher { ////// Instantiates a new PageDestroyedWatcher /// public PageDestroyedWatcher() { _table = new Hashtable(16); } ////// Adds a new DocumentPage to the Watcher /// /// The page to add public void AddPage(DocumentPage page) { if (!_table.Contains(page)) { _table.Add(page, false); page.PageDestroyed += new EventHandler(OnPageDestroyed); } else { _table[page] = false; } } ////// Removes an existing page from the Watcher /// /// public void RemovePage(DocumentPage page) { if (_table.Contains(page)) { _table.Remove(page); page.PageDestroyed -= new EventHandler(OnPageDestroyed); } } ////// Indicates whether the specified DocumentPage has been destroyed. /// /// ///public bool IsDestroyed(DocumentPage page) { if (_table.Contains(page)) { return (bool)_table[page]; } else { return true; } } /// /// Handles the OnPageDestroyed event and updates the Destroyed state of the pages /// associated with this Watcher. /// /// /// private void OnPageDestroyed(object sender, EventArgs e) { DocumentPage page = sender as DocumentPage; Invariant.Assert(page != null, "Invalid type in PageDestroyedWatcher"); if (_table.Contains(page)) { _table[page] = true; } } //Hashtable to associate Destroyed states with DocumentPages. private Hashtable _table; } #endregion PageDestroyedWatcher #region PageCacheEntry ////// An entry in our PageCache /// internal struct PageCacheEntry { ////// The size of the given page /// public Size PageSize; ////// Whether the above PageSize is up to date. /// public bool Dirty; } #endregion PageCacheEntry #region PageCacheChangedEvent ////// PageCacheChanged event handler. /// internal delegate void PageCacheChangedEventHandler(object sender, PageCacheChangedEventArgs e); ////// Event arguments for the PageCacheChanged event. /// internal class PageCacheChangedEventArgs : EventArgs { ////// Constructor. /// /// The changes corresponding to this event public PageCacheChangedEventArgs(Listchanges) { _changes = changes; } /// /// The list of changes associated with this event. /// public ListChanges { get { return _changes; } } private readonly List _changes; } /// /// Represents a single change to the PageCache /// internal class PageCacheChange { ////// Constructor. /// /// The first page changed. /// The number of pages changed. /// The type of changed incurred. public PageCacheChange(int start, int count, PageCacheChangeType type) { _start = start; _count = count; _type = type; } ////// Zero-based page number for this first page that has changed. /// public int Start { get { return _start; } } ////// Number of continuous pages changed. /// public int Count { get { return _count; } } ////// The type of change that occurred. /// public PageCacheChangeType Type { get { return _type; } } private readonly int _start; private readonly int _count; private readonly PageCacheChangeType _type; } internal enum PageCacheChangeType { ////// Pages were added to the cache. /// Add=0, ////// Pages were removed from the cache. /// Remove, ////// Pages in the cache were updated. /// Update } #endregion PageCacheChangedEvent } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: PageCache caches information about individual pages in a document. // // History: // 10/21/04 - jdersch created // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Windows; using System.Windows.Threading; using System.Windows.Documents; namespace MS.Internal.Documents { ////// PageCache acts as both a page-dimension cache and a proxy for an DocumentPaginator document. /// It doles out pages to DocumentGrid and keeps the cache in [....] with the DocumentPaginator. /// ///http://d2/DRX/default.aspx internal class PageCache { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Default constructor for a PageCache object. /// Creates our internal cache, represented as a Generic List. /// public PageCache() { //Create the internal representation of our Cache with a default size. //This cache is a dynamic List which will expand to accommodate larger documents. _cache = new List(_defaultCacheSize); //Create the PageDestroyedWatcher which will keep track of what DocumentPages //have been destroyed. _pageDestroyedWatcher = new PageDestroyedWatcher(); } #endregion Constructors //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- #region Public Properties /// /// The DocumentPaginator Content tree we're interested in caching information about. /// ///public DynamicDocumentPaginator Content { set { //If the content is actually changing we update our paginator here. if (_documentPaginator != value) { //Reset our flags and default page size. _dynamicPageSizes = false; _defaultPageSize = _initialDefaultPageSize; _isDefaultSizeKnown = false; _isPaginationCompleted = false; //If the old DocumentPaginator is non-null, we need to //remove the old event handlers before we assign the new one. if (_documentPaginator != null) { _documentPaginator.PagesChanged -= new PagesChangedEventHandler(OnPagesChanged); _documentPaginator.GetPageCompleted -= new GetPageCompletedEventHandler(OnGetPageCompleted); _documentPaginator.PaginationProgress -= new PaginationProgressEventHandler(OnPaginationProgress); _documentPaginator.PaginationCompleted -= new EventHandler(OnPaginationCompleted); //Reset the Background Pagination flag to its original state. _documentPaginator.IsBackgroundPaginationEnabled = _originalIsBackgroundPaginationEnabled; } //Now assign the new paginator. _documentPaginator = value; //Clear our cache. ClearCache(); //Attach our event handlers and set relevant properties if the new content is non-null. if (_documentPaginator != null) { _documentPaginator.PagesChanged += new PagesChangedEventHandler(OnPagesChanged); _documentPaginator.GetPageCompleted += new GetPageCompletedEventHandler(OnGetPageCompleted); _documentPaginator.PaginationProgress += new PaginationProgressEventHandler(OnPaginationProgress); _documentPaginator.PaginationCompleted += new EventHandler(OnPaginationCompleted); //Set the paginator's PageSize so the new content will reflow to fit in the requested space. _documentPaginator.PageSize = _defaultPageSize; //We save off the original value so we can restore it when new content is assigned. _originalIsBackgroundPaginationEnabled = _documentPaginator.IsBackgroundPaginationEnabled; //Enable background pagination, and set the paginator's PageSize so the new content will //reflow to fit in the requested space. _documentPaginator.IsBackgroundPaginationEnabled = true; //Determine content flow direction, if the content has one specified. //We look for the FrameworkElement.FlowDirection property. //If it doesn't have one then we assume the content is Left-To-Right. //(Note: FlowDirection is a value type and thus cannot be null; //DependencyObject.GetValue will never return null for this even if the //DocumentPaginator does not have this property set -- it will just //return the default value for FlowDirectionProperty.) if (_documentPaginator.Source is DependencyObject) { FlowDirection flowDirection = (FlowDirection)((DependencyObject)_documentPaginator.Source).GetValue(FrameworkElement.FlowDirectionProperty); if (flowDirection == FlowDirection.LeftToRight) { _isContentRightToLeft = false; } else { _isContentRightToLeft = true; } } } //If the content is already paginated (as is the case for certain Fixed content) //we'll call OnPaginationProgress here to let the Cache (and any listeners) know that //some or all the pages are available. if ( _documentPaginator != null ) { if (_documentPaginator.PageCount > 0) { OnPaginationProgress(_documentPaginator, new PaginationProgressEventArgs(0, _documentPaginator.PageCount)); } if ( _documentPaginator.IsPageCountValid ) { OnPaginationCompleted(_documentPaginator, EventArgs.Empty); } } } } get { //Just return our current paginator. return _documentPaginator; } } /// /// The number of pages in the cache. /// ///public int PageCount { get { return _cache.Count; } } /// /// Based on current knowledge, reports whether the document consists /// entirely of pages with the same dimensions or whether page sizes vary /// (i.e. are 'Dynamic') /// ///public bool DynamicPageSizes { get { return _dynamicPageSizes; } } /// /// Indicates whether the content has an RTL flowdirection or not. /// ///public bool IsContentRightToLeft { get { return _isContentRightToLeft; } } public bool IsPaginationCompleted { get { return _isPaginationCompleted; } } #endregion Public Properties //------------------------------------------------------ // // Public Events // //------------------------------------------------------ #region Public Events /// /// Fired when one ore more pages in the document have been paginated. /// public event PaginationProgressEventHandler PaginationProgress; ////// Fired when the document is finished paginating. /// public event EventHandler PaginationCompleted; ////// Fired when one or more pages in the document have changed. /// public event PagesChangedEventHandler PagesChanged; ////// Fired when a requested page has been retrieved. /// public event GetPageCompletedEventHandler GetPageCompleted; ////// Fired when one or more entries in the PageCache have been updated for any reason /// (Due to pagination, a new default page size, page retrieval, etc...) /// public event PageCacheChangedEventHandler PageCacheChanged; #endregion Public Events //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ #region Public Methods ////// Retrieves the cached size of a page in the document, even if the page is /// marked as "dirty." Pages are only un-dirtied when GetPage() is called /// (that is, as pages are actually retrieved from the IDocumentFormatter). /// /// The pagenumber to return the dimensions of. ///The dimensions of the requested page, or (0,0) for nonexistent pages. public Size GetPageSize(int pageNumber) { if (pageNumber >= 0 && pageNumber < _cache.Count) { Size pageSize = _cache[pageNumber].PageSize; Invariant.Assert(pageSize != Size.Empty, "PageCache entry's PageSize is Empty."); return pageSize; } else { return new Size(0, 0); } } // ///// /// --- Commenting out this method as it is currently unused. --- // /// Retrieves a page from the DocumentPaginator Asynchronously. // /// Caller will receive a GetPageCompleted event when the page is actually available, // /// and the Cache will be updated to reflect the true page dimensions at that time. // /// // /// The page to retrieve from the DocumentPaginator // ///Nothing. // public void GetPage(int pageNumber) // { // if (_documentPaginator != null) // { // _documentPaginator.GetPageAsync(pageNumber, (object)pageNumber); // } // } ////// Retrieves the "Dirty" bit for the associated page. /// /// The page to retrive the Dirty bit for ///The dirty bit public bool IsPageDirty(int pageNumber) { if (pageNumber >= 0 && pageNumber < _cache.Count) { return _cache[pageNumber].Dirty; } else { return true; } } #endregion Public Methods //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- #region Private Methods ////// Handler for the OnPaginationProgress event fired by the DocumentPaginator. /// /// /// private void OnPaginationProgress(object sender, PaginationProgressEventArgs args) { //Since handling the PaginationProgress event entails a bit of work, we'll //have our dispatcher call the PaginationProgress event at Normal priority. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(PaginationProgressDelegate), args); } ////// Asynchronously handles the PaginationProgress event. /// This means that one or more pages have been added to the document, so we /// add any new pages to the cache, mark them as dirty, and fire off our PaginationProgress /// event. /// /// ///private object PaginationProgressDelegate(object parameter) { PaginationProgressEventArgs args = parameter as PaginationProgressEventArgs; if (args == null) { throw new InvalidOperationException("parameter"); } //Validate incoming parameters. ValidatePaginationArgs(args.Start, args.Count); if (_isPaginationCompleted) { if (args.Start == 0) { //Since we've started repaginating from the beginning of the document //after pagination was completed, we can't assume we know //the default page size anymore. _isDefaultSizeKnown = false; _dynamicPageSizes = false; } //Reset our IsPaginationCompleted flag since we just got a pagination event. _isPaginationCompleted = false; } //Check for integer overflow. if (args.Start + args.Count < 0) { throw new ArgumentOutOfRangeException("args"); } //Create our list of changes. We allocate space for 2 changes here //as we can have as many as two changes resulting from a Pagination event. List changes = new List (2); PageCacheChange change; //If we have pages to add or modify, do so now. if (args.Count > 0) { //If pagination has added new pages onto the end of the document, we //add new entries to our cache. if (args.Start >= _cache.Count) { //Completely new pages, so we add new cache entries change = AddRange(args.Start, args.Count); if (change != null) { changes.Add(change); } } else { //Pagination has updated some currently existing pages, so we'll //update our entries. if (args.Start + args.Count < _cache.Count) { //All pre-existing pages, so we just dirty the current cache entries. change = DirtyRange(args.Start, args.Count); if (change != null) { changes.Add(change); } } else { //Some pre-existing pages, some new. change = DirtyRange(args.Start, _cache.Count - args.Start); if (change != null) { changes.Add(change); } change = AddRange(_cache.Count, args.Count - (_cache.Count - args.Start) + 1); if (change != null) { changes.Add(change); } } } } //If the document's PageCount is now less than the size of our cache due to repagination //we remove the extra entries. int pageCount = _documentPaginator != null ? _documentPaginator.PageCount : 0; if (pageCount < _cache.Count) { change = new PageCacheChange(pageCount, _cache.Count - pageCount, PageCacheChangeType.Remove); changes.Add(change); //Remove the pages from the cache. _cache.RemoveRange(pageCount, _cache.Count - pageCount); } //Fire off our PageCacheChanged event. FirePageCacheChangedEvent(changes); //Fire the PaginationProgress event. if (PaginationProgress != null) { PaginationProgress(this, args); } return null; } /// /// Handler for the OnPaginationCompleted event. Merely fires off our own event. /// /// The sender of this event /// The arguments associated with this event private void OnPaginationCompleted(object sender, EventArgs args) { //Since handling the PaginationCompleted event entails a bit of work, we'll //have our dispatcher call the PaginationCompleted event at Normal priority. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(PaginationCompletedDelegate), args); } ////// Asynchronously handles the PaginationCompleted event. /// /// ///private object PaginationCompletedDelegate(object parameter) { EventArgs args = parameter as EventArgs; if (args == null) { throw new ArgumentOutOfRangeException("parameter"); } //set our IsPaginationCompleted flag since we're done paginating. _isPaginationCompleted = true; //Fire the PaginationProgress event. if (PaginationCompleted != null) { PaginationCompleted(this, args); } return null; } /// /// Handler for the OnPagesChanged event fired by the DocumentPaginator. /// /// /// private void OnPagesChanged(object sender, PagesChangedEventArgs args) { //Since handling the PagesChanged event entails a bit of work, we'll //have our dispatcher call the PagesChangedDelegate at Normal priority. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(PagesChangedDelegate), args); } ////// Asynchronously handles the PagesChanged event. /// This means that one or more pages have been invalidated so we /// dirty their cache entries and fire off our PagesChanged event. /// /// ///private object PagesChangedDelegate(object parameter) { PagesChangedEventArgs args = parameter as PagesChangedEventArgs; if (args == null) { throw new ArgumentOutOfRangeException("parameter"); } //Validate incoming parameters ValidatePaginationArgs(args.Start, args.Count); //Start values outside the range of current pages are invalid. //if (args.Start >= _cache.Count) //{ // throw new ArgumentOutOfRangeException("args"); //} //If the last page specified in the change is out of the range of currently-known //pages we make the assumption that the IDP means to invalidate all pages and so //we clip the count into range. //We also take into account integer overflow... if the sum of Start+Count is less than //zero then we've overflowed. int adjustedCount = args.Count; if (args.Start + args.Count >= _cache.Count || args.Start + args.Count < 0) { adjustedCount = _cache.Count - args.Start; } //Create our list of changes. We can have at most one. List changes = new List (1); //Now make the change if there is one to make. if (adjustedCount > 0) { PageCacheChange change = DirtyRange(args.Start, adjustedCount); if (change != null) { changes.Add(change); } //Fire off our PageCacheChanged event. FirePageCacheChangedEvent(changes); } //Fire the PagesChanged event. if (PagesChanged != null) { PagesChanged(this, args); } return null; } /// /// Handler for the GetPageCompleted event fired by the DocumentPaginator. /// /// /// private void OnGetPageCompleted(object sender, GetPageCompletedEventArgs args) { if (!args.Cancelled && args.Error == null && args.DocumentPage != DocumentPage.Missing) { //Add the page to the Watcher so we can determine if the page has been //destroyed in our Delegate. _pageDestroyedWatcher.AddPage(args.DocumentPage); //Since handling the GetPageCompleted event entails a bit of work, we'll //have our dispatcher call the GetPageCompletedDelegate at Normal priority. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(GetPageCompletedDelegate), args); } } ////// Asynchronously handles the GetPageCompleted event. /// This means that a requested page is available, so we /// update the page's cache entry, mark it as clean and fire off our /// GetPageCompleted event. /// /// ///private object GetPageCompletedDelegate(object parameter) { GetPageCompletedEventArgs args = parameter as GetPageCompletedEventArgs; if (args == null) { throw new ArgumentOutOfRangeException("parameter"); } //Check to see if the page has been destroyed, and remove it from the Watcher. bool pageDestroyed = _pageDestroyedWatcher.IsDestroyed(args.DocumentPage); _pageDestroyedWatcher.RemovePage(args.DocumentPage); //The page was destroyed, return early. if (pageDestroyed) { return null; } //We only update the entry if the GetPageAsync call was not canceled, //points to a valid page (i.e. is not DocumentPage.Missing) //and did not result in an Error condition. if (!args.Cancelled && args.Error == null && args.DocumentPage != DocumentPage.Missing ) { if (args.DocumentPage.Size == Size.Empty) { throw new ArgumentOutOfRangeException("args"); } //Update the cache. PageCacheEntry newEntry; newEntry.PageSize = args.DocumentPage.Size; newEntry.Dirty = false; //Create our list of changes. We can have at most two. List changes = new List (2); PageCacheChange change; //If we add pages such that there's going to be a gap //in the cache (for example if we get PaginationProgress events //for pages 1-3 and then 7-10), we need to fill in the gap with entries. if (args.PageNumber > _cache.Count - 1) { //Add the new page (this will cause any pages we //skipped over to be filled in) change = AddRange(args.PageNumber, 1); if (change != null) { changes.Add(change); } //Update the just-retrieved-page to reflect the actual page size. change = UpdateEntry(args.PageNumber, newEntry); if (change != null) { changes.Add(change); } } else { //Just update the retrieved page's cache entry. change = UpdateEntry(args.PageNumber, newEntry); if (change != null) { changes.Add(change); } } //If this page is a different size than the last-retrieved page, then we have a //dynamic document. if (_isDefaultSizeKnown && newEntry.PageSize != _lastPageSize) { _dynamicPageSizes = true; } _lastPageSize = newEntry.PageSize; //If this is the first page in the document that we've actually retrieved from //the DocumentPaginator, we'll use this page's size as the default size and //update any dirty pages in the cache with this default size. //(Technically there should be no non-dirty pages in the document at this point, but //if we want to change our heuristic (for example, use the "most common page size") //to define the default then we don't want to clobber known pages.) if (!_isDefaultSizeKnown) { _defaultPageSize = newEntry.PageSize; _isDefaultSizeKnown = true; SetDefaultPageSize(true); } //Fire off our PageCacheChanged event. FirePageCacheChangedEvent(changes); } //Fire the GetPageCompleted event. if (GetPageCompleted != null) { GetPageCompleted(this, args); } return null; } /// /// Checks that the start and count parameters passed by a /// Pagination event are valid. /// /// /// private void ValidatePaginationArgs(int start, int count) { if (start < 0) { throw new ArgumentOutOfRangeException("start"); } if (count <= 0) { throw new ArgumentOutOfRangeException("count"); } } ////// Updates entries in the cache to have the current default page size. /// private void SetDefaultPageSize(bool dirtyOnly) { //Create our list of changes. We can potentially have as many //changes as there are pages in the document. Listchanges = new List (PageCount); Invariant.Assert(_defaultPageSize != Size.Empty, "Default Page Size is Empty."); for (int i = 0; i < _cache.Count; i++) { if (_cache[i].Dirty || !dirtyOnly) { PageCacheEntry newEntry; newEntry.PageSize = _defaultPageSize; newEntry.Dirty = true; PageCacheChange change = UpdateEntry(i, newEntry); if (change != null) { changes.Add(change); } } } //Fire off our PageCacheChanged event. FirePageCacheChangedEvent(changes); } /// /// Fires the PageCacheChanged event with the specified changelist. /// /// The changes to pass along with the event. private void FirePageCacheChangedEvent(Listchanges) { Debug.Assert(changes != null, "Attempt to fire PageCacheChangedEvent with null change set."); //Fire off our PageCacheChangedEvent if we have any changes if (PageCacheChanged != null && changes != null && changes.Count > 0) { PageCacheChangedEventArgs args = new PageCacheChangedEventArgs(changes); PageCacheChanged(this, args); } } /// /// Adds a range of dirty cache entries to the cache. /// /// The starting index for the entries. /// The number of entries to add. private PageCacheChange AddRange(int start, int count) { //Make sure we're in range. if (start < 0 ) { throw new ArgumentOutOfRangeException("start"); } if (count < 1) { throw new ArgumentOutOfRangeException("count"); } Invariant.Assert(_defaultPageSize != Size.Empty, "Default Page Size is Empty."); //If we add pages such that there's going to be a gap //in the cache (for example if we get PaginationProgress events //for pages 1-3 and then 7-10), we need to fill in the gap with entries. if( start >= _cache.Count ) { count += (start - _cache.Count); start = _cache.Count; } //Add the new entries. //Each entry is marked as dirty and is assumed to have the default page size. for (int i = start; i < start+count; i++) { PageCacheEntry newEntry; newEntry.PageSize = _defaultPageSize; newEntry.Dirty = true; _cache.Add(newEntry); } return new PageCacheChange(start, count, PageCacheChangeType.Add); } ////// Updates the cache entry at the specified index with a new entry. /// /// /// private PageCacheChange UpdateEntry(int index, PageCacheEntry newEntry) { //Make sure we're in range. if (index >= _cache.Count || index < 0) { throw new ArgumentOutOfRangeException("index"); } Invariant.Assert(newEntry.PageSize != Size.Empty, "Updated entry newEntry has Empty PageSize."); //Check to see if the entry has changed. if (newEntry.PageSize != _cache[index].PageSize || newEntry.Dirty != _cache[index].Dirty) { //Update the cache entry _cache[index] = newEntry; return new PageCacheChange(index, 1, PageCacheChangeType.Update); } return null; } ////// Dirties the cache entries for a range of pages that have been /// modified. Adds new entries where necessary. /// /// /// private PageCacheChange DirtyRange(int start, int count) { //Make sure we're in range. if( start >= _cache.Count ) { throw new ArgumentOutOfRangeException("start"); } if (start + count > _cache.Count || count < 1 ) { throw new ArgumentOutOfRangeException("count"); } Invariant.Assert(_defaultPageSize != Size.Empty, "Default Page Size is Empty."); for (int i = start; i < start+count; i++) { //Dirty the pages in the range of invalidated pages. //This entails setting the dirty bit and //setting the page size to the default. //We'll add new entries if necessary. PageCacheEntry newEntry; newEntry.Dirty = true; newEntry.PageSize = _defaultPageSize; _cache[i] = newEntry; } return new PageCacheChange(start, count, PageCacheChangeType.Update); } ////// Clears out the current cache and lets listeners know of the change. /// private void ClearCache() { if (_cache.Count > 0) { //Build our list of changes Listchanges = new List (1); //This change indicates that all of the pages were removed from the cache. PageCacheChange change = new PageCacheChange(0, _cache.Count, PageCacheChangeType.Remove); changes.Add(change); //Clear the cache _cache.Clear(); //Fire the event. FirePageCacheChangedEvent(changes); } } #endregion Public Methods //----------------------------------------------------- // // Private Fields // //------------------------------------------------------ #region Private Fields //The List that contains our cache. private List _cache; //The PageDestroyedWatcher that keeps track of the Destroyed-state of DocumentPages. private PageDestroyedWatcher _pageDestroyedWatcher; //Our document private DynamicDocumentPaginator _documentPaginator; //The original state of IDP.IsBackgroundPaginationEnabled private bool _originalIsBackgroundPaginationEnabled; //Whether the page sizes are uniform or not private bool _dynamicPageSizes; //Whether our content has a FlowDirection of RTL or not. private bool _isContentRightToLeft; //Whether pagination has finished or is still in progress. private bool _isPaginationCompleted; //Flags related to the "default" size private bool _isDefaultSizeKnown; private Size _defaultPageSize; //The last page size retrieved from the DocumentPaginator. private Size _lastPageSize; //The _default_ default page size. We don't want the default to be (0,0) because: //a) (0,0) is nearly never a valid page size. //b) it causes page layout to end up with all pages initially visible which causes a huge perf hit. //Our initial default page size is 8.5"x11", which is 816x1056 pixels (at 1/96") private readonly Size _initialDefaultPageSize = new Size(816, 1056); //The default size of our List cache. private readonly int _defaultCacheSize = 64; #endregion Private Fields } #region PageDestroyedWatcher /// /// PageDestroyedWatcher is used to keep track of whether one or more /// DocumentPages have been Destroyed. This is necessary because /// DocumentPage does not expose a property that indicates whether it has /// been destroyed, only a PageDestroyed event. PageDestroyedWatcher /// listens for this event and keeps track of which DocumentPages have been /// destroyed. /// internal class PageDestroyedWatcher { ////// Instantiates a new PageDestroyedWatcher /// public PageDestroyedWatcher() { _table = new Hashtable(16); } ////// Adds a new DocumentPage to the Watcher /// /// The page to add public void AddPage(DocumentPage page) { if (!_table.Contains(page)) { _table.Add(page, false); page.PageDestroyed += new EventHandler(OnPageDestroyed); } else { _table[page] = false; } } ////// Removes an existing page from the Watcher /// /// public void RemovePage(DocumentPage page) { if (_table.Contains(page)) { _table.Remove(page); page.PageDestroyed -= new EventHandler(OnPageDestroyed); } } ////// Indicates whether the specified DocumentPage has been destroyed. /// /// ///public bool IsDestroyed(DocumentPage page) { if (_table.Contains(page)) { return (bool)_table[page]; } else { return true; } } /// /// Handles the OnPageDestroyed event and updates the Destroyed state of the pages /// associated with this Watcher. /// /// /// private void OnPageDestroyed(object sender, EventArgs e) { DocumentPage page = sender as DocumentPage; Invariant.Assert(page != null, "Invalid type in PageDestroyedWatcher"); if (_table.Contains(page)) { _table[page] = true; } } //Hashtable to associate Destroyed states with DocumentPages. private Hashtable _table; } #endregion PageDestroyedWatcher #region PageCacheEntry ////// An entry in our PageCache /// internal struct PageCacheEntry { ////// The size of the given page /// public Size PageSize; ////// Whether the above PageSize is up to date. /// public bool Dirty; } #endregion PageCacheEntry #region PageCacheChangedEvent ////// PageCacheChanged event handler. /// internal delegate void PageCacheChangedEventHandler(object sender, PageCacheChangedEventArgs e); ////// Event arguments for the PageCacheChanged event. /// internal class PageCacheChangedEventArgs : EventArgs { ////// Constructor. /// /// The changes corresponding to this event public PageCacheChangedEventArgs(Listchanges) { _changes = changes; } /// /// The list of changes associated with this event. /// public ListChanges { get { return _changes; } } private readonly List _changes; } /// /// Represents a single change to the PageCache /// internal class PageCacheChange { ////// Constructor. /// /// The first page changed. /// The number of pages changed. /// The type of changed incurred. public PageCacheChange(int start, int count, PageCacheChangeType type) { _start = start; _count = count; _type = type; } ////// Zero-based page number for this first page that has changed. /// public int Start { get { return _start; } } ////// Number of continuous pages changed. /// public int Count { get { return _count; } } ////// The type of change that occurred. /// public PageCacheChangeType Type { get { return _type; } } private readonly int _start; private readonly int _count; private readonly PageCacheChangeType _type; } internal enum PageCacheChangeType { ////// Pages were added to the cache. /// Add=0, ////// Pages were removed from the cache. /// Remove, ////// Pages in the cache were updated. /// Update } #endregion PageCacheChangedEvent } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RelationshipConstraintValidator.cs
- BuildManager.cs
- SyndicationDeserializer.cs
- TablePattern.cs
- DataGridHeaderBorder.cs
- SqlDataSourceCache.cs
- Compiler.cs
- Tool.cs
- ZipIOCentralDirectoryDigitalSignature.cs
- ToolStripItem.cs
- BamlLocalizableResourceKey.cs
- SqlCommandBuilder.cs
- SqlVisitor.cs
- FolderBrowserDialog.cs
- AttributeEmitter.cs
- TextRunTypographyProperties.cs
- Win32Native.cs
- _AutoWebProxyScriptEngine.cs
- ImageIndexConverter.cs
- LocalizableResourceBuilder.cs
- FontSizeConverter.cs
- XmlDataLoader.cs
- COM2ExtendedTypeConverter.cs
- WindowsImpersonationContext.cs
- ResourceDescriptionAttribute.cs
- FontSourceCollection.cs
- FlowSwitchLink.cs
- SynchronizationLockException.cs
- EmptyElement.cs
- InputLanguageProfileNotifySink.cs
- RegistryKey.cs
- PrintDocument.cs
- DbConnectionInternal.cs
- GridViewColumn.cs
- TypeToken.cs
- CodeDomSerializationProvider.cs
- CodeAttributeDeclaration.cs
- EventHandlersDesigner.cs
- Scripts.cs
- Comparer.cs
- SynchronizationContext.cs
- Win32.cs
- NumericUpDownAccelerationCollection.cs
- VectorConverter.cs
- ReadOnlyActivityGlyph.cs
- QueueTransferProtocol.cs
- TimeZoneInfo.cs
- ComponentEditorPage.cs
- MergePropertyDescriptor.cs
- OdbcEnvironmentHandle.cs
- ObjectView.cs
- EntityDataSourceDataSelection.cs
- UrlMapping.cs
- M3DUtil.cs
- PartialTrustVisibleAssembliesSection.cs
- SigningCredentials.cs
- UIElement3D.cs
- LinkedResourceCollection.cs
- GPPOINT.cs
- TcpProcessProtocolHandler.cs
- BaseProcessor.cs
- OracleFactory.cs
- DataServiceResponse.cs
- Drawing.cs
- SubpageParagraph.cs
- DeclarativeCatalogPart.cs
- TextEffectResolver.cs
- EdmMember.cs
- ExpressionParser.cs
- SqlDataSourceSelectingEventArgs.cs
- Tool.cs
- WebPartExportVerb.cs
- SplineKeyFrames.cs
- RedistVersionInfo.cs
- MDIClient.cs
- GridPattern.cs
- DetailsViewUpdateEventArgs.cs
- EventLogEntry.cs
- ListViewInsertedEventArgs.cs
- HealthMonitoringSection.cs
- ComponentResourceKey.cs
- ContentPlaceHolderDesigner.cs
- StorageEntitySetMapping.cs
- ToolStripDropDownClosedEventArgs.cs
- BitmapEffectGroup.cs
- DefaultParameterValueAttribute.cs
- SecurityChannel.cs
- OpCellTreeNode.cs
- WorkflowDesigner.cs
- Buffer.cs
- ParameterInfo.cs
- PolyLineSegment.cs
- BrushConverter.cs
- TempFiles.cs
- ExtentCqlBlock.cs
- Drawing.cs
- X509ChainPolicy.cs
- TypeUtils.cs
- ObjectStateManager.cs
- Tuple.cs