DataSourceProvider.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / System / Windows / Data / DataSourceProvider.cs / 1305600 / DataSourceProvider.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: common base class and contract for data source provider objects 
// 
// Specs:       http://avalon/connecteddata/Specs/Avalon%20DataProviders.mht
// 
//---------------------------------------------------------------------------

using System;
using System.Diagnostics; 
using System.ComponentModel;
using System.Threading; 
using System.Windows.Threading;     // Dispatcher* 

using MS.Internal;  // Invariant 

namespace System.Windows.Data
{
 
    /// 
    /// Common base class and contract for data source providers. 
    /// A DataProvider in Avalon is the factory that executes some query 
    /// to produce a single object or a list of objects that can be used
    /// as sources for Avalon data bindings. 
    /// It is a convenience wrapper around existing data model, it does not replace any data model.
    /// A data provider does not attempt to condense the complexity and versatility of a data model
    /// like ADO into one single object with a few properties.
    ///  
    /// 
    /// DataSourceProvider is an abstract class and cannot directly be used as a data provider. 
    /// Use one of the derived concrete provider, e.g. XmlDataProvider, ObjectDataProvider. 
    /// The DataProvider aware of Avalon's threading and dispatcher model. The data provider assumes
    /// the thread at creation time to be the UI thread. Events will get marshalled from a worker thread 
    /// to the app's UI thread.
    /// 
    public abstract class DataSourceProvider : INotifyPropertyChanged, ISupportInitialize
    { 
        /// 
        /// constructor captures the Dispatcher associated with the current thread 
        ///  
        protected DataSourceProvider()
        { 
            _dispatcher = Dispatcher.CurrentDispatcher;
        }

        ///  
        /// Start the initial query to the underlying data model.
        /// The result will be returned on the Data property. 
        /// This method is typically called by the binding engine when 
        /// dependent data bindings are activated.
        /// Set IsInitialLoadEnabled = false to prevent or delay the automatic loading of data. 
        /// 
        /// 
        /// The InitialLoad method can be called multiple times.
        /// The provider is expected to ignore subsequent calls once the provider 
        /// is busy executing the initial query, i.e. the provider shall not restart
        /// an already running query when InitialLoad is called again. 
        /// When the query finishes successfully, any InitialLoad call will still not re-query data. 
        /// The InitialLoad operation is typically asynchronous, a DataChanged event will
        /// be raised when the Data property assumed a new value. 
        /// The application should call Refresh to cause a refresh of data.
        /// 
        public void InitialLoad()
        { 
            // ignore call if IsInitialLoadEnabled == false or already started initialization
            if (!IsInitialLoadEnabled || _initialLoadCalled) 
                return; 

            _initialLoadCalled = true; 
            BeginQuery();
        }

        ///  
        /// Initiates a Refresh Operation to the underlying data model.
        /// The result will be returned on the Data property. 
        ///  
        /// 
        /// A refresh operation is typically asynchronous, a DataChanged event will 
        /// be raised when the Data property assumed a new value.
        /// If the refresh operation fails, the Data property will be set to null;
        /// the Error property will be set with the error exception.
        /// The app can call Refresh while a previous refresh is still underway. 
        /// Calling Refresh twice will cause the DataChanged event to raise twice.
        ///  
        public void Refresh() 
        {
            _initialLoadCalled = true; 
            BeginQuery();
        }

        ///  
        /// Set IsInitialLoadEnabled = false to prevent or delay the automatic loading of data.
        ///  
        [DefaultValue(true)] 
        public bool IsInitialLoadEnabled
        { 
            get { return _isInitialLoadEnabled; }
            set
            {
                _isInitialLoadEnabled = value; 
                OnPropertyChanged(new PropertyChangedEventArgs("IsInitialLoadEnabled"));
            } 
        } 

        ///  
        /// Get the underlying data object.
        /// This is the resulting data source the data provider
        /// 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
        public object Data
        { 
            get { return _data; } 
        }
 
        /// 
        /// Raise this event when a new data object becomes available
        /// on the Data property.
        ///  
        public event EventHandler DataChanged;
 
        ///  
        /// Return the error of the last query operation.
        /// To indicate there was no error, it will return null 
        /// 
        public Exception Error
        {
            get { return _error; } 
        }
 
 
        /// 
        /// Enter a Defer Cycle. 
        /// Defer cycles are used to coalesce property changes, any automatic
        /// Refresh is delayed until the Defer Cycle is exited.
        /// 
        ///  
        /// most typical usage is with a using block to set multiple proporties
        /// without the automatic Refresh to occur 
        ///  
        ///     XmlDataProvider xdv = new XmlDataProvider();
        ///     using(xdv.DeferRefresh()) { 
        ///         xdv.Source = "http://foo.com/bar.xml";
        ///         xdv.XPath = "/Bla/Baz[@Boo='xyz']";
        ///     }
        ///  
        /// 
        public virtual IDisposable DeferRefresh() 
        { 
            ++_deferLevel;
            return new DeferHelper(this); 
        }


        #region ISupportInitialize 
        /// 
        ///     Initialization of this element is about to begin 
        ///  
        void ISupportInitialize.BeginInit()
        { 
            BeginInit();
        }

        ///  
        ///     Initialization of this element has completed
        ///  
        void ISupportInitialize.EndInit() 
        {
            EndInit(); 
        }
        #endregion

 
        /// 
        /// PropertyChanged event (per ). 
        ///  
        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
        { 
            add
            {
                PropertyChanged += value;
            } 
            remove
            { 
                PropertyChanged -= value; 
            }
        } 


        //-----------------------------------------------------
        // 
        //  Protected Properties
        // 
        //----------------------------------------------------- 

        ///  
        /// IsRefreshDeferred returns true if there is still an
        /// outstanding DeferRefresh in use.  To get the best use
        /// out of refresh deferral, derived classes should try
        /// not to call Refresh when IsRefreshDeferred is true. 
        /// 
        protected bool IsRefreshDeferred 
        { 
            get
            { 
                return (  (_deferLevel > 0)
                        || (!IsInitialLoadEnabled && !_initialLoadCalled));
            }
        } 

        ///  
        /// The current Dispatcher to the Avalon UI thread to use. 
        /// 
        ///  
        /// By default, this is the Dispatcher associated with the thread
        /// on which this DataProvider instance was created.
        /// 
        protected Dispatcher Dispatcher 
        {
            get { return _dispatcher; } 
            set 
            {
                if (_dispatcher != value) 
                {
                    _dispatcher = value;
                }
            } 
        }
 
        //------------------------------------------------------ 
        //
        //  Protected Methods 
        //
        //-----------------------------------------------------

        #region Protected Methods 

        ///  
        /// Overridden by concrete data provider class. 
        /// the base class will call this method when InitialLoad or Refresh
        /// has been called and will delay this call if refresh is deferred ot 
        /// initial load is disabled.
        /// 
        /// 
        /// The implementor can choose to execute the query on the same thread or 
        /// on a background thread or using asynchronous API.
        /// When the query is complete, call OnQueryFinished to have the public properties updated. 
        ///  
        protected virtual void BeginQuery()
        { 
        }

        /// 
        /// A concrete data provider will call this method 
        /// to indicate that a query has finished.
        ///  
        ///  
        /// This callback can be called from any thread, this implementation
        /// will marshal back the result to the UI thread 
        /// before setting any of the public properties and before raising any events.
        /// resulting data from query
        /// 
        protected void OnQueryFinished(object newData) 
        {
             OnQueryFinished(newData, null, null, null); 
        } 

        ///  
        /// A concrete data provider will call this method
        /// to indicate that a query has finished.
        /// 
        ///  
        /// This callback can be called from any thread, this implementation
        /// will marshal back the result to the UI thread 
        /// before setting any of the public properties and before raising any events. 
        /// resulting data from query
        /// error that occured while running query; null signals no error 
        /// optional delegate to execute completion work on UI thread, e.g. setting additional properties
        /// optional arguments to send as parameter with the completionWork delegate
        /// 
        protected virtual void OnQueryFinished(object newData, Exception error, 
                                                DispatcherOperationCallback completionWork, object callbackArguments)
        { 
            Invariant.Assert(Dispatcher != null); 
            // check if we're already on the dispatcher thread
            if (Dispatcher.CheckAccess()) 
            {
                // already on UI thread
                UpdateWithNewResult(error, newData, completionWork, callbackArguments);
            } 
            else
            { 
                // marshal the result back to the main thread 
                Dispatcher.BeginInvoke(
                    DispatcherPriority.Normal, UpdateWithNewResultCallback, 
                    new object[] { this, error, newData, completionWork, callbackArguments });
            }
        }
 
        /// 
        /// PropertyChanged event (per ). 
        ///  
        protected virtual event PropertyChangedEventHandler PropertyChanged;
 
        /// 
        /// Raises a PropertyChanged event (per ).
        /// 
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) 
        {
            if (PropertyChanged != null) 
            { 
                PropertyChanged(this, e);
            } 
        }

        /// 
        ///     Initialization of this element is about to begin; 
        ///     no implicit Refresh occurs until the matched EndInit is called
        ///  
        protected virtual void BeginInit() 
        {
            ++_deferLevel; 
        }

        /// 
        ///     Initialization of this element has completed; 
        ///     this causes a Refresh if no other deferred refresh is outstanding
        ///  
        protected virtual void EndInit() 
        {
            EndDefer(); 
        }

        #endregion Protected Methods
 
        //------------------------------------------------------
        // 
        //  Private Methods 
        //
        //------------------------------------------------------ 

        #region Private Methods

        private void EndDefer() 
        {
            Debug.Assert(_deferLevel > 0); 
 
            --_deferLevel;
 
            if (_deferLevel == 0)
            {
                Refresh();
            } 
        }
 
        private static object UpdateWithNewResult(object arg) 
        {
            object[] args = (object[]) arg; 
            Invariant.Assert(args.Length == 5);
            DataSourceProvider provider = (DataSourceProvider) args[0];
            Exception error = (Exception) args[1];
            object newData = args[2]; 
            DispatcherOperationCallback completionWork
                = (DispatcherOperationCallback) args[3]; 
            object callbackArgs = args[4]; 

            provider.UpdateWithNewResult(error, newData, completionWork, callbackArgs); 
            return null;
        }

        private void UpdateWithNewResult(Exception error, object newData, DispatcherOperationCallback completionWork, object callbackArgs) 
        {
            bool errorChanged = (_error != error); 
            _error = error; 
            if (error != null)
            { 
                newData = null;
                _initialLoadCalled = false; // allow again InitialLoad after an error
            }
 
            _data = newData;
 
            if (completionWork != null) 
                completionWork(callbackArgs);
 
            // notify any listeners
            OnPropertyChanged(new PropertyChangedEventArgs("Data"));
            if (DataChanged != null)
            { 
                DataChanged(this, EventArgs.Empty);
            } 
            if (errorChanged) 
                OnPropertyChanged(new PropertyChangedEventArgs("Error"));
        } 

        #endregion Private Methods

        //----------------------------------------------------- 
        //
        //  Private Types 
        // 
        //------------------------------------------------------
 

        #region Private Types

        private class DeferHelper : IDisposable 
        {
            public DeferHelper(DataSourceProvider provider) 
            { 
                _provider = provider;
            } 

            public void Dispose()
            {
                GC.SuppressFinalize(this); 
                if (_provider != null)
                { 
                    _provider.EndDefer(); 
                    _provider = null;
                } 
            }

            private DataSourceProvider _provider;
        } 

        #endregion 
 
        //-----------------------------------------------------
        // 
        //  Private Fields
        //
        //-----------------------------------------------------
        private bool _isInitialLoadEnabled = true; 
        private bool _initialLoadCalled;
        private int _deferLevel; 
        private object _data; 
        private Exception _error;
        private Dispatcher _dispatcher; 

        static readonly DispatcherOperationCallback UpdateWithNewResultCallback = new DispatcherOperationCallback(UpdateWithNewResult);

    } 

} 
 

 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK