Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / MS / Internal / Data / DataBindEngine.cs / 1 / DataBindEngine.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description: Data binding engine.
//
//---------------------------------------------------------------------------
using System;
using System.Collections.Generic; // Dictionary
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
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; // Dictionary
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
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
- EventItfInfo.cs
- HtmlUtf8RawTextWriter.cs
- RayHitTestParameters.cs
- OrderingQueryOperator.cs
- PreProcessor.cs
- Cursor.cs
- ReadOnlyCollectionBase.cs
- InvalidDataException.cs
- SecurityContext.cs
- Transform.cs
- XamlWriterExtensions.cs
- TextRangeProviderWrapper.cs
- ErasingStroke.cs
- XhtmlBasicListAdapter.cs
- ControlParameter.cs
- IpcClientChannel.cs
- odbcmetadatacolumnnames.cs
- IndicCharClassifier.cs
- GridSplitterAutomationPeer.cs
- ZipIOCentralDirectoryFileHeader.cs
- PersonalizationAdministration.cs
- Paragraph.cs
- GlyphInfoList.cs
- HeaderCollection.cs
- ReceiveActivity.cs
- ColorTransformHelper.cs
- BrushConverter.cs
- PermissionRequestEvidence.cs
- PartialTrustVisibleAssembliesSection.cs
- Atom10FeedFormatter.cs
- TextElementCollectionHelper.cs
- FrameworkName.cs
- RowToParametersTransformer.cs
- ListBindingHelper.cs
- SingleKeyFrameCollection.cs
- SimpleHandlerFactory.cs
- TableLayoutSettingsTypeConverter.cs
- SchemaNames.cs
- PrimitiveType.cs
- XmlText.cs
- IPipelineRuntime.cs
- SeparatorAutomationPeer.cs
- COM2ExtendedUITypeEditor.cs
- BinaryReader.cs
- ClientRuntimeConfig.cs
- ReadOnlyAttribute.cs
- PageAdapter.cs
- Point3DAnimation.cs
- TimeStampChecker.cs
- DataGridViewAutoSizeColumnModeEventArgs.cs
- IPEndPointCollection.cs
- XmlAnyElementAttribute.cs
- AudioDeviceOut.cs
- FormViewDeleteEventArgs.cs
- WorkflowRuntimeService.cs
- SecurityChannelListener.cs
- XmlSchemaSimpleTypeList.cs
- Border.cs
- LocalBuilder.cs
- AttributeParameterInfo.cs
- EmbeddedMailObject.cs
- LinearKeyFrames.cs
- PropertyGrid.cs
- X509Certificate.cs
- Error.cs
- shaper.cs
- CellTreeNode.cs
- IFlowDocumentViewer.cs
- HtmlTableCellCollection.cs
- ProgressiveCrcCalculatingStream.cs
- SettingsPropertyWrongTypeException.cs
- Nodes.cs
- DBSqlParserTable.cs
- PageCatalogPartDesigner.cs
- DrawingAttributeSerializer.cs
- PersonalizationDictionary.cs
- Int32Storage.cs
- _Connection.cs
- HttpClientProtocol.cs
- NotifyParentPropertyAttribute.cs
- XmlSchemaAll.cs
- QilTernary.cs
- TimelineCollection.cs
- StylusPlugInCollection.cs
- StreamAsIStream.cs
- SpeakInfo.cs
- UpdateRecord.cs
- WindowsToolbar.cs
- TemplateField.cs
- PeerToPeerException.cs
- Assert.cs
- MethodBody.cs
- TreeNodeCollection.cs
- Collection.cs
- XPathBuilder.cs
- ZipArchive.cs
- RegistryPermission.cs
- SecurityNegotiationException.cs
- FontStyleConverter.cs
- DataGridRow.cs