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; // Dictionaryusing 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; // Dictionaryusing 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- TextRange.cs
- TaskExtensions.cs
- Message.cs
- UpdateRecord.cs
- BindingSource.cs
- SapiAttributeParser.cs
- EqualityComparer.cs
- XmlHelper.cs
- KeyInterop.cs
- FileStream.cs
- DecoderNLS.cs
- LinkLabel.cs
- XPathDescendantIterator.cs
- InternalTypeHelper.cs
- SqlDataReader.cs
- DBSqlParserColumnCollection.cs
- IgnoreSectionHandler.cs
- _TransmitFileOverlappedAsyncResult.cs
- XsdCachingReader.cs
- AttributeSetAction.cs
- InvalidWMPVersionException.cs
- BezierSegment.cs
- HostingEnvironmentWrapper.cs
- TextEditorLists.cs
- AssociationSet.cs
- Subset.cs
- X500Name.cs
- ExceptionHelpers.cs
- SafeNativeMethods.cs
- ProcessManager.cs
- AccessKeyManager.cs
- DataListItemCollection.cs
- PageThemeCodeDomTreeGenerator.cs
- LinqDataView.cs
- Int32AnimationUsingKeyFrames.cs
- AllMembershipCondition.cs
- IgnoreFlushAndCloseStream.cs
- VectorConverter.cs
- ParserStreamGeometryContext.cs
- ExtractCollection.cs
- _CookieModule.cs
- AmbientValueAttribute.cs
- Options.cs
- HttpCookieCollection.cs
- SessionPageStatePersister.cs
- AutoCompleteStringCollection.cs
- SimpleRecyclingCache.cs
- SizeKeyFrameCollection.cs
- Sql8ConformanceChecker.cs
- ModulesEntry.cs
- SessionStateItemCollection.cs
- Inflater.cs
- MoveSizeWinEventHandler.cs
- NameValueCollection.cs
- ResXResourceWriter.cs
- GlobalizationSection.cs
- BindingList.cs
- SecurityAttributeGenerationHelper.cs
- WinEventHandler.cs
- StrictAndMessageFilter.cs
- ResourceBinder.cs
- ImageDrawing.cs
- SetIterators.cs
- SubqueryRules.cs
- StreamResourceInfo.cs
- COM2IManagedPerPropertyBrowsingHandler.cs
- DBConcurrencyException.cs
- DefaultAuthorizationContext.cs
- ReadOnlyCollectionBase.cs
- SmiEventStream.cs
- FixedSchema.cs
- SmiContext.cs
- CryptoKeySecurity.cs
- DurableErrorHandler.cs
- DBSqlParserTableCollection.cs
- SafeHandles.cs
- VScrollBar.cs
- TemplateBindingExtensionConverter.cs
- Error.cs
- OrderedDictionary.cs
- OrthographicCamera.cs
- UrlPath.cs
- GridViewColumnHeaderAutomationPeer.cs
- KnownIds.cs
- ConstructorBuilder.cs
- TypeContext.cs
- DbExpressionVisitor.cs
- PropertyGridEditorPart.cs
- WebPartVerb.cs
- DataObjectFieldAttribute.cs
- RepeaterItem.cs
- DynamicUpdateCommand.cs
- ListBoxAutomationPeer.cs
- ElementNotEnabledException.cs
- DataGridViewButtonColumn.cs
- PathData.cs
- ClientSettingsStore.cs
- SimpleType.cs
- AccessDataSourceView.cs
- OutputCacheSection.cs