Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / Data / DataBindEngine.cs / 1305600 / 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 System.Windows.Markup; using MS.Internal.Data; using MS.Internal; // Invariant.Assert namespace MS.Internal.Data { internal enum TaskOps { TransferValue, UpdateValue, AttachToContext, VerifySourceReference, RaiseTargetUpdatedEvent, } internal interface IDataBindEngineClient { void TransferValue(); void UpdateValue(); bool AttachToContext(bool lastChance); void VerifySourceReference(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; case TaskOps.VerifySourceReference: client.VerifySourceReference(lastChance); 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 IsShutDown { get { return (_viewManager == null); } } 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) { if (IsShutDown) return null; 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); } } // 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); } } 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, object sender, EventArgs e) { 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; [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
- LineGeometry.cs
- DataTrigger.cs
- UserControl.cs
- TraceUtility.cs
- OperandQuery.cs
- SmiEventSink.cs
- Int16Animation.cs
- TypeToken.cs
- StrokeCollectionDefaultValueFactory.cs
- CompositeControl.cs
- IconHelper.cs
- OutputWindow.cs
- WebCodeGenerator.cs
- MenuItem.cs
- BinaryFormatter.cs
- PreloadedPackages.cs
- PopupRoot.cs
- ControlCachePolicy.cs
- SizeIndependentAnimationStorage.cs
- TextBoxBase.cs
- ExpressionWriter.cs
- FamilyTypeface.cs
- IpcChannelHelper.cs
- DropDownButton.cs
- Soap.cs
- DefaultObjectMappingItemCollection.cs
- MiniModule.cs
- Win32.cs
- FileUpload.cs
- BindingCollection.cs
- InputBinding.cs
- GestureRecognizer.cs
- XmlSchemaProviderAttribute.cs
- XPathBinder.cs
- FullTextLine.cs
- DoubleAnimationUsingKeyFrames.cs
- DateBoldEvent.cs
- QilInvoke.cs
- XmlSchemaAnyAttribute.cs
- _ShellExpression.cs
- BoundField.cs
- GeneralTransformCollection.cs
- XmlIlTypeHelper.cs
- Formatter.cs
- NativeMethods.cs
- DbParameterHelper.cs
- ComplexLine.cs
- CodeNamespaceCollection.cs
- SqlUnionizer.cs
- CodeAccessSecurityEngine.cs
- SchemaCompiler.cs
- ConfigurationPermission.cs
- BinaryWriter.cs
- EncryptedXml.cs
- SmiSettersStream.cs
- ImageMap.cs
- Help.cs
- TabPage.cs
- BitmapEffectGroup.cs
- ThrowOnMultipleAssignment.cs
- MenuItemStyleCollection.cs
- RepeatInfo.cs
- GlyphCache.cs
- DataPointer.cs
- CommonDialog.cs
- OrderedDictionary.cs
- LineServicesRun.cs
- linebase.cs
- httpstaticobjectscollection.cs
- VerticalAlignConverter.cs
- KnownTypesHelper.cs
- EdmItemCollection.cs
- ValueQuery.cs
- WindowsSolidBrush.cs
- LinearGradientBrush.cs
- LoginView.cs
- HashHelper.cs
- NavigationWindowAutomationPeer.cs
- ModuleBuilder.cs
- SoapTypeAttribute.cs
- HandleCollector.cs
- DataGridDetailsPresenterAutomationPeer.cs
- PrimitiveCodeDomSerializer.cs
- RequestCachePolicy.cs
- SemaphoreFullException.cs
- TraceSwitch.cs
- DrawingContextDrawingContextWalker.cs
- GridViewRowCollection.cs
- DataErrorValidationRule.cs
- TextMarkerSource.cs
- SqlException.cs
- CharEnumerator.cs
- ContainerFilterService.cs
- EntityProviderServices.cs
- DataReaderContainer.cs
- VerticalAlignConverter.cs
- LabelLiteral.cs
- DeclarativeExpressionConditionDeclaration.cs
- MulticastOption.cs
- CatalogPart.cs