Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / Data / DataBindEngine.cs / 4 / DataBindEngine.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: Data binding engine. // //--------------------------------------------------------------------------- using System; using System.ComponentModel; using System.Diagnostics; using System.Collections; using System.Collections.Specialized; using System.Globalization; using System.Windows.Threading; using System.Security; // [SecurityCritical,SecurityTreatAsSafe] using System.Threading; using System.Windows; using System.Windows.Data; using MS.Internal.Data; using MS.Internal; // Invariant.Assert namespace MS.Internal.Data { internal enum TaskOps { TransferValue, UpdateValue, AttachToContext, RaiseTargetUpdatedEvent, } internal interface IDataBindEngineClient { void TransferValue(); void UpdateValue(); bool AttachToContext(bool lastChance); void OnTargetUpdated(); DependencyObject TargetElement { get; } } internal class DataBindEngine : DispatcherObject { //----------------------------------------------------- // // Nested classes // //----------------------------------------------------- // The task list is represented by a singly linked list of Tasks // connected via the Next pointer. The variables _head and _tail // point to the beginning and end of the list. The head is always // a dummy Task that is never used for anything else - this makes // the code for adding to the list simpler. // // In addition, all tasks for a particular client are linked in // reverse order of arrival by the PreviousForClient back pointer. // The heads of these lists are kept in the _mostRecentForClient // hashtable. This allows rapid cancellation of all tasks pending // for a particular client - we only need to look at the tasks that // are actually affected, rather than the entire list. This avoids // an O(n^2) algorithm (bug 1366032). private class Task { public enum Status { Pending, Running, Completed, Retry, Cancelled }; public IDataBindEngineClient client; public TaskOps op; public Status status; public Task Next; public Task PreviousForClient; public Task(IDataBindEngineClient c, TaskOps o, Task previousForClient) { client = c; op = o; PreviousForClient = previousForClient; status = Status.Pending; } public void Run(bool lastChance) { status = Status.Running; Status newStatus = Status.Completed; switch (op) { case TaskOps.TransferValue: client.TransferValue(); break; case TaskOps.UpdateValue: client.UpdateValue(); break; case TaskOps.RaiseTargetUpdatedEvent: client.OnTargetUpdated(); break; case TaskOps.AttachToContext: bool succeeded = client.AttachToContext(lastChance); if (!succeeded && !lastChance) newStatus = Status.Retry; break; } status = newStatus; } } //------------------------------------------------------ // // Constructors // //----------------------------------------------------- ////// Critical: This code calls into Link demanded methods to attach handlers /// TreatAsSafe: This code does not take any parameter or return state. /// It simply attaches private call back. /// [SecurityCritical,SecurityTreatAsSafe] private DataBindEngine() { // Set up the final cleanup Dispatcher.ShutdownFinished += new EventHandler(OnDispatcherShutDown); // also cleanup during AppDomain shutdown events (bug 121070) AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnDispatcherShutDown); AppDomain.CurrentDomain.DomainUnload += new EventHandler(OnDispatcherShutDown); // initialize the task list _head = new Task(null, TaskOps.TransferValue, null); _tail = _head; _mostRecentTask = new HybridDictionary(); } //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ internal PathParser PathParser { get { return _pathParser; } } internal ValueConverterContext ValueConverterContext { get { return _valueConverterContext; } } internal AccessorTable AccessorTable { get { return _accessorTable; } } internal bool CleanupEnabled { get { return _cleanupEnabled; } set { _cleanupEnabled = value; WeakEventManager.SetCleanupEnabled(value); } } internal IAsyncDataDispatcher AsyncDataDispatcher { get { // lazy construction of async dispatcher if (_defaultAsyncDataDispatcher == null) _defaultAsyncDataDispatcher = new DefaultAsyncDataDispatcher(); return _defaultAsyncDataDispatcher; } } ////// Return the DataBindEngine for the current thread /// internal static DataBindEngine CurrentDataBindEngine { get { // _currentEngine is [ThreadStatic], so there's one per thread if (_currentEngine == null) { _currentEngine = new DataBindEngine(); } return _currentEngine; } } //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ internal void AddTask(IDataBindEngineClient c, TaskOps op) { // ignore requests that arrive after shutdown if (_mostRecentTask == null) return; // if we're adding to an empty list, request that the list be processed if (_head == _tail) { RequestRun(); } // link a new task into the list Task recentTask = (Task)_mostRecentTask[c]; Task newTask = new Task(c, op, recentTask); _tail.Next = newTask; _tail = newTask; _mostRecentTask[c] = newTask; // if the task is AttachToContext and the target is a UIElement, // register for the LayoutUpdated event, and run the task list from the // event handler. This avoids flashing, at the expense of lots more // events and handlers (bug 1019232) if (op == TaskOps.AttachToContext && _layoutElement == null && (_layoutElement = c.TargetElement as UIElement) != null) { _layoutElement.LayoutUpdated += new EventHandler(OnLayoutUpdated); } } internal void CancelTasks(IDataBindEngineClient c) { // ignore requests that arrive after shutdown if (_mostRecentTask == null) return; // cancel pending tasks for the given client for (Task task = (Task)_mostRecentTask[c]; task != null; task = task.PreviousForClient) { Invariant.Assert(task.client == c, "task list is corrupt"); if (task.status == Task.Status.Pending) { task.status = Task.Status.Cancelled; } } // no need to look at these tasks ever again _mostRecentTask.Remove(c); } internal object Run(object arg) { bool lastChance = (bool)arg; Task retryHead = lastChance ? null : new Task(null, TaskOps.TransferValue, null); Task retryTail = retryHead; // unregister the LayoutUpdated event - we only need to be called once if (_layoutElement != null) { _layoutElement.LayoutUpdated -= new EventHandler(OnLayoutUpdated); _layoutElement = null; } // iterate through the task list Task nextTask = null; for (Task task = _head.Next; task != null; task = nextTask) { // sever the back pointer - older tasks are no longer needed task.PreviousForClient = null; // run pending tasks if (task.status == Task.Status.Pending) { task.Run(lastChance); // fetch the next task _after_ the current task has // run (in case the current task causes new tasks to be // added to the list, as in bug 1938866), but _before_ // moving the current task to the retry list (which overwrites // the Next pointer) nextTask = task.Next; if (task.status == Task.Status.Retry && !lastChance) { // the task needs to be retried - add it to the list task.status = Task.Status.Pending; retryTail.Next = task; retryTail = task; retryTail.Next = null; } } else { nextTask = task.Next; } } // return the list to its empty state _head.Next = null; _tail = _head; _mostRecentTask.Clear(); // repost the tasks that need to be retried if (!lastChance) { // there is already a dispatcher request to call Run, so change // _head temporarily so that AddTask does not make another request Task headSave = _head; _head = null; for (Task task = retryHead.Next; task != null; task = task.Next) { AddTask(task.client, task.op); } _head = headSave; } return null; } internal ViewRecord GetViewRecord(object collection, CollectionViewSource key, Type collectionViewType) { ViewRecord record = _viewManager.GetViewRecord(collection, key, collectionViewType); // lacking any definitive event on which to trigger a cleanup pass, // we use a heuristic, namely the creation of a new view. This suggests // that there is new activity, which often means that old content is // being replaced. So perhaps the view table now has stale entries. if (record != null && !record.IsInitialized) { ScheduleCleanup(); } return record; } // cache of default converters (so that all uses of string-to-int can // share the same converter) internal IValueConverter GetDefaultValueConverter(Type sourceType, Type targetType, bool targetToSource) { IValueConverter result = _valueConverterTable[sourceType, targetType, targetToSource]; if (result == null) { result = DefaultValueConverter.Create(sourceType, targetType, targetToSource, this); if (result != null) _valueConverterTable.Add(sourceType, targetType, targetToSource, result); } return result; } // make an async request to the scheduler that handles requests for the given target internal void AddAsyncRequest(DependencyObject target, AsyncDataRequest request) { if (target == null) return; // get the appropriate scheduler IAsyncDataDispatcher asyncDispatcher = AsyncDataDispatcher; /* AsyncDataDispatcher property is cut (task 41079) IAsyncDataDispatcher asyncDispatcher = Binding.GetAsyncDataDispatcher(target); if (asyncDispatcher == null) { asyncDispatcher = AsyncDataDispatcher; } */ // add it to the list of schedulers that need cleanup if (_asyncDispatchers == null) { _asyncDispatchers = new HybridDictionary(1); // lazy instantiation } _asyncDispatchers[asyncDispatcher] = null; // the value is unused // make the request asyncDispatcher.AddRequest(request); } // // retrieve the value, using the cache if necessary internal object GetValue(object item, PropertyDescriptor pd, bool indexerIsNext) { return _valueTable.GetValue(item, pd, indexerIsNext); } // give the value cache first chance at handling property changes internal void RegisterForCacheChanges(object item, object descriptor) { PropertyDescriptor pd = descriptor as PropertyDescriptor; if (item != null && pd != null && ValueTable.ShouldCache(item, pd)) { _valueTable.RegisterForChanges(item, pd, this); } } // schedule a cleanup pass. This can be called from any thread. internal void ScheduleCleanup() { // only the first request after a previous cleanup should schedule real work if (Interlocked.Increment(ref _cleanupRequests) == 1) { Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new DispatcherOperationCallback(CleanupOperation), null); } } // return true if something was actually cleaned up internal bool Cleanup() { bool foundDirt = false; foundDirt = _viewManager.Purge() || foundDirt; foundDirt = WeakEventManager.Cleanup() || foundDirt; foundDirt = _valueTable.Purge() || foundDirt; return foundDirt; } // Event registration //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- private void RequestRun() { // Run tasks before layout, to front load as much layout work as possible Dispatcher.BeginInvoke(DispatcherPriority.DataBind, new DispatcherOperationCallback(Run), false); // Run tasks (especially re-tried AttachToContext tasks) again after // layout as the last chance. Any failures in AttachToContext will // be treated as an error. Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(Run), true); } // run a cleanup pass private object CleanupOperation(object arg) { // allow new requests, even if cleanup is disabled Interlocked.Exchange(ref _cleanupRequests, 0); if (!_cleanupEnabled) return null; Cleanup(); return null; } // do the final cleanup when the Dispatcher or AppDomain is shut down private void OnDispatcherShutDown(object sender, EventArgs e) { _viewManager = null; _valueConverterTable = null; _mostRecentTask = null; _head = _tail = null; // notify all the async dispatchers we've ever talked to // The InterlockedExchange makes sure we only do this once // (in case Dispatcher and AppDomain are being shut down simultaneously // on two different threads) HybridDictionary asyncDispatchers = (HybridDictionary)Interlocked.Exchange(ref _asyncDispatchers, null); if (asyncDispatchers != null) { foreach (object o in asyncDispatchers.Keys) { IAsyncDataDispatcher dispatcher = o as IAsyncDataDispatcher; if (dispatcher != null) { dispatcher.CancelAllRequests(); } } } _defaultAsyncDataDispatcher = null; // Note: the engine is still held in TLS. This maintains the 1-1 relationship // between the thread and the engine. However the engine is basically // dead - _mostRecentTask is null, and most operations are now no-ops or illegal. // This imitates the behavior of the thread's Dispatcher. } // A UIElement with pending AttachToContext task(s) has raised the // LayoutUpdated event. Run the task list. private void OnLayoutUpdated(object sender, EventArgs e) { Run(false); } //----------------------------------------------------- // // Private Types // //------------------------------------------------------ // cache of default value converters (so that all uses of string-to-int can // share the same converter) class ValueConverterTable : Hashtable { struct Key { Type _sourceType, _targetType; bool _targetToSource; public Key(Type sourceType, Type targetType, bool targetToSource) { _sourceType = sourceType; _targetType = targetType; _targetToSource = targetToSource; } public override int GetHashCode() { return _sourceType.GetHashCode() + _targetType.GetHashCode(); } public override bool Equals(object o) { if (o is Key) { return (this == (Key)o); } return false; } public static bool operator==(Key k1, Key k2) { return k1._sourceType == k2._sourceType && k1._targetType == k2._targetType && k1._targetToSource == k2._targetToSource; } public static bool operator!=(Key k1, Key k2) { return !(k1 == k2); } } public IValueConverter this[Type sourceType, Type targetType, bool targetToSource] { get { Key key = new Key(sourceType, targetType, targetToSource); object value = base[key]; return (IValueConverter)value; } } public void Add(Type sourceType, Type targetType, bool targetToSource, IValueConverter value) { base.Add(new Key(sourceType, targetType, targetToSource), value); } } //----------------------------------------------------- // // Private Fields // //------------------------------------------------------ private HybridDictionary _mostRecentTask; // client --> Task Task _head; Task _tail; private UIElement _layoutElement; private ViewManager _viewManager = new ViewManager(); private ValueConverterTable _valueConverterTable = new ValueConverterTable(); private PathParser _pathParser = new PathParser(); private IAsyncDataDispatcher _defaultAsyncDataDispatcher; private HybridDictionary _asyncDispatchers; private ValueConverterContext _valueConverterContext = new ValueConverterContext(); private bool _cleanupEnabled = true; internal static readonly CultureInfo EnglishUSCulture = CultureInfo.GetCultureInfo("en-us"); private ValueTable _valueTable = new ValueTable(); private AccessorTable _accessorTable = new AccessorTable(); private int _cleanupRequests; [ThreadStatic] private static DataBindEngine _currentEngine; // one engine per thread } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- StrongTypingException.cs
- XXXInfos.cs
- HostUtils.cs
- TableCell.cs
- WorkerProcess.cs
- LineServices.cs
- DataServiceException.cs
- DefaultSection.cs
- SslStream.cs
- RadialGradientBrush.cs
- DataViewManagerListItemTypeDescriptor.cs
- HtmlEmptyTagControlBuilder.cs
- BitmapScalingModeValidation.cs
- ISFTagAndGuidCache.cs
- ApplicationException.cs
- itemelement.cs
- NoClickablePointException.cs
- ConfigurationStrings.cs
- XsltFunctions.cs
- ProviderCollection.cs
- SettingsPropertyValueCollection.cs
- XmlTypeMapping.cs
- DesignerTextWriter.cs
- ExtractCollection.cs
- FtpWebResponse.cs
- GeometryModel3D.cs
- UnaryNode.cs
- XsltContext.cs
- DbConnectionPool.cs
- WinInetCache.cs
- RenderDataDrawingContext.cs
- TransactionProtocol.cs
- ButtonBase.cs
- AddingNewEventArgs.cs
- TrackingSection.cs
- ImageList.cs
- StatusBarItemAutomationPeer.cs
- MgmtConfigurationRecord.cs
- AnimatedTypeHelpers.cs
- EntityClientCacheEntry.cs
- PageBreakRecord.cs
- NotificationContext.cs
- XmlSchemaSimpleType.cs
- MailWebEventProvider.cs
- XsdDuration.cs
- AssemblyInfo.cs
- dsa.cs
- Byte.cs
- PackageFilter.cs
- GlyphCache.cs
- TextTrailingCharacterEllipsis.cs
- ApplicationDirectoryMembershipCondition.cs
- CodeCommentStatementCollection.cs
- BitmapEffectvisualstate.cs
- PaperSize.cs
- InfoCardRSAPKCS1SignatureFormatter.cs
- UInt64Converter.cs
- CompilerTypeWithParams.cs
- HostingEnvironmentSection.cs
- GridViewCellAutomationPeer.cs
- EncoderNLS.cs
- storepermissionattribute.cs
- SoapClientMessage.cs
- IndexingContentUnit.cs
- ContextStack.cs
- Token.cs
- StreamReader.cs
- ResXDataNode.cs
- UnionExpr.cs
- DataRecordInternal.cs
- InternalConfigSettingsFactory.cs
- XmlSchemaAll.cs
- PartialCachingControl.cs
- SqlBulkCopyColumnMappingCollection.cs
- RsaKeyIdentifierClause.cs
- DateTimePicker.cs
- QuotedPrintableStream.cs
- PermissionListSet.cs
- UriParserTemplates.cs
- SchemaReference.cs
- UpdateException.cs
- MessageFilterException.cs
- PerformanceCounterLib.cs
- connectionpool.cs
- MSAAWinEventWrap.cs
- Geometry.cs
- shaperfactoryquerycachekey.cs
- DbTransaction.cs
- CodeTypeDelegate.cs
- TouchesOverProperty.cs
- MimeObjectFactory.cs
- DataMember.cs
- ZipIOExtraFieldPaddingElement.cs
- ObjectCloneHelper.cs
- UrlAuthFailedErrorFormatter.cs
- UpdatePanelControlTrigger.cs
- SizeAnimationClockResource.cs
- ProgressChangedEventArgs.cs
- CompressEmulationStream.cs
- SoapObjectWriter.cs