Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Data / XmlDataProvider.cs / 1 / XmlDataProvider.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: Implementation of XmlDataProvider object. // // Specs: http://avalon/connecteddata/Specs/Avalon%20DataProviders.mht // http://avalon/connecteddata/Specs/XmlDataSource.mht // //--------------------------------------------------------------------------- using System; using System.IO; // Stream using System.Collections; using System.ComponentModel; // ISupportInitialize, AsyncCompletedEventHandler, [DesignerSerialization*], [DefaultValue] using System.Diagnostics; using System.IO.Packaging; // PackUriHelper using System.Globalization; // CultureInfo using System.Net; // WebRequest, IWebRequestCreate using System.Threading; // ThreadPool, WaitCallback using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; // IXmlSerializable using System.Xml.XPath; using System.Windows; using System.Windows.Data; using System.Windows.Threading; // Dispatcher* using System.Windows.Markup; // IUriContext, [XamlDesignerSerializer] using MS.Internal; // CriticalExceptions using MS.Internal.Utility; // BindUriHelper using MS.Internal.Data; // XmlDataCollection namespace System.Windows.Data { ////// XmlDataProvider class, gets XmlNodes to use as source in data binding /// ///[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)] [ContentProperty("XmlSerializer")] public class XmlDataProvider : DataSourceProvider, IUriContext { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- /// /// Instantiates a new instance of a XmlDataProvider /// public XmlDataProvider() { } //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- #region Public Properties ////// Source property, indicated the Uri of the source Xml data file, if this /// property is set, then any inline xml data is discarded. /// ////// Setting this property will implicitly cause this DataProvider to refresh. /// When changing multiple refresh-causing properties, the use of /// public Uri Source { get { return _source; } set { if ((_domSetDocument != null) || _source != value) { _domSetDocument = null; _source = value; if (!IsRefreshDeferred) Refresh(); } } } ///is recommended. /// /// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeSource() { return (_domSetDocument == null) && (_source != null); } ////// Document Property, returns the current XmlDocument that this data source /// is using, if set the Source property is cleared and any inline xml data is /// discarded /// ////// Setting this property will implicitly cause this DataProvider to refresh. /// When changing multiple refresh-causing properties, the use of /// // this property cannot be serialized since the produced XAML/XML wouldn't be parseable anymore; // instead, a user-set DOM is serialized as InlineData [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public XmlDocument Document { get { return _document; } set { if ((_domSetDocument == null) || _source != null || _document != value) { _domSetDocument = value; _source = null; ChangeDocument(value); // set immediately so that next get_Document returns this value, // even when data provider is in deferred or asynch mode if (!IsRefreshDeferred) Refresh(); } } } ///is recommended. /// /// XPath property, the XPath query used for generating the DataCollection /// ////// Setting this property will implicitly cause this DataProvider to refresh. /// When changing multiple refresh-causing properties, the use of /// [DesignerSerializationOptions(DesignerSerializationOptions.SerializeAsAttribute)] public string XPath { get { return _xPath; } set { if (_xPath != value) { _xPath = value; if (!IsRefreshDeferred) Refresh(); } } } ///is recommended. /// /// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeXPath() { return (_xPath != null) && (_xPath.Length != 0); } ////// XmlNamespaceManager property, XmlNamespaceManager used for executing XPath queries. /// in order to set this property using markup, an XmlDataNamespaceManager resource can be used. /// ////// Setting this property will implicitly cause this DataProvider to refresh. /// When changing multiple refresh-causing properties, the use of /// [DefaultValue(null)] public XmlNamespaceManager XmlNamespaceManager { get { return _nsMgr; } set { if (_nsMgr != value) { _nsMgr = value; if (!IsRefreshDeferred) Refresh(); } } } ///is recommended. /// /// If true object creation will be performed in a worker /// thread, otherwise will be done in active context. /// [DefaultValue(true)] public bool IsAsynchronous { get { return _isAsynchronous; } set { _isAsynchronous = value; } } ////// The content property for inline Xml data. /// Used by the parser to compile the literal content of the embedded XML island. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] [EditorBrowsable(EditorBrowsableState.Never)] public IXmlSerializable XmlSerializer { get { if (_xmlSerializer == null) { _xmlSerializer = new XmlIslandSerializer(this); } return _xmlSerializer; } } ////// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeXmlSerializer() { // serialize InlineData only if the Xml DOM used was originally a inline Xml data island return (DocumentForSerialization != null); } #endregion #region IUriContext ////// Provides the base uri of the current context. /// Uri IUriContext.BaseUri { get { return BaseUri; } set { BaseUri = value; } } ////// Implementation for BaseUri. /// protected virtual Uri BaseUri { get { return _baseUri; } set { _baseUri = value; } } #endregion IUriContext //------------------------------------------------------ // // Protected Methods // //------------------------------------------------------ ////// Prepare loading either the external XML or inline XML and /// produce Xml node collection. /// Execution is either immediately or on a background thread, see IsAsynchronous. /// Called by base class from InitialLoad or Refresh /// protected override void BeginQuery() { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.BeginQuery( TraceData.Identify(this), IsAsynchronous ? "asynchronous" : "synchronous")); } if (_source != null) { // load DOM from external source Debug.Assert(_domSetDocument == null, "Should not be possible to be using Source and user-set Doc at the same time."); DiscardInline(); LoadFromSource(); // will execute synch or asycnh, depends on IsAsynchronous } else { XmlDocument doc = null; if (_domSetDocument != null) { DiscardInline(); doc = _domSetDocument; } else // inline doc { // Did we already parse the inline DOM? // Don't do this during EndInit - it duplicates effort of Parse if (_inEndInit) return; doc = _savedDocument; } // Doesn't matter if the doc is set programmatically or from inline, // here we create a new collection for it and make it active. if (IsAsynchronous && doc != null) { // process node collection on a worker thread ? ThreadPool.QueueUserWorkItem(new WaitCallback(BuildNodeCollectionAsynch), doc); } else if (doc != null || Data != null) { // process the doc synchronously if we're in synchronous mode, // or if the doc is empty. But don't process an empty doc // if the data is already null, to avoid unnecessary work BuildNodeCollection(doc); } } } ////// Initialization of this element has completed; /// this causes a Refresh if no other deferred refresh is outstanding /// protected override void EndInit() { // inhibit re-parsing of inline doc (from BeginQuery) try { _inEndInit = true; base.EndInit(); } finally { _inEndInit = false; } } //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Preparing DOM // Load XML from a URI; this runs on caller thread of BeginQuery (Refresh/InitialLoad) private void LoadFromSource() { // convert the Source into an absolute URI Uri sourceUri = this.Source; if (sourceUri.IsAbsoluteUri == false) { Uri baseUri = (_baseUri != null) ? _baseUri : BindUriHelper.BaseUri; sourceUri = BindUriHelper.GetResolvedUri(baseUri, sourceUri); } // create a request to load the content // Ideally we would want to use RegisterPrefix and WebRequest.Create. // However, these two functions regress 700k working set in System.dll and System.xml.dll // which is mostly for logging and config. // Call PackWebRequestFactory.CreateWebRequest to bypass the regression if possible // by calling Create on PackWebRequest if uri is pack scheme WebRequest request = PackWebRequestFactory.CreateWebRequest(sourceUri); if (request == null) { throw new Exception(SR.Get(SRID.WebRequestCreationFailed)); } // load it on a worker thread ? if (IsAsynchronous) ThreadPool.QueueUserWorkItem(new WaitCallback(CreateDocFromExternalSourceAsynch), request); else CreateDocFromExternalSource(request); } #region Content Parsing implementation private class XmlIslandSerializer : IXmlSerializable { internal XmlIslandSerializer(XmlDataProvider host) { _host = host; } public XmlSchema GetSchema() { // return null; } public void WriteXml(XmlWriter writer) { XmlDocument doc = _host.DocumentForSerialization; if (doc != null) doc.Save(writer); } public void ReadXml(XmlReader reader) { _host.ParseInline(reader); } private XmlDataProvider _host; } ////// Parse method, /// /// private void ParseInline(XmlReader xmlReader) { if ((_source == null) && (_domSetDocument == null) && _tryInlineDoc) { // load it on a worker thread ? if (IsAsynchronous) { _waitForInlineDoc = new ManualResetEvent(false); // tells serializer to wait until _document is ready ThreadPool.QueueUserWorkItem(new WaitCallback(CreateDocFromInlineXmlAsync), xmlReader); } else CreateDocFromInlineXml(xmlReader); } } private XmlDocument DocumentForSerialization { get { // allow inline serialization only if the original XML data was inline // or the user has assigned own DOM to our Document property if (_tryInlineDoc || (_savedDocument != null) || (_domSetDocument != null)) { // if inline or assigned doc hasn't been parsed yet, wait for it if (_waitForInlineDoc != null) _waitForInlineDoc.WaitOne(); return _document; } return null; } } #endregion //Content Parsing implementation // this method can run on a worker thread! private void CreateDocFromInlineXmlAsync(object arg) { XmlReader xmlReader = (XmlReader) arg; CreateDocFromInlineXml(xmlReader); } // this method can run on a worker thread! private void CreateDocFromInlineXml(XmlReader xmlReader) { // Maybe things have changed and we don't want to use inline doc anymore if (!_tryInlineDoc) { _savedDocument = null; if (_waitForInlineDoc != null) _waitForInlineDoc.Set(); return; } XmlDocument doc; Exception ex = null; try { doc = new XmlDocument(); try { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.XmlProvider)) { TraceData.Trace(TraceEventType.Warning, TraceData.XmlLoadInline( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous")); } // Load the inline doc from the reader doc.Load(xmlReader); } catch (XmlException xmle) { if (TraceData.IsEnabled) TraceData.Trace(TraceEventType.Error, TraceData.XmlDPInlineDocError, xmle); ex = xmle; } if (ex == null) { // Save a copy of the inline document to be used in future // queries, and by serialization. _savedDocument = (XmlDocument)doc.Clone(); } } finally { xmlReader.Close(); // Whether or not parsing was successful, unblock the serializer thread. // If serializer had to wait for the inline doc, it's available now. // If there was an error, null will be returned for DocumentForSerialization. if (_waitForInlineDoc != null) _waitForInlineDoc.Set(); } // warn the user if the default xmlns wasn't set explicitly (bug 1006946) if (TraceData.IsEnabled) { XmlNode root = doc.DocumentElement; if (root != null && root.NamespaceURI == XamlReaderHelper.DefaultNamespaceURI) { TraceData.Trace(TraceEventType.Error, TraceData.XmlNamespaceNotSet); } } if (ex == null) { // Load succeeded. Create the node collection. (This calls // OnQueryFinished to reset the Document and Data properties). BuildNodeCollection(doc); } else { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.QueryFinished( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous", TraceData.Identify(null), TraceData.IdentifyException(ex))); } // Load failed. Report the error, and reset // Data and Document properties to null. OnQueryFinished(null, ex, CompletedCallback, null); } } // this method can run on a worker thread! private void CreateDocFromExternalSourceAsynch(object arg) { WebRequest request = (WebRequest) arg; CreateDocFromExternalSource(request); } // this method can run on a worker thread! private void CreateDocFromExternalSource(WebRequest request) { bool isExtendedTraceEnabled = TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.XmlProvider); XmlDocument doc = new XmlDocument(); Exception ex = null; // request the content from the URI try { if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.XmlLoadSource( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous", TraceData.Identify(request.RequestUri.ToString()))); } WebResponse response = WpfWebRequestHelper.GetResponse(request); if (response == null) { throw new InvalidOperationException(SR.Get(SRID.GetResponseFailed)); } // Get Stream and content type from WebResponse. Stream stream = response.GetResponseStream(); if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.XmlLoadDoc( TraceData.Identify(this))); } // load the XML from the stream doc.Load(stream); stream.Close(); } catch (Exception e) { if (CriticalExceptions.IsCriticalException(e)) { throw; } ex = e; if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.XmlDPAsyncDocError, Source, ex); } } //FXCop Fix: CatchNonClsCompliantExceptionsInGeneralHandlers catch { throw; } if (ex != null) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.QueryFinished( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous", TraceData.Identify(null), TraceData.IdentifyException(ex))); } // we're done if we got an error up to this point // both .Data and .Document properties will be reset to null OnQueryFinished(null, ex, CompletedCallback, null); return; // have an error, no processing of DOM } BuildNodeCollection(doc); // above method also calls OnQueryFinished to push new property values } // this method can run on a worker thread! private void BuildNodeCollectionAsynch(object arg) { XmlDocument doc = (XmlDocument) arg; BuildNodeCollection(doc); } // this method can run on a worker thread! private void BuildNodeCollection(XmlDocument doc) { XmlDataCollection collection = null; if (doc != null) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.XmlBuildCollection)) { TraceData.Trace(TraceEventType.Warning, TraceData.XmlBuildCollection( TraceData.Identify(this))); } XmlNodeList nodes = GetResultNodeList(doc); //we always create a new DataCollection collection = new XmlDataCollection(this); if (nodes != null) { collection.SynchronizeCollection(nodes); } } if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.QueryFinished( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous", TraceData.Identify(collection), TraceData.IdentifyException(null))); } OnQueryFinished(collection, null, CompletedCallback, doc); } // this callback will execute on the UI thread; // OnQueryFinished marshals back to UI thread if necessary private object OnCompletedCallback(object arg) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.QueryResult( TraceData.Identify(this), TraceData.Identify(Data))); } ChangeDocument((XmlDocument) arg); return null; } // change Document property, and update event listeners accordingly private void ChangeDocument(XmlDocument doc) { if (_document != doc) { if (_document != null) UnHook(); _document = doc; if (_document != null) Hook(); OnPropertyChanged(new PropertyChangedEventArgs("Document")); } } #endregion Preparing DOM // Point of no return: do not ever try to use the inline XML again. private void DiscardInline() { _tryInlineDoc = false; _savedDocument = null; if (_waitForInlineDoc != null) _waitForInlineDoc.Set(); } private void Hook() { if (!_isListening) { _document.NodeInserted += NodeChangeHandler; _document.NodeRemoved += NodeChangeHandler; _document.NodeChanged += NodeChangeHandler; _isListening = true; } } private void UnHook() { if (_isListening) { _document.NodeInserted -= NodeChangeHandler; _document.NodeRemoved -= NodeChangeHandler; _document.NodeChanged -= NodeChangeHandler; _isListening = false; } } private void OnNodeChanged(object sender, XmlNodeChangedEventArgs e) { if (XmlDataCollection == null) return; UnHook(); XmlNodeList nodes = GetResultNodeList((XmlDocument) sender); // Compare the entire new list with the old, // and make all the necessary insert/remove changes. XmlDataCollection.SynchronizeCollection(nodes); Hook(); } private XmlNodeList GetResultNodeList(XmlDocument doc) { Debug.Assert(doc != null); XmlNodeList nodes = null; if (doc.DocumentElement != null) { // if no XPath is specified, use the root node by default string xpath = (string.IsNullOrEmpty(XPath)) ? "/" : XPath; try { if (XmlNamespaceManager != null) { nodes = doc.SelectNodes(xpath, XmlNamespaceManager); } else { nodes = doc.SelectNodes(xpath); } } catch (XPathException xe) { if (TraceData.IsEnabled) TraceData.Trace(TraceEventType.Error, TraceData.XmlDPSelectNodesFailed, xpath, xe); } } return nodes; } //----------------------------------------------------- // // Private Properties // //----------------------------------------------------- private XmlDataCollection XmlDataCollection { get { return (XmlDataCollection) this.Data; } } private DispatcherOperationCallback CompletedCallback { get { if (_onCompletedCallback == null) _onCompletedCallback = new DispatcherOperationCallback(OnCompletedCallback); return _onCompletedCallback; } } private XmlNodeChangedEventHandler NodeChangeHandler { get { if (_nodeChangedHandler == null) _nodeChangedHandler = new XmlNodeChangedEventHandler(OnNodeChanged); return _nodeChangedHandler; } } //----------------------------------------------------- // // Private Fields // //------------------------------------------------------ private XmlDocument _document; // the active XML DOM document private XmlDocument _domSetDocument; // a DOM set by user private XmlDocument _savedDocument; // stored copy of Inline Xml for rollback private ManualResetEvent _waitForInlineDoc; // serializer waits on this for inline doc private XmlNamespaceManager _nsMgr; private Uri _source; private Uri _baseUri; private string _xPath = string.Empty; private bool _tryInlineDoc = true; private bool _isListening = false; private XmlIslandSerializer _xmlSerializer; bool _isAsynchronous = true; bool _inEndInit; private DispatcherOperationCallback _onCompletedCallback; private XmlNodeChangedEventHandler _nodeChangedHandler; } } // 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: Implementation of XmlDataProvider object. // // Specs: http://avalon/connecteddata/Specs/Avalon%20DataProviders.mht // http://avalon/connecteddata/Specs/XmlDataSource.mht // //--------------------------------------------------------------------------- using System; using System.IO; // Stream using System.Collections; using System.ComponentModel; // ISupportInitialize, AsyncCompletedEventHandler, [DesignerSerialization*], [DefaultValue] using System.Diagnostics; using System.IO.Packaging; // PackUriHelper using System.Globalization; // CultureInfo using System.Net; // WebRequest, IWebRequestCreate using System.Threading; // ThreadPool, WaitCallback using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; // IXmlSerializable using System.Xml.XPath; using System.Windows; using System.Windows.Data; using System.Windows.Threading; // Dispatcher* using System.Windows.Markup; // IUriContext, [XamlDesignerSerializer] using MS.Internal; // CriticalExceptions using MS.Internal.Utility; // BindUriHelper using MS.Internal.Data; // XmlDataCollection namespace System.Windows.Data { ////// XmlDataProvider class, gets XmlNodes to use as source in data binding /// ///[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)] [ContentProperty("XmlSerializer")] public class XmlDataProvider : DataSourceProvider, IUriContext { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- /// /// Instantiates a new instance of a XmlDataProvider /// public XmlDataProvider() { } //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- #region Public Properties ////// Source property, indicated the Uri of the source Xml data file, if this /// property is set, then any inline xml data is discarded. /// ////// Setting this property will implicitly cause this DataProvider to refresh. /// When changing multiple refresh-causing properties, the use of /// public Uri Source { get { return _source; } set { if ((_domSetDocument != null) || _source != value) { _domSetDocument = null; _source = value; if (!IsRefreshDeferred) Refresh(); } } } ///is recommended. /// /// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeSource() { return (_domSetDocument == null) && (_source != null); } ////// Document Property, returns the current XmlDocument that this data source /// is using, if set the Source property is cleared and any inline xml data is /// discarded /// ////// Setting this property will implicitly cause this DataProvider to refresh. /// When changing multiple refresh-causing properties, the use of /// // this property cannot be serialized since the produced XAML/XML wouldn't be parseable anymore; // instead, a user-set DOM is serialized as InlineData [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public XmlDocument Document { get { return _document; } set { if ((_domSetDocument == null) || _source != null || _document != value) { _domSetDocument = value; _source = null; ChangeDocument(value); // set immediately so that next get_Document returns this value, // even when data provider is in deferred or asynch mode if (!IsRefreshDeferred) Refresh(); } } } ///is recommended. /// /// XPath property, the XPath query used for generating the DataCollection /// ////// Setting this property will implicitly cause this DataProvider to refresh. /// When changing multiple refresh-causing properties, the use of /// [DesignerSerializationOptions(DesignerSerializationOptions.SerializeAsAttribute)] public string XPath { get { return _xPath; } set { if (_xPath != value) { _xPath = value; if (!IsRefreshDeferred) Refresh(); } } } ///is recommended. /// /// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeXPath() { return (_xPath != null) && (_xPath.Length != 0); } ////// XmlNamespaceManager property, XmlNamespaceManager used for executing XPath queries. /// in order to set this property using markup, an XmlDataNamespaceManager resource can be used. /// ////// Setting this property will implicitly cause this DataProvider to refresh. /// When changing multiple refresh-causing properties, the use of /// [DefaultValue(null)] public XmlNamespaceManager XmlNamespaceManager { get { return _nsMgr; } set { if (_nsMgr != value) { _nsMgr = value; if (!IsRefreshDeferred) Refresh(); } } } ///is recommended. /// /// If true object creation will be performed in a worker /// thread, otherwise will be done in active context. /// [DefaultValue(true)] public bool IsAsynchronous { get { return _isAsynchronous; } set { _isAsynchronous = value; } } ////// The content property for inline Xml data. /// Used by the parser to compile the literal content of the embedded XML island. /// [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] [EditorBrowsable(EditorBrowsableState.Never)] public IXmlSerializable XmlSerializer { get { if (_xmlSerializer == null) { _xmlSerializer = new XmlIslandSerializer(this); } return _xmlSerializer; } } ////// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool ShouldSerializeXmlSerializer() { // serialize InlineData only if the Xml DOM used was originally a inline Xml data island return (DocumentForSerialization != null); } #endregion #region IUriContext ////// Provides the base uri of the current context. /// Uri IUriContext.BaseUri { get { return BaseUri; } set { BaseUri = value; } } ////// Implementation for BaseUri. /// protected virtual Uri BaseUri { get { return _baseUri; } set { _baseUri = value; } } #endregion IUriContext //------------------------------------------------------ // // Protected Methods // //------------------------------------------------------ ////// Prepare loading either the external XML or inline XML and /// produce Xml node collection. /// Execution is either immediately or on a background thread, see IsAsynchronous. /// Called by base class from InitialLoad or Refresh /// protected override void BeginQuery() { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.BeginQuery( TraceData.Identify(this), IsAsynchronous ? "asynchronous" : "synchronous")); } if (_source != null) { // load DOM from external source Debug.Assert(_domSetDocument == null, "Should not be possible to be using Source and user-set Doc at the same time."); DiscardInline(); LoadFromSource(); // will execute synch or asycnh, depends on IsAsynchronous } else { XmlDocument doc = null; if (_domSetDocument != null) { DiscardInline(); doc = _domSetDocument; } else // inline doc { // Did we already parse the inline DOM? // Don't do this during EndInit - it duplicates effort of Parse if (_inEndInit) return; doc = _savedDocument; } // Doesn't matter if the doc is set programmatically or from inline, // here we create a new collection for it and make it active. if (IsAsynchronous && doc != null) { // process node collection on a worker thread ? ThreadPool.QueueUserWorkItem(new WaitCallback(BuildNodeCollectionAsynch), doc); } else if (doc != null || Data != null) { // process the doc synchronously if we're in synchronous mode, // or if the doc is empty. But don't process an empty doc // if the data is already null, to avoid unnecessary work BuildNodeCollection(doc); } } } ////// Initialization of this element has completed; /// this causes a Refresh if no other deferred refresh is outstanding /// protected override void EndInit() { // inhibit re-parsing of inline doc (from BeginQuery) try { _inEndInit = true; base.EndInit(); } finally { _inEndInit = false; } } //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Preparing DOM // Load XML from a URI; this runs on caller thread of BeginQuery (Refresh/InitialLoad) private void LoadFromSource() { // convert the Source into an absolute URI Uri sourceUri = this.Source; if (sourceUri.IsAbsoluteUri == false) { Uri baseUri = (_baseUri != null) ? _baseUri : BindUriHelper.BaseUri; sourceUri = BindUriHelper.GetResolvedUri(baseUri, sourceUri); } // create a request to load the content // Ideally we would want to use RegisterPrefix and WebRequest.Create. // However, these two functions regress 700k working set in System.dll and System.xml.dll // which is mostly for logging and config. // Call PackWebRequestFactory.CreateWebRequest to bypass the regression if possible // by calling Create on PackWebRequest if uri is pack scheme WebRequest request = PackWebRequestFactory.CreateWebRequest(sourceUri); if (request == null) { throw new Exception(SR.Get(SRID.WebRequestCreationFailed)); } // load it on a worker thread ? if (IsAsynchronous) ThreadPool.QueueUserWorkItem(new WaitCallback(CreateDocFromExternalSourceAsynch), request); else CreateDocFromExternalSource(request); } #region Content Parsing implementation private class XmlIslandSerializer : IXmlSerializable { internal XmlIslandSerializer(XmlDataProvider host) { _host = host; } public XmlSchema GetSchema() { // return null; } public void WriteXml(XmlWriter writer) { XmlDocument doc = _host.DocumentForSerialization; if (doc != null) doc.Save(writer); } public void ReadXml(XmlReader reader) { _host.ParseInline(reader); } private XmlDataProvider _host; } ////// Parse method, /// /// private void ParseInline(XmlReader xmlReader) { if ((_source == null) && (_domSetDocument == null) && _tryInlineDoc) { // load it on a worker thread ? if (IsAsynchronous) { _waitForInlineDoc = new ManualResetEvent(false); // tells serializer to wait until _document is ready ThreadPool.QueueUserWorkItem(new WaitCallback(CreateDocFromInlineXmlAsync), xmlReader); } else CreateDocFromInlineXml(xmlReader); } } private XmlDocument DocumentForSerialization { get { // allow inline serialization only if the original XML data was inline // or the user has assigned own DOM to our Document property if (_tryInlineDoc || (_savedDocument != null) || (_domSetDocument != null)) { // if inline or assigned doc hasn't been parsed yet, wait for it if (_waitForInlineDoc != null) _waitForInlineDoc.WaitOne(); return _document; } return null; } } #endregion //Content Parsing implementation // this method can run on a worker thread! private void CreateDocFromInlineXmlAsync(object arg) { XmlReader xmlReader = (XmlReader) arg; CreateDocFromInlineXml(xmlReader); } // this method can run on a worker thread! private void CreateDocFromInlineXml(XmlReader xmlReader) { // Maybe things have changed and we don't want to use inline doc anymore if (!_tryInlineDoc) { _savedDocument = null; if (_waitForInlineDoc != null) _waitForInlineDoc.Set(); return; } XmlDocument doc; Exception ex = null; try { doc = new XmlDocument(); try { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.XmlProvider)) { TraceData.Trace(TraceEventType.Warning, TraceData.XmlLoadInline( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous")); } // Load the inline doc from the reader doc.Load(xmlReader); } catch (XmlException xmle) { if (TraceData.IsEnabled) TraceData.Trace(TraceEventType.Error, TraceData.XmlDPInlineDocError, xmle); ex = xmle; } if (ex == null) { // Save a copy of the inline document to be used in future // queries, and by serialization. _savedDocument = (XmlDocument)doc.Clone(); } } finally { xmlReader.Close(); // Whether or not parsing was successful, unblock the serializer thread. // If serializer had to wait for the inline doc, it's available now. // If there was an error, null will be returned for DocumentForSerialization. if (_waitForInlineDoc != null) _waitForInlineDoc.Set(); } // warn the user if the default xmlns wasn't set explicitly (bug 1006946) if (TraceData.IsEnabled) { XmlNode root = doc.DocumentElement; if (root != null && root.NamespaceURI == XamlReaderHelper.DefaultNamespaceURI) { TraceData.Trace(TraceEventType.Error, TraceData.XmlNamespaceNotSet); } } if (ex == null) { // Load succeeded. Create the node collection. (This calls // OnQueryFinished to reset the Document and Data properties). BuildNodeCollection(doc); } else { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.QueryFinished( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous", TraceData.Identify(null), TraceData.IdentifyException(ex))); } // Load failed. Report the error, and reset // Data and Document properties to null. OnQueryFinished(null, ex, CompletedCallback, null); } } // this method can run on a worker thread! private void CreateDocFromExternalSourceAsynch(object arg) { WebRequest request = (WebRequest) arg; CreateDocFromExternalSource(request); } // this method can run on a worker thread! private void CreateDocFromExternalSource(WebRequest request) { bool isExtendedTraceEnabled = TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.XmlProvider); XmlDocument doc = new XmlDocument(); Exception ex = null; // request the content from the URI try { if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.XmlLoadSource( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous", TraceData.Identify(request.RequestUri.ToString()))); } WebResponse response = WpfWebRequestHelper.GetResponse(request); if (response == null) { throw new InvalidOperationException(SR.Get(SRID.GetResponseFailed)); } // Get Stream and content type from WebResponse. Stream stream = response.GetResponseStream(); if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.XmlLoadDoc( TraceData.Identify(this))); } // load the XML from the stream doc.Load(stream); stream.Close(); } catch (Exception e) { if (CriticalExceptions.IsCriticalException(e)) { throw; } ex = e; if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.XmlDPAsyncDocError, Source, ex); } } //FXCop Fix: CatchNonClsCompliantExceptionsInGeneralHandlers catch { throw; } if (ex != null) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.QueryFinished( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous", TraceData.Identify(null), TraceData.IdentifyException(ex))); } // we're done if we got an error up to this point // both .Data and .Document properties will be reset to null OnQueryFinished(null, ex, CompletedCallback, null); return; // have an error, no processing of DOM } BuildNodeCollection(doc); // above method also calls OnQueryFinished to push new property values } // this method can run on a worker thread! private void BuildNodeCollectionAsynch(object arg) { XmlDocument doc = (XmlDocument) arg; BuildNodeCollection(doc); } // this method can run on a worker thread! private void BuildNodeCollection(XmlDocument doc) { XmlDataCollection collection = null; if (doc != null) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.XmlBuildCollection)) { TraceData.Trace(TraceEventType.Warning, TraceData.XmlBuildCollection( TraceData.Identify(this))); } XmlNodeList nodes = GetResultNodeList(doc); //we always create a new DataCollection collection = new XmlDataCollection(this); if (nodes != null) { collection.SynchronizeCollection(nodes); } } if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.QueryFinished( TraceData.Identify(this), Dispatcher.CheckAccess() ? "synchronous" : "asynchronous", TraceData.Identify(collection), TraceData.IdentifyException(null))); } OnQueryFinished(collection, null, CompletedCallback, doc); } // this callback will execute on the UI thread; // OnQueryFinished marshals back to UI thread if necessary private object OnCompletedCallback(object arg) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ProviderQuery)) { TraceData.Trace(TraceEventType.Warning, TraceData.QueryResult( TraceData.Identify(this), TraceData.Identify(Data))); } ChangeDocument((XmlDocument) arg); return null; } // change Document property, and update event listeners accordingly private void ChangeDocument(XmlDocument doc) { if (_document != doc) { if (_document != null) UnHook(); _document = doc; if (_document != null) Hook(); OnPropertyChanged(new PropertyChangedEventArgs("Document")); } } #endregion Preparing DOM // Point of no return: do not ever try to use the inline XML again. private void DiscardInline() { _tryInlineDoc = false; _savedDocument = null; if (_waitForInlineDoc != null) _waitForInlineDoc.Set(); } private void Hook() { if (!_isListening) { _document.NodeInserted += NodeChangeHandler; _document.NodeRemoved += NodeChangeHandler; _document.NodeChanged += NodeChangeHandler; _isListening = true; } } private void UnHook() { if (_isListening) { _document.NodeInserted -= NodeChangeHandler; _document.NodeRemoved -= NodeChangeHandler; _document.NodeChanged -= NodeChangeHandler; _isListening = false; } } private void OnNodeChanged(object sender, XmlNodeChangedEventArgs e) { if (XmlDataCollection == null) return; UnHook(); XmlNodeList nodes = GetResultNodeList((XmlDocument) sender); // Compare the entire new list with the old, // and make all the necessary insert/remove changes. XmlDataCollection.SynchronizeCollection(nodes); Hook(); } private XmlNodeList GetResultNodeList(XmlDocument doc) { Debug.Assert(doc != null); XmlNodeList nodes = null; if (doc.DocumentElement != null) { // if no XPath is specified, use the root node by default string xpath = (string.IsNullOrEmpty(XPath)) ? "/" : XPath; try { if (XmlNamespaceManager != null) { nodes = doc.SelectNodes(xpath, XmlNamespaceManager); } else { nodes = doc.SelectNodes(xpath); } } catch (XPathException xe) { if (TraceData.IsEnabled) TraceData.Trace(TraceEventType.Error, TraceData.XmlDPSelectNodesFailed, xpath, xe); } } return nodes; } //----------------------------------------------------- // // Private Properties // //----------------------------------------------------- private XmlDataCollection XmlDataCollection { get { return (XmlDataCollection) this.Data; } } private DispatcherOperationCallback CompletedCallback { get { if (_onCompletedCallback == null) _onCompletedCallback = new DispatcherOperationCallback(OnCompletedCallback); return _onCompletedCallback; } } private XmlNodeChangedEventHandler NodeChangeHandler { get { if (_nodeChangedHandler == null) _nodeChangedHandler = new XmlNodeChangedEventHandler(OnNodeChanged); return _nodeChangedHandler; } } //----------------------------------------------------- // // Private Fields // //------------------------------------------------------ private XmlDocument _document; // the active XML DOM document private XmlDocument _domSetDocument; // a DOM set by user private XmlDocument _savedDocument; // stored copy of Inline Xml for rollback private ManualResetEvent _waitForInlineDoc; // serializer waits on this for inline doc private XmlNamespaceManager _nsMgr; private Uri _source; private Uri _baseUri; private string _xPath = string.Empty; private bool _tryInlineDoc = true; private bool _isListening = false; private XmlIslandSerializer _xmlSerializer; bool _isAsynchronous = true; bool _inEndInit; private DispatcherOperationCallback _onCompletedCallback; private XmlNodeChangedEventHandler _nodeChangedHandler; } } // 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
- CollectionViewGroupInternal.cs
- WebRequestModulesSection.cs
- HttpException.cs
- WebPartTransformerAttribute.cs
- HtmlElementErrorEventArgs.cs
- FileDialog.cs
- LicenseManager.cs
- DataGridState.cs
- DbLambda.cs
- HashHelper.cs
- RotateTransform.cs
- MenuItemBinding.cs
- ConcurrentStack.cs
- DataGridViewRowCollection.cs
- EntityClassGenerator.cs
- JsonServiceDocumentSerializer.cs
- safemediahandle.cs
- _OSSOCK.cs
- ConnectionStringsExpressionBuilder.cs
- DockAndAnchorLayout.cs
- LoadWorkflowByKeyAsyncResult.cs
- SpnegoTokenAuthenticator.cs
- Gdiplus.cs
- Html32TextWriter.cs
- TemplatedMailWebEventProvider.cs
- UICuesEvent.cs
- BuildTopDownAttribute.cs
- HtmlMobileTextWriter.cs
- FrameworkElement.cs
- SqlMethodAttribute.cs
- GeometryDrawing.cs
- DynamicObject.cs
- DesignerAutoFormat.cs
- TaskDesigner.cs
- ItemCollection.cs
- ChtmlLinkAdapter.cs
- SerialPinChanges.cs
- Trace.cs
- ExceptionHandlerDesigner.cs
- RemoteDebugger.cs
- TempFiles.cs
- CollectionDataContract.cs
- ContourSegment.cs
- DocumentApplicationJournalEntry.cs
- NullableConverter.cs
- AsymmetricSecurityProtocolFactory.cs
- ByteStreamMessageEncoderFactory.cs
- ButtonColumn.cs
- ContentDesigner.cs
- Stackframe.cs
- CreateUserWizardStep.cs
- BatchParser.cs
- KeysConverter.cs
- SoapProcessingBehavior.cs
- NamespaceList.cs
- BatchServiceHost.cs
- Attributes.cs
- DataGridToolTip.cs
- RegexStringValidator.cs
- FilePresentation.cs
- DashStyles.cs
- TextEffect.cs
- XmlFormatWriterGenerator.cs
- PropertyMappingExceptionEventArgs.cs
- RenderingBiasValidation.cs
- SqlBuffer.cs
- PartialCachingControl.cs
- SecondaryIndex.cs
- FormsAuthenticationTicket.cs
- WebPartsPersonalization.cs
- _ShellExpression.cs
- CookielessHelper.cs
- FilteredXmlReader.cs
- QueuedDeliveryRequirementsMode.cs
- StaticTextPointer.cs
- MachineKeyConverter.cs
- CharEntityEncoderFallback.cs
- CompleteWizardStep.cs
- DefaultObjectMappingItemCollection.cs
- FtpWebRequest.cs
- storepermission.cs
- path.cs
- Parameter.cs
- PrtCap_Reader.cs
- QueryExpr.cs
- MsmqIntegrationMessagePool.cs
- Point3DAnimationBase.cs
- FrugalMap.cs
- TypeHelpers.cs
- GridViewRowCollection.cs
- NTAccount.cs
- Slider.cs
- XsltOutput.cs
- InkCanvas.cs
- ColorConverter.cs
- MouseOverProperty.cs
- Light.cs
- SqlDataSource.cs
- ProgramNode.cs
- ScrollBarAutomationPeer.cs