Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / Data / DataBindEngine.cs / 3 / 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; internal static readonly CultureInfo EnglishUSCulture = CultureInfo.GetCultureInfo("en-us"); 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; internal static readonly CultureInfo EnglishUSCulture = CultureInfo.GetCultureInfo("en-us"); 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
- PlanCompiler.cs
- ComponentCollection.cs
- TextEditorMouse.cs
- GenericEnumerator.cs
- BitmapDecoder.cs
- XmlWellformedWriter.cs
- SelectionUIService.cs
- Interfaces.cs
- Command.cs
- SecurityContext.cs
- AssemblyCollection.cs
- SystemPens.cs
- COM2IVsPerPropertyBrowsingHandler.cs
- OleDbFactory.cs
- QueryInterceptorAttribute.cs
- BasicExpressionVisitor.cs
- XmlDataLoader.cs
- Point4DConverter.cs
- AttachmentService.cs
- ImageClickEventArgs.cs
- XmlAttributeAttribute.cs
- WebRequestModuleElementCollection.cs
- FindCompletedEventArgs.cs
- Column.cs
- CellTreeNodeVisitors.cs
- TextCompositionManager.cs
- _LocalDataStoreMgr.cs
- WsatConfiguration.cs
- XPathPatternParser.cs
- StructuredTypeEmitter.cs
- DocumentCollection.cs
- ObjectListCommandCollection.cs
- HitTestDrawingContextWalker.cs
- PhysicalAddress.cs
- Ipv6Element.cs
- SchemaElementDecl.cs
- DbSourceParameterCollection.cs
- RadialGradientBrush.cs
- AlphabeticalEnumConverter.cs
- SQLInt64.cs
- LocalBuilder.cs
- MimeXmlReflector.cs
- ToolStripDropDownClosedEventArgs.cs
- PhoneCall.cs
- _DisconnectOverlappedAsyncResult.cs
- IsolatedStorageException.cs
- FieldNameLookup.cs
- SafeBitVector32.cs
- NavigateUrlConverter.cs
- ApplicationServiceHelper.cs
- RemoveStoryboard.cs
- CustomAttributeSerializer.cs
- DSACryptoServiceProvider.cs
- OLEDB_Util.cs
- SqlConnectionPoolProviderInfo.cs
- TdsParserStaticMethods.cs
- SamlAuthorizationDecisionStatement.cs
- FormsIdentity.cs
- MasterPageParser.cs
- PositiveTimeSpanValidator.cs
- TreeViewItemAutomationPeer.cs
- KoreanCalendar.cs
- XmlElementList.cs
- DataExpression.cs
- IDataContractSurrogate.cs
- MorphHelper.cs
- IssuanceLicense.cs
- Polyline.cs
- Base64Decoder.cs
- LoginName.cs
- VirtualizingPanel.cs
- BadImageFormatException.cs
- Stack.cs
- InheritanceContextHelper.cs
- FontSourceCollection.cs
- MSAAWinEventWrap.cs
- EncoderFallback.cs
- FlowNode.cs
- TableRowCollection.cs
- StyleCollection.cs
- Button.cs
- ColumnCollectionEditor.cs
- SettingsAttributes.cs
- OutputWindow.cs
- XPathNavigatorReader.cs
- LinkAreaEditor.cs
- ContentType.cs
- ContextDataSourceContextData.cs
- TemplateFactory.cs
- MessageLogTraceRecord.cs
- TypeToken.cs
- Ticks.cs
- HighContrastHelper.cs
- Resources.Designer.cs
- InputLanguageSource.cs
- TextContainerChangeEventArgs.cs
- ExpressionBuilderCollection.cs
- DesignerVerb.cs
- SudsParser.cs
- TimeSpanSecondsOrInfiniteConverter.cs