e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
292 lines
9.8 KiB
C#
292 lines
9.8 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.Activities
|
|
{
|
|
using System;
|
|
using System.Activities.Hosting;
|
|
using System.Activities.Runtime;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Runtime;
|
|
|
|
[Fx.Tag.XamlVisible(false)]
|
|
public sealed class WorkflowDataContext : CustomTypeDescriptor, INotifyPropertyChanged, IDisposable
|
|
{
|
|
ActivityExecutor executor;
|
|
ActivityInstance activityInstance;
|
|
IDictionary<Location, PropertyDescriptorImpl> locationMapping;
|
|
PropertyChangedEventHandler propertyChangedEventHandler;
|
|
PropertyDescriptorCollection properties;
|
|
ActivityContext cachedResolutionContext;
|
|
|
|
internal WorkflowDataContext(ActivityExecutor executor, ActivityInstance activityInstance, bool includeLocalVariables)
|
|
{
|
|
this.executor = executor;
|
|
this.activityInstance = activityInstance;
|
|
this.IncludesLocalVariables = includeLocalVariables;
|
|
this.properties = CreateProperties();
|
|
}
|
|
|
|
internal bool IncludesLocalVariables
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
// We want our own cached ActivityContext rather than using this.executor.GetResolutionContext
|
|
// because there is no synchronization of access to the executor's cached object and access thru
|
|
// this WorkflowDataContext will not be done on the workflow runtime thread.
|
|
ActivityContext ResolutionContext
|
|
{
|
|
get
|
|
{
|
|
ThrowIfEnvironmentDisposed();
|
|
if (this.cachedResolutionContext == null)
|
|
{
|
|
this.cachedResolutionContext = new ActivityContext(this.activityInstance, this.executor);
|
|
this.cachedResolutionContext.AllowChainedEnvironmentAccess = true;
|
|
}
|
|
else
|
|
{
|
|
this.cachedResolutionContext.Reinitialize(this.activityInstance, this.executor);
|
|
}
|
|
return this.cachedResolutionContext;
|
|
}
|
|
}
|
|
|
|
PropertyChangedEventHandler PropertyChangedEventHandler
|
|
{
|
|
get
|
|
{
|
|
if (this.propertyChangedEventHandler == null)
|
|
{
|
|
this.propertyChangedEventHandler = new PropertyChangedEventHandler(this.OnLocationChanged);
|
|
}
|
|
return this.propertyChangedEventHandler;
|
|
}
|
|
}
|
|
|
|
PropertyDescriptorCollection CreateProperties()
|
|
{
|
|
// The name in child Activity will shadow the name in parent.
|
|
Dictionary<string, object> names = new Dictionary<string, object>();
|
|
|
|
List<PropertyDescriptorImpl> propertyList = new List<PropertyDescriptorImpl>();
|
|
|
|
LocationReferenceEnvironment environment = this.activityInstance.Activity.PublicEnvironment;
|
|
bool isLocalEnvironment = true;
|
|
while (environment != null)
|
|
{
|
|
foreach (LocationReference locRef in environment.GetLocationReferences())
|
|
{
|
|
if (this.IncludesLocalVariables || !isLocalEnvironment || !(locRef is Variable))
|
|
{
|
|
AddProperty(locRef, names, propertyList);
|
|
}
|
|
}
|
|
|
|
environment = environment.Parent;
|
|
isLocalEnvironment = false;
|
|
}
|
|
|
|
return new PropertyDescriptorCollection(propertyList.ToArray(), true);
|
|
}
|
|
|
|
void AddProperty(LocationReference reference, Dictionary<string, object> names,
|
|
List<PropertyDescriptorImpl> propertyList)
|
|
{
|
|
if (!string.IsNullOrEmpty(reference.Name) &&
|
|
!names.ContainsKey(reference.Name))
|
|
{
|
|
names.Add(reference.Name, reference);
|
|
PropertyDescriptorImpl property = new PropertyDescriptorImpl(reference);
|
|
propertyList.Add(property);
|
|
AddNotifyHandler(property);
|
|
}
|
|
}
|
|
|
|
void AddNotifyHandler(PropertyDescriptorImpl property)
|
|
{
|
|
ActivityContext activityContext = this.ResolutionContext;
|
|
try
|
|
{
|
|
Location location = property.LocationReference.GetLocation(activityContext);
|
|
INotifyPropertyChanged notify = location as INotifyPropertyChanged;
|
|
if (notify != null)
|
|
{
|
|
notify.PropertyChanged += PropertyChangedEventHandler;
|
|
|
|
if (this.locationMapping == null)
|
|
{
|
|
this.locationMapping = new Dictionary<Location, PropertyDescriptorImpl>();
|
|
}
|
|
this.locationMapping.Add(location, property);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
activityContext.Dispose();
|
|
}
|
|
}
|
|
|
|
void OnLocationChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
PropertyChangedEventHandler handler = this.PropertyChanged;
|
|
if (handler != null)
|
|
{
|
|
Location location = (Location)sender;
|
|
|
|
Fx.Assert(this.locationMapping != null, "Location mapping must not be null.");
|
|
PropertyDescriptorImpl property;
|
|
if (this.locationMapping.TryGetValue(location, out property))
|
|
{
|
|
if (e.PropertyName == "Value")
|
|
{
|
|
handler(this, new PropertyChangedEventArgs(property.Name));
|
|
}
|
|
else
|
|
{
|
|
handler(this, new PropertyChangedEventArgs(property.Name + "." + e.PropertyName));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (this.locationMapping != null)
|
|
{
|
|
foreach (KeyValuePair<Location, PropertyDescriptorImpl> pair in this.locationMapping)
|
|
{
|
|
INotifyPropertyChanged notify = pair.Key as INotifyPropertyChanged;
|
|
if (notify != null)
|
|
{
|
|
notify.PropertyChanged -= PropertyChangedEventHandler;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We need a separate method here from Dispose(), because Dispose currently
|
|
// doesn't make the WDC uncallable, it just unhooks it from notifications.
|
|
internal void DisposeEnvironment()
|
|
{
|
|
this.activityInstance = null;
|
|
}
|
|
|
|
void ThrowIfEnvironmentDisposed()
|
|
{
|
|
if (this.activityInstance == null)
|
|
{
|
|
throw FxTrace.Exception.AsError(
|
|
new ObjectDisposedException(this.GetType().FullName, SR.WDCDisposed));
|
|
}
|
|
}
|
|
|
|
public override PropertyDescriptorCollection GetProperties()
|
|
{
|
|
return this.properties;
|
|
}
|
|
|
|
class PropertyDescriptorImpl : PropertyDescriptor
|
|
{
|
|
LocationReference reference;
|
|
//
|
|
|
|
|
|
|
|
public PropertyDescriptorImpl(LocationReference reference)
|
|
: base(reference.Name, new Attribute[0])
|
|
{
|
|
this.reference = reference;
|
|
}
|
|
|
|
public override Type ComponentType
|
|
{
|
|
get { return typeof(WorkflowDataContext); }
|
|
}
|
|
|
|
public override bool IsReadOnly
|
|
{
|
|
get
|
|
{
|
|
//
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override Type PropertyType
|
|
{
|
|
get
|
|
{
|
|
return this.reference.Type;
|
|
}
|
|
}
|
|
|
|
public LocationReference LocationReference
|
|
{
|
|
get
|
|
{
|
|
return this.reference;
|
|
}
|
|
}
|
|
|
|
public override bool CanResetValue(object component)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override object GetValue(object component)
|
|
{
|
|
WorkflowDataContext dataContext = (WorkflowDataContext)component;
|
|
|
|
ActivityContext activityContext = dataContext.ResolutionContext;
|
|
try
|
|
{
|
|
return this.reference.GetLocation(activityContext).Value;
|
|
}
|
|
finally
|
|
{
|
|
activityContext.Dispose();
|
|
}
|
|
}
|
|
|
|
public override void ResetValue(object component)
|
|
{
|
|
throw FxTrace.Exception.AsError(new NotSupportedException(SR.CannotResetPropertyInDataContext));
|
|
}
|
|
|
|
public override void SetValue(object component, object value)
|
|
{
|
|
if (IsReadOnly)
|
|
{
|
|
throw FxTrace.Exception.AsError(new NotSupportedException(SR.PropertyReadOnlyInWorkflowDataContext(this.Name)));
|
|
}
|
|
|
|
WorkflowDataContext dataContext = (WorkflowDataContext)component;
|
|
|
|
ActivityContext activityContext = dataContext.ResolutionContext;
|
|
try
|
|
{
|
|
Location location = this.reference.GetLocation(activityContext);
|
|
location.Value = value;
|
|
}
|
|
finally
|
|
{
|
|
activityContext.Dispose();
|
|
}
|
|
}
|
|
|
|
public override bool ShouldSerializeValue(object component)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|