DataBindEngine.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / Data / DataBindEngine.cs / 4 / DataBindEngine.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: Data binding engine. 
// 
//---------------------------------------------------------------------------
 
using System;
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
            Dispatcher.ShutdownFinished += new EventHandler(OnDispatcherShutDown); 

            // also cleanup during AppDomain shutdown events (bug 121070)
            AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnDispatcherShutDown);
            AppDomain.CurrentDomain.DomainUnload += new EventHandler(OnDispatcherShutDown); 

            // 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 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)
        {
            ViewRecord record = _viewManager.GetViewRecord(collection, key, collectionViewType);
 
            // 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);
            } 
        }

        // 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 OnDispatcherShutDown(object sender, EventArgs e)
        { 
            _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); 
            }
        }

        //----------------------------------------------------- 
        //
        //  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;
        internal static readonly CultureInfo EnglishUSCulture = CultureInfo.GetCultureInfo("en-us"); 

        private ValueTable  _valueTable = new ValueTable(); 
        private AccessorTable _accessorTable = new AccessorTable(); 
        private int         _cleanupRequests;
 
        [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