DataBindEngine.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Framework / MS / Internal / Data / DataBindEngine.cs / 2 / DataBindEngine.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: Data binding engine. 
// 
//---------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;   // Dictionary
using System.ComponentModel;
using System.Diagnostics; 
using System.Collections;
using System.Collections.Specialized; 
using System.Globalization; 
using System.Windows.Threading;
using System.Security;              // [SecurityCritical,SecurityTreatAsSafe] 
using System.Threading;

using System.Windows;
using System.Windows.Data; 
using MS.Internal.Data;
using MS.Internal;          // Invariant.Assert 
 
namespace MS.Internal.Data
{ 
    internal enum TaskOps
    {
        TransferValue,
        UpdateValue, 
        AttachToContext,
        RaiseTargetUpdatedEvent, 
    } 

    internal interface IDataBindEngineClient 
    {
        void TransferValue();
        void UpdateValue();
        bool AttachToContext(bool lastChance); 
        void OnTargetUpdated();
        DependencyObject TargetElement { get; } 
    } 

    internal class DataBindEngine : DispatcherObject 
    {
        //-----------------------------------------------------
        //
        //  Nested classes 
        //
        //----------------------------------------------------- 
 
        // The task list is represented by a singly linked list of Tasks
        // connected via the Next pointer.  The variables _head and _tail 
        // point to the beginning and end of the list.  The head is always
        // a dummy Task that is never used for anything else - this makes
        // the code for adding to the list simpler.
        // 
        // In addition, all tasks for a particular client are linked in
        // reverse order of arrival by the PreviousForClient back pointer. 
        // The heads of these lists are kept in the _mostRecentForClient 
        // hashtable.  This allows rapid cancellation of all tasks pending
        // for a particular client - we only need to look at the tasks that 
        // are actually affected, rather than the entire list.  This avoids
        // an O(n^2) algorithm (bug 1366032).

        private class Task 
        {
            public enum Status { Pending, Running, Completed, Retry, Cancelled }; 
            public IDataBindEngineClient client; 
            public TaskOps op;
            public Status status; 
            public Task Next;
            public Task PreviousForClient;

            public Task(IDataBindEngineClient c, TaskOps o, Task previousForClient) 
            {
                client = c; 
                op = o; 
                PreviousForClient = previousForClient;
                status = Status.Pending; 
            }

            public void Run(bool lastChance)
            { 
                status = Status.Running;
                Status newStatus = Status.Completed; 
                switch (op) 
                {
                    case TaskOps.TransferValue: 
                        client.TransferValue();
                        break;

                    case TaskOps.UpdateValue: 
                        client.UpdateValue();
                        break; 
 
                    case TaskOps.RaiseTargetUpdatedEvent:
                        client.OnTargetUpdated(); 
                        break;

                    case TaskOps.AttachToContext:
                        bool succeeded = client.AttachToContext(lastChance); 
                        if (!succeeded && !lastChance)
                            newStatus = Status.Retry; 
                        break; 
                }
                status = newStatus; 
            }
        }

        //------------------------------------------------------ 
        //
        //  Constructors 
        // 
        //-----------------------------------------------------
 
        /// 
        ///     Critical: This code calls into Link demanded methods to attach handlers
        ///     TreatAsSafe: This code does not take any parameter or return state.
        ///     It simply attaches private call back. 
        /// 
        [SecurityCritical,SecurityTreatAsSafe] 
        private DataBindEngine() 
        {
            // Set up the final cleanup 
            DataBindEngineShutDownListener listener = new DataBindEngineShutDownListener(this);

            // initialize the task list
            _head = new Task(null, TaskOps.TransferValue, null); 
            _tail = _head;
            _mostRecentTask = new HybridDictionary(); 
        } 

        //------------------------------------------------------ 
        //
        //  Internal Properties
        //
        //------------------------------------------------------ 

        internal PathParser PathParser { get { return _pathParser; } } 
        internal ValueConverterContext ValueConverterContext { get { return _valueConverterContext; } } 
        internal AccessorTable AccessorTable { get { return _accessorTable; } }
 
        internal bool CleanupEnabled
        {
            get { return _cleanupEnabled; }
            set 
            {
                _cleanupEnabled = value; 
                WeakEventManager.SetCleanupEnabled(value); 
            }
        } 

        internal IAsyncDataDispatcher AsyncDataDispatcher
        {
            get 
            {
                // lazy construction of async dispatcher 
                if (_defaultAsyncDataDispatcher == null) 
                    _defaultAsyncDataDispatcher = new DefaultAsyncDataDispatcher();
 
                return _defaultAsyncDataDispatcher;
            }
        }
 
        /// 
        /// Return the DataBindEngine for the current thread 
        ///  
        internal static DataBindEngine CurrentDataBindEngine
        { 
            get
            {
                // _currentEngine is [ThreadStatic], so there's one per thread
                if (_currentEngine == null) 
                {
                    _currentEngine = new DataBindEngine(); 
                } 

                return _currentEngine; 
            }
        }

 
        //-----------------------------------------------------
        // 
        //  Internal Methods 
        //
        //------------------------------------------------------ 

        internal void AddTask(IDataBindEngineClient c, TaskOps op)
        {
            // ignore requests that arrive after shutdown 
            if (_mostRecentTask == null)
                return; 
 
            // if we're adding to an empty list, request that the list be processed
            if (_head == _tail) 
            {
                RequestRun();
            }
 
            // link a new task into the list
            Task recentTask = (Task)_mostRecentTask[c]; 
            Task newTask = new Task(c, op, recentTask); 
            _tail.Next = newTask;
            _tail = newTask; 
            _mostRecentTask[c] = newTask;

            // if the task is AttachToContext and the target is a UIElement,
            // register for the LayoutUpdated event, and run the task list from the 
            // event handler.  This avoids flashing, at the expense of lots more
            // events and handlers (bug 1019232) 
            if (op == TaskOps.AttachToContext && 
                _layoutElement == null &&
                (_layoutElement = c.TargetElement as UIElement) != null) 
            {
                _layoutElement.LayoutUpdated += new EventHandler(OnLayoutUpdated);
            }
        } 

        internal void CancelTask(IDataBindEngineClient c, TaskOps op) 
        { 
            // ignore requests that arrive after shutdown
            if (_mostRecentTask == null) 
                return;

            for (Task task = (Task)_mostRecentTask[c];  task != null;  task = task.PreviousForClient)
            { 
                if (task.op == op && task.status == Task.Status.Pending)
                { 
                    task.status = Task.Status.Cancelled; 
                    break;
                } 
            }
        }

        internal void CancelTasks(IDataBindEngineClient c) 
        {
            // ignore requests that arrive after shutdown 
            if (_mostRecentTask == null) 
                return;
 
            // cancel pending tasks for the given client
            for (Task task = (Task)_mostRecentTask[c];  task != null;  task = task.PreviousForClient)
            {
                Invariant.Assert(task.client == c, "task list is corrupt"); 
                if (task.status == Task.Status.Pending)
                { 
                    task.status = Task.Status.Cancelled; 
                }
            } 

            // no need to look at these tasks ever again
            _mostRecentTask.Remove(c);
        } 

        internal object Run(object arg) 
        { 
            bool lastChance = (bool)arg;
            Task retryHead = lastChance ? null : new Task(null, TaskOps.TransferValue, null); 
            Task retryTail = retryHead;

            // unregister the LayoutUpdated event - we only need to be called once
            if (_layoutElement != null) 
            {
                _layoutElement.LayoutUpdated -= new EventHandler(OnLayoutUpdated); 
                _layoutElement = null; 
            }
 
            // iterate through the task list
            Task nextTask = null;
            for (Task task = _head.Next;  task != null;  task = nextTask)
            { 
                // sever the back pointer - older tasks are no longer needed
                task.PreviousForClient = null; 
 
                // run pending tasks
                if (task.status == Task.Status.Pending) 
                {
                    task.Run(lastChance);

                    // fetch the next task _after_ the current task has 
                    // run (in case the current task causes new tasks to be
                    // added to the list, as in bug 1938866), but _before_ 
                    // moving the current task to the retry list (which overwrites 
                    // the Next pointer)
                    nextTask = task.Next; 

                    if (task.status == Task.Status.Retry && !lastChance)
                    {
                        // the task needs to be retried - add it to the list 
                        task.status = Task.Status.Pending;
                        retryTail.Next = task; 
                        retryTail = task; 
                        retryTail.Next = null;
                    } 
                }
                else
                {
                    nextTask = task.Next; 
                }
            } 
 
            // return the list to its empty state
            _head.Next = null; 
            _tail = _head;
            _mostRecentTask.Clear();

            // repost the tasks that need to be retried 
            if (!lastChance)
            { 
                // there is already a dispatcher request to call Run, so change 
                // _head temporarily so that AddTask does not make another request
                Task headSave = _head; 
                _head = null;

                for (Task task = retryHead.Next;  task != null;  task = task.Next)
                { 
                    AddTask(task.client, task.op);
                } 
 
                _head = headSave;
            } 

            return null;
        }
 
        internal ViewRecord GetViewRecord(object collection, CollectionViewSource key, Type collectionViewType, bool createView)
        { 
            ViewRecord record = _viewManager.GetViewRecord(collection, key, collectionViewType, createView); 

            // lacking any definitive event on which to trigger a cleanup pass, 
            // we use a heuristic, namely the creation of a new view.  This suggests
            // that there is new activity, which often means that old content is
            // being replaced.  So perhaps the view table now has stale entries.
            if (record != null && !record.IsInitialized) 
            {
                ScheduleCleanup(); 
            } 

            return record; 
        }

        // cache of default converters (so that all uses of string-to-int can
        // share the same converter) 
        internal IValueConverter GetDefaultValueConverter(Type sourceType,
                                                        Type targetType, 
                                                        bool targetToSource) 
        {
            IValueConverter result = _valueConverterTable[sourceType, targetType, targetToSource]; 

            if (result == null)
            {
                result = DefaultValueConverter.Create(sourceType, targetType, targetToSource, this); 
                if (result != null)
                    _valueConverterTable.Add(sourceType, targetType, targetToSource, result); 
            } 

            return result; 
        }

        // make an async request to the scheduler that handles requests for the given target
        internal void AddAsyncRequest(DependencyObject target, AsyncDataRequest request) 
        {
            if (target == null) 
                return; 

            // get the appropriate scheduler 
            IAsyncDataDispatcher asyncDispatcher = AsyncDataDispatcher;
            /* AsyncDataDispatcher property is cut (task 41079)
            IAsyncDataDispatcher asyncDispatcher = Binding.GetAsyncDataDispatcher(target);
            if (asyncDispatcher == null) 
            {
                asyncDispatcher = AsyncDataDispatcher; 
            } 
            */
 
            // add it to the list of schedulers that need cleanup
            if (_asyncDispatchers == null)
            {
                _asyncDispatchers = new HybridDictionary(1);    // lazy instantiation 
            }
            _asyncDispatchers[asyncDispatcher] = null;  // the value is unused 
 
            // make the request
            asyncDispatcher.AddRequest(request); 
        }


        // 

 
 

        // retrieve the value, using the cache if necessary 
        internal object GetValue(object item, PropertyDescriptor pd, bool indexerIsNext)
        {
            return _valueTable.GetValue(item, pd, indexerIsNext);
        } 

        // give the value cache first chance at handling property changes 
        internal void RegisterForCacheChanges(object item, object descriptor) 
        {
            PropertyDescriptor pd = descriptor as PropertyDescriptor; 
            if (item != null && pd != null && ValueTable.ShouldCache(item, pd))
            {
                _valueTable.RegisterForChanges(item, pd, this);
            } 
        }
 
        // see whether the type implements INullable, without loading System.Data unnecessarily 
        internal bool IsINullable(Type type)
        { 
            CachedTypeInfo cti;

            if (!_typeInfoTable.TryGetValue(type, out cti))
            { 
                cti = new CachedTypeInfo();
                _typeInfoTable.Add(type, cti); 
 
                Type[] interfaces = type.FindInterfaces(new System.Reflection.TypeFilter(FilterForFullName),
                                                            "System.Data.SqlTypes.INullable"); 

                cti.IsINullable = (interfaces.Length > 0 && IsINullableImpl(interfaces[0]));
            }
 
            return cti.IsINullable;
        } 
 
        // separate function to avoid loading System.Data unnecessarily
        [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] 
        static bool IsINullableImpl(Type type)
        {
            return (type == typeof(System.Data.SqlTypes.INullable));
        } 

        static bool FilterForFullName(Type type, object criterion) 
        { 
            string fullName = (String)criterion;
            return String.Equals(type.FullName, fullName, StringComparison.Ordinal); 
        }

        // schedule a cleanup pass.  This can be called from any thread.
        internal void ScheduleCleanup() 
        {
            // only the first request after a previous cleanup should schedule real work 
            if (Interlocked.Increment(ref _cleanupRequests) == 1) 
            {
                Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new DispatcherOperationCallback(CleanupOperation), null); 
            }
        }

        // return true if something was actually cleaned up 
        internal bool Cleanup()
        { 
            bool foundDirt = false; 

            foundDirt = _viewManager.Purge() || foundDirt; 

            foundDirt = WeakEventManager.Cleanup() || foundDirt;

            foundDirt = _valueTable.Purge() || foundDirt; 

            return foundDirt; 
        } 

        // Event registration 


        //-----------------------------------------------------
        // 
        //  Private Methods
        // 
        //----------------------------------------------------- 

        private void RequestRun() 
        {
            // Run tasks before layout, to front load as much layout work as possible
            Dispatcher.BeginInvoke(DispatcherPriority.DataBind, new DispatcherOperationCallback(Run), false);
 
            // Run tasks (especially re-tried AttachToContext tasks) again after
            // layout as the last chance.  Any failures in AttachToContext will 
            // be treated as an error. 
            Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(Run), true);
        } 

        // run a cleanup pass
        private object CleanupOperation(object arg)
        { 
            // allow new requests, even if cleanup is disabled
            Interlocked.Exchange(ref _cleanupRequests, 0); 
 
            if (!_cleanupEnabled)
                return null; 

            Cleanup();

            return null; 
        }
 
        // do the final cleanup when the Dispatcher or AppDomain is shut down 
        private void OnShutDown()
        { 
            _viewManager = null;
            _valueConverterTable = null;
            _mostRecentTask = null;
            _head = _tail = null; 

            // notify all the async dispatchers we've ever talked to 
            // The InterlockedExchange makes sure we only do this once 
            // (in case Dispatcher and AppDomain are being shut down simultaneously
            // on two different threads) 
            HybridDictionary asyncDispatchers = (HybridDictionary)Interlocked.Exchange(ref _asyncDispatchers, null);
            if (asyncDispatchers != null)
            {
                foreach (object o in asyncDispatchers.Keys) 
                {
                    IAsyncDataDispatcher dispatcher = o as IAsyncDataDispatcher; 
                    if (dispatcher != null) 
                    {
                        dispatcher.CancelAllRequests(); 
                    }
                }
            }
 
            _defaultAsyncDataDispatcher = null;
 
            // Note: the engine is still held in TLS.  This maintains the 1-1 relationship 
            // between the thread and the engine.  However the engine is basically
            // dead - _mostRecentTask is null, and most operations are now no-ops or illegal. 
            // This imitates the behavior of the thread's Dispatcher.
        }

        // A UIElement with pending AttachToContext task(s) has raised the 
        // LayoutUpdated event.  Run the task list.
        private void OnLayoutUpdated(object sender, EventArgs e) 
        { 
            Run(false);
        } 

        //-----------------------------------------------------
        //
        //  Private Types 
        //
        //------------------------------------------------------ 
 
        // cache of default value converters (so that all uses of string-to-int can
        // share the same converter) 
        class ValueConverterTable : Hashtable
        {
            struct Key
            { 
                Type _sourceType, _targetType;
                bool _targetToSource; 
 
                public Key(Type sourceType, Type targetType, bool targetToSource)
                { 
                    _sourceType = sourceType;
                    _targetType = targetType;
                    _targetToSource = targetToSource;
                } 

                public override int GetHashCode() 
                { 
                    return _sourceType.GetHashCode() + _targetType.GetHashCode();
                } 

                public override bool Equals(object o)
                {
                    if (o is Key) 
                    {
                        return (this == (Key)o); 
                    } 
                    return false;
                } 

                public static bool operator==(Key k1, Key k2)
                {
                    return  k1._sourceType == k2._sourceType && 
                            k1._targetType == k2._targetType &&
                            k1._targetToSource == k2._targetToSource; 
                } 

                public static bool operator!=(Key k1, Key k2) 
                {
                    return !(k1 == k2);
                }
            } 

            public IValueConverter this[Type sourceType, Type targetType, bool targetToSource] 
            { 
                get
                { 
                    Key key = new Key(sourceType, targetType, targetToSource);
                    object value = base[key];
                    return (IValueConverter)value;
                } 
            }
 
            public void Add(Type sourceType, Type targetType, bool targetToSource, IValueConverter value) 
            {
                base.Add(new Key(sourceType, targetType, targetToSource), value); 
            }
        }

        // information about types 
        class CachedTypeInfo
        { 
            public bool IsINullable 
            {
                get { return _isINullable; } 
                set { _isINullable = value; }
            }

            bool _isINullable; 
        }
 
        private sealed class DataBindEngineShutDownListener : ShutDownListener 
        {
            ///  
            ///     Critical: accesses AppDomain.DomainUnload event
            ///     TreatAsSafe: This code does not take any parameter or return state.
            ///                  It simply attaches private callbacks.
            ///  
            [SecurityCritical,SecurityTreatAsSafe]
            public DataBindEngineShutDownListener(DataBindEngine target) : base(target) 
            { 
            }
 
            internal override void OnShutDown(object target)
            {
                DataBindEngine table = (DataBindEngine)target;
                table.OnShutDown(); 
            }
        } 
 
        //-----------------------------------------------------
        // 
        //  Private Fields
        //
        //------------------------------------------------------
 
        private HybridDictionary _mostRecentTask;           // client --> Task
        Task                _head; 
        Task                _tail; 
        private UIElement   _layoutElement;
        private ViewManager _viewManager = new ViewManager(); 
        private ValueConverterTable _valueConverterTable = new ValueConverterTable();
        private PathParser  _pathParser = new PathParser();
        private IAsyncDataDispatcher _defaultAsyncDataDispatcher;
        private HybridDictionary _asyncDispatchers; 
        private ValueConverterContext _valueConverterContext = new ValueConverterContext();
 
        private bool        _cleanupEnabled = true; 

        private ValueTable  _valueTable = new ValueTable(); 
        private AccessorTable _accessorTable = new AccessorTable();
        private int         _cleanupRequests;

        private Dictionary _typeInfoTable = new Dictionary(); 

        [ThreadStatic] 
        private static DataBindEngine   _currentEngine; // one engine per thread 
    }
 
}

// 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: Data binding engine. 
// 
//---------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;   // Dictionary
using System.ComponentModel;
using System.Diagnostics; 
using System.Collections;
using System.Collections.Specialized; 
using System.Globalization; 
using System.Windows.Threading;
using System.Security;              // [SecurityCritical,SecurityTreatAsSafe] 
using System.Threading;

using System.Windows;
using System.Windows.Data; 
using MS.Internal.Data;
using MS.Internal;          // Invariant.Assert 
 
namespace MS.Internal.Data
{ 
    internal enum TaskOps
    {
        TransferValue,
        UpdateValue, 
        AttachToContext,
        RaiseTargetUpdatedEvent, 
    } 

    internal interface IDataBindEngineClient 
    {
        void TransferValue();
        void UpdateValue();
        bool AttachToContext(bool lastChance); 
        void OnTargetUpdated();
        DependencyObject TargetElement { get; } 
    } 

    internal class DataBindEngine : DispatcherObject 
    {
        //-----------------------------------------------------
        //
        //  Nested classes 
        //
        //----------------------------------------------------- 
 
        // The task list is represented by a singly linked list of Tasks
        // connected via the Next pointer.  The variables _head and _tail 
        // point to the beginning and end of the list.  The head is always
        // a dummy Task that is never used for anything else - this makes
        // the code for adding to the list simpler.
        // 
        // In addition, all tasks for a particular client are linked in
        // reverse order of arrival by the PreviousForClient back pointer. 
        // The heads of these lists are kept in the _mostRecentForClient 
        // hashtable.  This allows rapid cancellation of all tasks pending
        // for a particular client - we only need to look at the tasks that 
        // are actually affected, rather than the entire list.  This avoids
        // an O(n^2) algorithm (bug 1366032).

        private class Task 
        {
            public enum Status { Pending, Running, Completed, Retry, Cancelled }; 
            public IDataBindEngineClient client; 
            public TaskOps op;
            public Status status; 
            public Task Next;
            public Task PreviousForClient;

            public Task(IDataBindEngineClient c, TaskOps o, Task previousForClient) 
            {
                client = c; 
                op = o; 
                PreviousForClient = previousForClient;
                status = Status.Pending; 
            }

            public void Run(bool lastChance)
            { 
                status = Status.Running;
                Status newStatus = Status.Completed; 
                switch (op) 
                {
                    case TaskOps.TransferValue: 
                        client.TransferValue();
                        break;

                    case TaskOps.UpdateValue: 
                        client.UpdateValue();
                        break; 
 
                    case TaskOps.RaiseTargetUpdatedEvent:
                        client.OnTargetUpdated(); 
                        break;

                    case TaskOps.AttachToContext:
                        bool succeeded = client.AttachToContext(lastChance); 
                        if (!succeeded && !lastChance)
                            newStatus = Status.Retry; 
                        break; 
                }
                status = newStatus; 
            }
        }

        //------------------------------------------------------ 
        //
        //  Constructors 
        // 
        //-----------------------------------------------------
 
        /// 
        ///     Critical: This code calls into Link demanded methods to attach handlers
        ///     TreatAsSafe: This code does not take any parameter or return state.
        ///     It simply attaches private call back. 
        /// 
        [SecurityCritical,SecurityTreatAsSafe] 
        private DataBindEngine() 
        {
            // Set up the final cleanup 
            DataBindEngineShutDownListener listener = new DataBindEngineShutDownListener(this);

            // initialize the task list
            _head = new Task(null, TaskOps.TransferValue, null); 
            _tail = _head;
            _mostRecentTask = new HybridDictionary(); 
        } 

        //------------------------------------------------------ 
        //
        //  Internal Properties
        //
        //------------------------------------------------------ 

        internal PathParser PathParser { get { return _pathParser; } } 
        internal ValueConverterContext ValueConverterContext { get { return _valueConverterContext; } } 
        internal AccessorTable AccessorTable { get { return _accessorTable; } }
 
        internal bool CleanupEnabled
        {
            get { return _cleanupEnabled; }
            set 
            {
                _cleanupEnabled = value; 
                WeakEventManager.SetCleanupEnabled(value); 
            }
        } 

        internal IAsyncDataDispatcher AsyncDataDispatcher
        {
            get 
            {
                // lazy construction of async dispatcher 
                if (_defaultAsyncDataDispatcher == null) 
                    _defaultAsyncDataDispatcher = new DefaultAsyncDataDispatcher();
 
                return _defaultAsyncDataDispatcher;
            }
        }
 
        /// 
        /// Return the DataBindEngine for the current thread 
        ///  
        internal static DataBindEngine CurrentDataBindEngine
        { 
            get
            {
                // _currentEngine is [ThreadStatic], so there's one per thread
                if (_currentEngine == null) 
                {
                    _currentEngine = new DataBindEngine(); 
                } 

                return _currentEngine; 
            }
        }

 
        //-----------------------------------------------------
        // 
        //  Internal Methods 
        //
        //------------------------------------------------------ 

        internal void AddTask(IDataBindEngineClient c, TaskOps op)
        {
            // ignore requests that arrive after shutdown 
            if (_mostRecentTask == null)
                return; 
 
            // if we're adding to an empty list, request that the list be processed
            if (_head == _tail) 
            {
                RequestRun();
            }
 
            // link a new task into the list
            Task recentTask = (Task)_mostRecentTask[c]; 
            Task newTask = new Task(c, op, recentTask); 
            _tail.Next = newTask;
            _tail = newTask; 
            _mostRecentTask[c] = newTask;

            // if the task is AttachToContext and the target is a UIElement,
            // register for the LayoutUpdated event, and run the task list from the 
            // event handler.  This avoids flashing, at the expense of lots more
            // events and handlers (bug 1019232) 
            if (op == TaskOps.AttachToContext && 
                _layoutElement == null &&
                (_layoutElement = c.TargetElement as UIElement) != null) 
            {
                _layoutElement.LayoutUpdated += new EventHandler(OnLayoutUpdated);
            }
        } 

        internal void CancelTask(IDataBindEngineClient c, TaskOps op) 
        { 
            // ignore requests that arrive after shutdown
            if (_mostRecentTask == null) 
                return;

            for (Task task = (Task)_mostRecentTask[c];  task != null;  task = task.PreviousForClient)
            { 
                if (task.op == op && task.status == Task.Status.Pending)
                { 
                    task.status = Task.Status.Cancelled; 
                    break;
                } 
            }
        }

        internal void CancelTasks(IDataBindEngineClient c) 
        {
            // ignore requests that arrive after shutdown 
            if (_mostRecentTask == null) 
                return;
 
            // cancel pending tasks for the given client
            for (Task task = (Task)_mostRecentTask[c];  task != null;  task = task.PreviousForClient)
            {
                Invariant.Assert(task.client == c, "task list is corrupt"); 
                if (task.status == Task.Status.Pending)
                { 
                    task.status = Task.Status.Cancelled; 
                }
            } 

            // no need to look at these tasks ever again
            _mostRecentTask.Remove(c);
        } 

        internal object Run(object arg) 
        { 
            bool lastChance = (bool)arg;
            Task retryHead = lastChance ? null : new Task(null, TaskOps.TransferValue, null); 
            Task retryTail = retryHead;

            // unregister the LayoutUpdated event - we only need to be called once
            if (_layoutElement != null) 
            {
                _layoutElement.LayoutUpdated -= new EventHandler(OnLayoutUpdated); 
                _layoutElement = null; 
            }
 
            // iterate through the task list
            Task nextTask = null;
            for (Task task = _head.Next;  task != null;  task = nextTask)
            { 
                // sever the back pointer - older tasks are no longer needed
                task.PreviousForClient = null; 
 
                // run pending tasks
                if (task.status == Task.Status.Pending) 
                {
                    task.Run(lastChance);

                    // fetch the next task _after_ the current task has 
                    // run (in case the current task causes new tasks to be
                    // added to the list, as in bug 1938866), but _before_ 
                    // moving the current task to the retry list (which overwrites 
                    // the Next pointer)
                    nextTask = task.Next; 

                    if (task.status == Task.Status.Retry && !lastChance)
                    {
                        // the task needs to be retried - add it to the list 
                        task.status = Task.Status.Pending;
                        retryTail.Next = task; 
                        retryTail = task; 
                        retryTail.Next = null;
                    } 
                }
                else
                {
                    nextTask = task.Next; 
                }
            } 
 
            // return the list to its empty state
            _head.Next = null; 
            _tail = _head;
            _mostRecentTask.Clear();

            // repost the tasks that need to be retried 
            if (!lastChance)
            { 
                // there is already a dispatcher request to call Run, so change 
                // _head temporarily so that AddTask does not make another request
                Task headSave = _head; 
                _head = null;

                for (Task task = retryHead.Next;  task != null;  task = task.Next)
                { 
                    AddTask(task.client, task.op);
                } 
 
                _head = headSave;
            } 

            return null;
        }
 
        internal ViewRecord GetViewRecord(object collection, CollectionViewSource key, Type collectionViewType, bool createView)
        { 
            ViewRecord record = _viewManager.GetViewRecord(collection, key, collectionViewType, createView); 

            // lacking any definitive event on which to trigger a cleanup pass, 
            // we use a heuristic, namely the creation of a new view.  This suggests
            // that there is new activity, which often means that old content is
            // being replaced.  So perhaps the view table now has stale entries.
            if (record != null && !record.IsInitialized) 
            {
                ScheduleCleanup(); 
            } 

            return record; 
        }

        // cache of default converters (so that all uses of string-to-int can
        // share the same converter) 
        internal IValueConverter GetDefaultValueConverter(Type sourceType,
                                                        Type targetType, 
                                                        bool targetToSource) 
        {
            IValueConverter result = _valueConverterTable[sourceType, targetType, targetToSource]; 

            if (result == null)
            {
                result = DefaultValueConverter.Create(sourceType, targetType, targetToSource, this); 
                if (result != null)
                    _valueConverterTable.Add(sourceType, targetType, targetToSource, result); 
            } 

            return result; 
        }

        // make an async request to the scheduler that handles requests for the given target
        internal void AddAsyncRequest(DependencyObject target, AsyncDataRequest request) 
        {
            if (target == null) 
                return; 

            // get the appropriate scheduler 
            IAsyncDataDispatcher asyncDispatcher = AsyncDataDispatcher;
            /* AsyncDataDispatcher property is cut (task 41079)
            IAsyncDataDispatcher asyncDispatcher = Binding.GetAsyncDataDispatcher(target);
            if (asyncDispatcher == null) 
            {
                asyncDispatcher = AsyncDataDispatcher; 
            } 
            */
 
            // add it to the list of schedulers that need cleanup
            if (_asyncDispatchers == null)
            {
                _asyncDispatchers = new HybridDictionary(1);    // lazy instantiation 
            }
            _asyncDispatchers[asyncDispatcher] = null;  // the value is unused 
 
            // make the request
            asyncDispatcher.AddRequest(request); 
        }


        // 

 
 

        // retrieve the value, using the cache if necessary 
        internal object GetValue(object item, PropertyDescriptor pd, bool indexerIsNext)
        {
            return _valueTable.GetValue(item, pd, indexerIsNext);
        } 

        // give the value cache first chance at handling property changes 
        internal void RegisterForCacheChanges(object item, object descriptor) 
        {
            PropertyDescriptor pd = descriptor as PropertyDescriptor; 
            if (item != null && pd != null && ValueTable.ShouldCache(item, pd))
            {
                _valueTable.RegisterForChanges(item, pd, this);
            } 
        }
 
        // see whether the type implements INullable, without loading System.Data unnecessarily 
        internal bool IsINullable(Type type)
        { 
            CachedTypeInfo cti;

            if (!_typeInfoTable.TryGetValue(type, out cti))
            { 
                cti = new CachedTypeInfo();
                _typeInfoTable.Add(type, cti); 
 
                Type[] interfaces = type.FindInterfaces(new System.Reflection.TypeFilter(FilterForFullName),
                                                            "System.Data.SqlTypes.INullable"); 

                cti.IsINullable = (interfaces.Length > 0 && IsINullableImpl(interfaces[0]));
            }
 
            return cti.IsINullable;
        } 
 
        // separate function to avoid loading System.Data unnecessarily
        [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] 
        static bool IsINullableImpl(Type type)
        {
            return (type == typeof(System.Data.SqlTypes.INullable));
        } 

        static bool FilterForFullName(Type type, object criterion) 
        { 
            string fullName = (String)criterion;
            return String.Equals(type.FullName, fullName, StringComparison.Ordinal); 
        }

        // schedule a cleanup pass.  This can be called from any thread.
        internal void ScheduleCleanup() 
        {
            // only the first request after a previous cleanup should schedule real work 
            if (Interlocked.Increment(ref _cleanupRequests) == 1) 
            {
                Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new DispatcherOperationCallback(CleanupOperation), null); 
            }
        }

        // return true if something was actually cleaned up 
        internal bool Cleanup()
        { 
            bool foundDirt = false; 

            foundDirt = _viewManager.Purge() || foundDirt; 

            foundDirt = WeakEventManager.Cleanup() || foundDirt;

            foundDirt = _valueTable.Purge() || foundDirt; 

            return foundDirt; 
        } 

        // Event registration 


        //-----------------------------------------------------
        // 
        //  Private Methods
        // 
        //----------------------------------------------------- 

        private void RequestRun() 
        {
            // Run tasks before layout, to front load as much layout work as possible
            Dispatcher.BeginInvoke(DispatcherPriority.DataBind, new DispatcherOperationCallback(Run), false);
 
            // Run tasks (especially re-tried AttachToContext tasks) again after
            // layout as the last chance.  Any failures in AttachToContext will 
            // be treated as an error. 
            Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(Run), true);
        } 

        // run a cleanup pass
        private object CleanupOperation(object arg)
        { 
            // allow new requests, even if cleanup is disabled
            Interlocked.Exchange(ref _cleanupRequests, 0); 
 
            if (!_cleanupEnabled)
                return null; 

            Cleanup();

            return null; 
        }
 
        // do the final cleanup when the Dispatcher or AppDomain is shut down 
        private void OnShutDown()
        { 
            _viewManager = null;
            _valueConverterTable = null;
            _mostRecentTask = null;
            _head = _tail = null; 

            // notify all the async dispatchers we've ever talked to 
            // The InterlockedExchange makes sure we only do this once 
            // (in case Dispatcher and AppDomain are being shut down simultaneously
            // on two different threads) 
            HybridDictionary asyncDispatchers = (HybridDictionary)Interlocked.Exchange(ref _asyncDispatchers, null);
            if (asyncDispatchers != null)
            {
                foreach (object o in asyncDispatchers.Keys) 
                {
                    IAsyncDataDispatcher dispatcher = o as IAsyncDataDispatcher; 
                    if (dispatcher != null) 
                    {
                        dispatcher.CancelAllRequests(); 
                    }
                }
            }
 
            _defaultAsyncDataDispatcher = null;
 
            // Note: the engine is still held in TLS.  This maintains the 1-1 relationship 
            // between the thread and the engine.  However the engine is basically
            // dead - _mostRecentTask is null, and most operations are now no-ops or illegal. 
            // This imitates the behavior of the thread's Dispatcher.
        }

        // A UIElement with pending AttachToContext task(s) has raised the 
        // LayoutUpdated event.  Run the task list.
        private void OnLayoutUpdated(object sender, EventArgs e) 
        { 
            Run(false);
        } 

        //-----------------------------------------------------
        //
        //  Private Types 
        //
        //------------------------------------------------------ 
 
        // cache of default value converters (so that all uses of string-to-int can
        // share the same converter) 
        class ValueConverterTable : Hashtable
        {
            struct Key
            { 
                Type _sourceType, _targetType;
                bool _targetToSource; 
 
                public Key(Type sourceType, Type targetType, bool targetToSource)
                { 
                    _sourceType = sourceType;
                    _targetType = targetType;
                    _targetToSource = targetToSource;
                } 

                public override int GetHashCode() 
                { 
                    return _sourceType.GetHashCode() + _targetType.GetHashCode();
                } 

                public override bool Equals(object o)
                {
                    if (o is Key) 
                    {
                        return (this == (Key)o); 
                    } 
                    return false;
                } 

                public static bool operator==(Key k1, Key k2)
                {
                    return  k1._sourceType == k2._sourceType && 
                            k1._targetType == k2._targetType &&
                            k1._targetToSource == k2._targetToSource; 
                } 

                public static bool operator!=(Key k1, Key k2) 
                {
                    return !(k1 == k2);
                }
            } 

            public IValueConverter this[Type sourceType, Type targetType, bool targetToSource] 
            { 
                get
                { 
                    Key key = new Key(sourceType, targetType, targetToSource);
                    object value = base[key];
                    return (IValueConverter)value;
                } 
            }
 
            public void Add(Type sourceType, Type targetType, bool targetToSource, IValueConverter value) 
            {
                base.Add(new Key(sourceType, targetType, targetToSource), value); 
            }
        }

        // information about types 
        class CachedTypeInfo
        { 
            public bool IsINullable 
            {
                get { return _isINullable; } 
                set { _isINullable = value; }
            }

            bool _isINullable; 
        }
 
        private sealed class DataBindEngineShutDownListener : ShutDownListener 
        {
            ///  
            ///     Critical: accesses AppDomain.DomainUnload event
            ///     TreatAsSafe: This code does not take any parameter or return state.
            ///                  It simply attaches private callbacks.
            ///  
            [SecurityCritical,SecurityTreatAsSafe]
            public DataBindEngineShutDownListener(DataBindEngine target) : base(target) 
            { 
            }
 
            internal override void OnShutDown(object target)
            {
                DataBindEngine table = (DataBindEngine)target;
                table.OnShutDown(); 
            }
        } 
 
        //-----------------------------------------------------
        // 
        //  Private Fields
        //
        //------------------------------------------------------
 
        private HybridDictionary _mostRecentTask;           // client --> Task
        Task                _head; 
        Task                _tail; 
        private UIElement   _layoutElement;
        private ViewManager _viewManager = new ViewManager(); 
        private ValueConverterTable _valueConverterTable = new ValueConverterTable();
        private PathParser  _pathParser = new PathParser();
        private IAsyncDataDispatcher _defaultAsyncDataDispatcher;
        private HybridDictionary _asyncDispatchers; 
        private ValueConverterContext _valueConverterContext = new ValueConverterContext();
 
        private bool        _cleanupEnabled = true; 

        private ValueTable  _valueTable = new ValueTable(); 
        private AccessorTable _accessorTable = new AccessorTable();
        private int         _cleanupRequests;

        private Dictionary _typeInfoTable = new Dictionary(); 

        [ThreadStatic] 
        private static DataBindEngine   _currentEngine; // one engine per thread 
    }
 
}

// 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