Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / Tools / System.Activities.Presentation / System / Activities / Presentation / View / VariableDesigner.xaml.cs / 1305376 / VariableDesigner.xaml.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------
namespace System.Activities.Presentation.View
{
using Microsoft.VisualBasic.Activities;
using System;
using System.Activities.Presentation.PropertyEditing;
using System.Activities.Presentation.View;
using System.Activities.Presentation.Model;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Activities.Presentation.Services;
using System.Windows.Threading;
using System.Reflection;
partial class VariableDesigner
{
public static readonly DependencyProperty ContextProperty = DependencyProperty.Register(
"Context",
typeof(EditingContext),
typeof(VariableDesigner),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(VariableDesigner.OnContextChanged)));
static readonly DependencyPropertyKey CurrentVariableScopePropertyKey = DependencyProperty.RegisterReadOnly(
"CurrentVariableScope",
typeof(ModelItemCollection),
typeof(VariableDesigner),
new UIPropertyMetadata(null, new PropertyChangedCallback(OnVariableScopeChanged)));
public static readonly DependencyProperty CurrentVariableScopeProperty = CurrentVariableScopePropertyKey.DependencyProperty;
public static readonly RoutedEvent VariableCollectionChangedEvent =
EventManager.RegisterRoutedEvent("VariableCollectionChanged",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(VariableDesigner));
const string DefaultVariableName = "variable";
List scopesList = new List();
ObservableCollection variableWrapperCollection = new ObservableCollection();
bool trackVariableWrapperContainerChanges = true;
bool isSelectionSourceInternal = false;
ModelItem variableToSelect = null;
bool continueScopeEdit = false;
ModelItem lastSelection;
DataGridHelper dgHelper;
public VariableDesigner()
{
InitializeComponent();
this.dgHelper = new DataGridHelper(this.variableDataGrid, this);
this.dgHelper.Context = this.Context;
this.dgHelper.AddNewRowCommand = DesignerView.CreateVariableCommand;
this.dgHelper.AddNewRowContent = (string)this.FindResource("addVariableNewRowLabel");
this.dgHelper.ResolveDynamicTemplateCallback = this.OnResolveDynamicContentTemplate;
this.dgHelper.LoadDynamicContentDataCallback = this.OnShowExtendedValueEditor;
this.dgHelper.LoadCustomPropertyValueEditorCallback = this.OnLoadExtendedValueEditor;
this.dgHelper.ShowValidationErrorAsToolTip = false;
this.variableDataGrid.SelectionChanged += OnDataGridRowSelected;
this.variableWrapperCollection.CollectionChanged += OnVariableWrapperCollectionChanged;
this.variableDataGrid.ItemsSource = this.variableWrapperCollection;
var converter = (BreadCrumbTextConverter)this.FindResource("scopeToNameConverter");
converter.PixelsPerChar = (this.FontSize - 5);
}
public event RoutedEventHandler VariableCollectionChanged
{
add
{
AddHandler(VariableCollectionChangedEvent, value);
}
remove
{
RemoveHandler(VariableCollectionChangedEvent, value);
}
}
public EditingContext Context
{
get { return (EditingContext)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
public ModelItemCollection CurrentVariableScope
{
get { return (ModelItemCollection)GetValue(CurrentVariableScopeProperty); }
private set { SetValue(CurrentVariableScopePropertyKey, value); }
}
public List ScopesList
{
get { return this.scopesList; }
}
public bool CreateNewVariableWrapper()
{
bool result = false;
ModelItemCollection scope = this.GetDefaultVariableScope();
if (null != scope)
{
DesignTimeVariable wrapperObject = null;
Variable variable = Variable.Create(this.GetDefaultName(), this.GetDefaultType(), VariableModifiers.None);
using (ModelEditingScope change = scope.BeginEdit((string)this.FindResource("addNewVariableDescription")))
{
ModelItem wrappedVariable = scope.Add(variable);
wrapperObject = new DesignTimeVariable(wrappedVariable, this);
this.variableWrapperCollection.Add(wrapperObject);
change.Complete();
result = true;
}
this.dgHelper.BeginRowEdit(wrapperObject);
}
return result;
}
internal void ChangeVariableType(DesignTimeVariable oldVariableWrapper, Variable newVariable)
{
//check who is the sender of the event - data grid or property inspector
int index = this.variableDataGrid.Items.IndexOf(this.variableDataGrid.SelectedItem);
DataGridCell cell = DataGridHelper.GetCell(this.variableDataGrid, index, 1);
bool shouldReselct = cell.IsEditing;
//get location of the variable
ModelItemCollection container = VariableHelper.GetVariableCollection(oldVariableWrapper.ReflectedObject.Parent.Parent);
index = container.IndexOf(oldVariableWrapper.ReflectedObject);
//remove all value
container.RemoveAt(index);
oldVariableWrapper.Dispose();
oldVariableWrapper.Initialize(container.Insert(index, newVariable));
}
internal void NotifyVariableScopeChanged(DesignTimeVariable variable)
{
this.variableToSelect = null != variable ? variable.ReflectedObject : null;
int index = this.variableDataGrid.Items.IndexOf(this.variableDataGrid.SelectedItem);
DataGridCell cell = DataGridHelper.GetCell(this.variableDataGrid, index, 2);
this.continueScopeEdit = cell.IsEditing;
}
//Check case-insensitive duplicates, which are not allowed in VB expressions
internal void CheckCaseInsensitiveDuplicates(VBIdentifierName identifierName, string newName)
{
Func checkForDuplicates = new Func(p => string.Equals((string)p.ReflectedObject.Properties["Name"].ComputedValue, newName, StringComparison.OrdinalIgnoreCase) && !object.Equals(p.GetVariableName(), identifierName));
DesignTimeVariable duplicate = this.variableWrapperCollection.FirstOrDefault(checkForDuplicates);
if (duplicate != null)
{
identifierName.IsValid = false;
identifierName.ErrorMessage = string.Format(CultureInfo.CurrentUICulture, SR.DuplicateVisualBasicIdentifier, newName);
VBIdentifierName duplicateIdentifier = duplicate.GetVariableName();
if (duplicateIdentifier.IsValid)
{
duplicateIdentifier.IsValid = false;
duplicateIdentifier.ErrorMessage = string.Format(CultureInfo.CurrentUICulture, SR.DuplicateVisualBasicIdentifier, duplicateIdentifier.IdentifierName);
}
};
}
//Check duplicates with old value. When there's only one variable duplicate with the old value,
//the only one variable should be valid now after the change
void ClearCaseInsensitiveDuplicates(VBIdentifierName identifier, string oldName)
{
Func checkForOldNameDuplicates = new Func(p => string.Equals((string)p.ReflectedObject.Properties["Name"].ComputedValue, oldName, StringComparison.OrdinalIgnoreCase) && !object.Equals(p.GetVariableName(), identifier));
IEnumerable oldDuplicates = this.variableWrapperCollection.Where(checkForOldNameDuplicates);
if (oldDuplicates.Count() == 1)
{
DesignTimeVariable wrapper = oldDuplicates.First();
VBIdentifierName oldDuplicate = wrapper.GetVariableName();
oldDuplicate.IsValid = true;
oldDuplicate.ErrorMessage = string.Empty;
}
}
internal void NotifyVariableNameChanged(VBIdentifierName identifierName, string newName, string oldName)
{
//Check whether there're any variables' name conflict with the old name which can be cleaned up now
this.ClearCaseInsensitiveDuplicates(identifierName, oldName);
//Check whether there're any duplicates with new name
this.CheckCaseInsensitiveDuplicates(identifierName, newName);
}
internal void UpdateTypeDesigner(DesignTimeVariable variable)
{
this.dgHelper.UpdateDynamicContentColumns(variable);
}
string GetDefaultName()
{
return this.variableWrapperCollection.GetUniqueName(VariableDesigner.DefaultVariableName, wrapper => wrapper.GetVariableName().IdentifierName);
}
Type GetDefaultType()
{
return typeof(string);
}
ModelItemCollection GetDefaultVariableScope()
{
//do a lazdy scope refresh
//if we have a valid variable scope
if (null != this.CurrentVariableScope)
{
//get the tree manager
var treeManager = this.Context.Services.GetService();
if (null != treeManager)
{
//get the model tree root
var root = treeManager.Root;
//get the first scope, which is attached to the model tree (in case of undo/redo operations, even though VariableScope might be
//valid, it actually isn't attached to model tree, so using it as a variable scope doesn't make any sense)
var validScope = this.scopesList.FirstOrDefault( p => ModelItem.Equals(p, root) || root.IsParentOf(p) );
//check if valid scope is different that current variable scope. most likely - due to undo/redo operation which removed an activity
if (!ModelItem.Equals(validScope, this.CurrentVariableScope.Parent))
{
//it is different - update the current variable scope (this setter will unhook old event handlers, clean the scopesList collection and hook
//for new event notifications
this.CurrentVariableScope = validScope.GetVariableCollection();
}
}
}
//return validated variable scope
return this.CurrentVariableScope;
}
void Populate(ModelItem workflowElement)
{
this.scopesList.ForEach(p =>
{
p.Properties["Variables"].Collection.CollectionChanged -= OnVariableCollectionChanged;
});
this.scopesList.Clear();
this.variableDataGrid.ItemsSource = null;
this.variableWrapperCollection.All(p => { p.Dispose(); return true; });
this.variableWrapperCollection.Clear();
var allVariables = VariableHelper.FindDeclaredVariables(workflowElement, this.scopesList);
//fill variable wrapper collection only if designer is visible
if (workflowElement != null && this.IsVisible)
{
allVariables.ForEach(variable =>
{
this.variableWrapperCollection.Add(new DesignTimeVariable(variable, this));
});
}
this.variableDataGrid.ItemsSource = this.variableWrapperCollection;
this.scopesList.ForEach(p =>
{
p.Properties["Variables"].Collection.CollectionChanged += OnVariableCollectionChanged;
});
}
void StoreLastSelection()
{
if (!this.isSelectionSourceInternal)
{
ModelItem current = this.Context.Items.GetValue().PrimarySelection;
if (null == current || !typeof(DesignTimeVariable).IsAssignableFrom(current.ItemType))
{
this.lastSelection = current;
}
}
}
void OnDataGridRowSelected(object sender, RoutedEventArgs e)
{
if (null != this.Context && !this.isSelectionSourceInternal)
{
this.isSelectionSourceInternal = true;
if (null != this.variableDataGrid.SelectedItem && this.variableDataGrid.SelectedItem is DesignTimeVariable)
{
DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItem;
this.Context.Items.SetValue(new Selection(variable.Content));
}
else
{
this.Context.Items.SetValue(new Selection());
}
this.isSelectionSourceInternal = false;
}
}
void OnContextChanged()
{
if (null != this.Context)
{
this.Context.Items.Subscribe(new SubscribeContextCallback(OnItemSelected));
}
this.dgHelper.Context = this.Context;
}
protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsKeyboardFocusWithinChanged(e);
if ((bool)e.NewValue)
{
//Re-select the already selected row to raise DataGridSelectionChanged event,
//which will update the Selection context item and will refresh property grid
object selectedRow = this.variableDataGrid.SelectedItem;
this.isSelectionSourceInternal = true;
this.variableDataGrid.SelectedItem = null;
this.isSelectionSourceInternal = false;
this.variableDataGrid.SelectedItem = selectedRow;
}
}
void OnItemSelected(Selection newSelection)
{
//check if selection update source is internal - it is internal if someone clicks on row in variable designer. in such case, do not update selection
if (!this.isSelectionSourceInternal)
{
//whenever selection changes:
//1) check if selection is a result of undo/redo operation - if it is, we might be in the middle of collection changed
// notification, so i have to postpone variable scope update untill collection update is completed.
if (this.Context.Services.GetService().IsUndoRedoInProgress)
{
//delegate call to update selection after update is completed
this.Dispatcher.BeginInvoke(new Action(() =>
{
//get current selection - i can't use newSelection passed into the method, because undo/redo may alter that value
var current = this.Context.Items.GetValue().PrimarySelection;
//do the actual selection update
this.OnItemSelectedCore(current);
}), DispatcherPriority.ApplicationIdle);
}
else
{
//store element selected before variable designer started updating selection - when designer is closed, we try to restore that selection
this.StoreLastSelection();
this.OnItemSelectedCore(newSelection.PrimarySelection);
}
}
}
void OnItemSelectedCore(ModelItem primarySelection)
{
//update variable scope - but ignore selection changes made by selecting variables.
if (null == primarySelection || !primarySelection.IsAssignableFrom())
{
ModelItem element = VariableHelper.GetVariableScopeElement(primarySelection);
this.CurrentVariableScope = VariableHelper.GetVariableCollection(element);
}
}
void OnVariableScopeChanged()
{
this.Populate(null != this.CurrentVariableScope ? this.CurrentVariableScope.Parent : null);
}
void OnVariableCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var isUndoRedoInProgress = this.Context.Services.GetService().IsUndoRedoInProgress;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (ModelItem variable in e.NewItems)
{
DesignTimeVariable wrapper = this.variableWrapperCollection
.FirstOrDefault(p => (ModelItem.Equals(p.ReflectedObject, variable)));
if (wrapper == null)
{
wrapper = new DesignTimeVariable(variable, this);
this.variableWrapperCollection.Add(wrapper);
}
if (null != this.variableToSelect && this.variableToSelect == variable)
{
this.variableDataGrid.SelectedItem = wrapper;
this.variableToSelect = null;
int index = this.variableDataGrid.Items.IndexOf(this.variableDataGrid.SelectedItem);
DataGridRow row = DataGridHelper.GetRow(this.variableDataGrid, index);
if (!row.IsSelected)
{
row.IsSelected = true;
}
if (this.continueScopeEdit)
{
DataGridCell cell = DataGridHelper.GetCell(this.variableDataGrid, index, 2);
cell.IsEditing = true;
}
}
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (ModelItem variable in e.OldItems)
{
DesignTimeVariable wrapper = this.variableWrapperCollection.FirstOrDefault(p => ModelItem.Equals(p.ReflectedObject, variable));
if (null != wrapper)
{
//in case of undo/redo operation - just remove old reference to the wrapper, new one will be added be undo stack anyway
if (!this.ScopesList.Contains((sender as ModelItem).Parent) || isUndoRedoInProgress)
{
this.variableWrapperCollection.Remove(wrapper);
}
}
}
break;
}
this.RaiseEvent(new RoutedEventArgs(VariableDesigner.VariableCollectionChangedEvent, this));
}
void OnVariableWrapperCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Remove:
foreach (DesignTimeVariable arg in e.OldItems)
{
if (this.trackVariableWrapperContainerChanges)
{
this.ClearCaseInsensitiveDuplicates(arg.GetVariableName(), (string)arg.ReflectedObject.Properties["Name"].ComputedValue);
ModelItemCollection collection = (ModelItemCollection)arg.ReflectedObject.Parent;
collection.Remove(arg.ReflectedObject);
}
arg.Dispose();
}
break;
case NotifyCollectionChangedAction.Add:
foreach (DesignTimeVariable var in e.NewItems)
{
this.CheckCaseInsensitiveDuplicates(var.GetVariableName(), (string)var.ReflectedObject.Properties["Name"].ComputedValue);
}
break;
}
}
void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (bool.Equals(true, e.NewValue))
{
this.StoreLastSelection();
this.OnVariableScopeChanged();
}
else if (this.variableDataGrid.SelectedItem is DesignTimeVariable)
{
Selection newSelection = null == this.lastSelection ? new Selection() : new Selection(this.lastSelection);
this.isSelectionSourceInternal = true;
this.Context.Items.SetValue(newSelection);
this.variableDataGrid.SelectedItem = null;
this.isSelectionSourceInternal = false;
}
}
bool OnResolveDynamicContentTemplate(ResolveTemplateParams resolveParams)
{
var variable = (DesignObjectWrapper)resolveParams.Cell.DataContext;
//get editor associated with variable's value
var editorType = variable.GetDynamicPropertyValueEditorType(DesignTimeVariable.VariableValueProperty);
//if yes there is a custom one - use it
if (!typeof(ExpressionValueEditor).IsAssignableFrom(editorType))
{
//get inline editor template - it will be used for both templates - view and editing;
resolveParams.Template = variable.GetDynamicPropertyValueEditor(DesignTimeVariable.VariableValueProperty).InlineEditorTemplate;
resolveParams.IsDefaultTemplate = false;
}
else
{
//no custom editor - depending on grid state display either editable or readonly expression template
string key = resolveParams.Cell.IsEditing ? "variableExpressionEditableTemplate" : "variableExpressionReadonlyTemplate";
resolveParams.Template = (DataTemplate)this.FindResource(key);
resolveParams.IsDefaultTemplate = true;
}
return true;
}
DialogPropertyValueEditor OnLoadExtendedValueEditor(DataGridCell cell, object instance)
{
var variable = (DesignObjectWrapper)cell.DataContext;
return variable.GetDynamicPropertyValueEditor(DesignTimeVariable.VariableValueProperty) as DialogPropertyValueEditor;
}
ModelProperty OnShowExtendedValueEditor(DataGridCell cell, object instance)
{
var variable = (DesignObjectWrapper)cell.DataContext;
return variable.Content.Properties[DesignTimeVariable.VariableValueProperty];
}
static void OnContextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
((VariableDesigner)sender).OnContextChanged();
}
static void OnVariableScopeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
VariableDesigner control = (VariableDesigner)sender;
if (!object.Equals(e.OldValue, e.NewValue))
{
control.OnVariableScopeChanged();
}
}
}
internal static class VariableHelper
{
static Type VariablesCollectionType = typeof(Collection);
static Type CodeActivityType = typeof(CodeActivity);
static Type GenericCodeActivityType = typeof(CodeActivity<>);
static Type AsyncCodeActivityType = typeof(AsyncCodeActivity);
static Type GenericAsyncCodeActivityType = typeof(AsyncCodeActivity<>);
internal static ModelItemCollection GetVariableCollection(this ModelItem element)
{
if (null != element)
{
Type elementType = element.ItemType;
if (!((CodeActivityType.IsAssignableFrom(elementType)) || (GenericAsyncCodeActivityType.IsAssignableFrom(elementType)) ||
(AsyncCodeActivityType.IsAssignableFrom(elementType)) || (GenericAsyncCodeActivityType.IsAssignableFrom(elementType))))
{
ModelProperty variablesProperty = element.Properties["Variables"];
if ((variablesProperty != null) && (variablesProperty.PropertyType == VariablesCollectionType))
{
return variablesProperty.Collection;
}
}
}
return null;
}
internal static ModelItem GetVariableScopeElement(this ModelItem element)
{
while (null != element && null == VariableHelper.GetVariableCollection(element))
{
element = element.Parent;
}
return element;
}
internal static List FindDeclaredVariables(this ModelItem element, IList scopeList)
{
var variables = VariableHelper.FindVariablesInScope(element, scopeList);
var contained = VariableHelper.GetVariableCollection(element);
if (null != contained)
{
if (null != scopeList)
{
scopeList.Insert(0, element);
}
variables.InsertRange(0, contained.AsEnumerable());
}
return variables;
}
internal static List FindDeclaredVariables(this ModelItem element)
{
return VariableHelper.FindDeclaredVariables(element, null);
}
internal static List FindActionHandlerVariables(this ModelItem workflowElement)
{
List variables = new List();
while (null != workflowElement)
//if (null != workflowElement)
{
//variables =
IEnumerable> actionHandlers =
//take into account all properties defined in workflow element
workflowElement.Properties.
//select only those, which can be assigned to ActivityDelegate
Where(p => typeof(ActivityDelegate).IsAssignableFrom(p.PropertyType)).
//from those, take actual ModelItem value and and get its properties
Select(p => p.Value == null ? Enumerable.Empty() : p.Value.Properties);
//there might be many(?) action handlers within one activity, so get variables from all of them
foreach (IEnumerable actionHandler in actionHandlers)
{
//browse all properties in given action handler
IEnumerable variablesInScope = actionHandler.
//choose only those of base type equal to DelegateArgument
Where(p => (typeof(DelegateArgument).IsAssignableFrom(p.PropertyType) && null != p.Value)).
//from those, take actual ModelItem value
Select(p => p.Value);
//if anything is returned - add it to variables list
variables.AddRange(variablesInScope);
}
workflowElement = workflowElement.Parent;
}
return variables;
}
internal static List FindVariablesInScope(this ModelItem element, IList scopeList)
{
List variables = new List();
if (null != scopeList)
{
scopeList.Clear();
}
if (null != element)
{
element = element.Parent;
while (element != null)
{
ModelItemCollection variablesInElement = VariableHelper.GetVariableCollection(element);
if (null != variablesInElement)
{
if (null != scopeList)
{
scopeList.Add(element);
}
variables.AddRange(variablesInElement.AsEnumerable());
}
element = element.Parent;
}
}
return variables;
}
internal static List FindUniqueVariablesInScope(ModelItem element)
{
Dictionary variables = new Dictionary();
while (element != null)
{
ModelItemCollection variablesInElement = VariableHelper.GetVariableCollection(element);
if (null != variablesInElement)
{
foreach (ModelItem modelVariable in variablesInElement)
{
LocationReference locationReference = modelVariable.GetCurrentValue() as LocationReference;
if (locationReference != null && !string.IsNullOrWhiteSpace(locationReference.Name) && !variables.ContainsKey(locationReference.Name))
{
variables.Add(locationReference.Name, modelVariable);
}
}
}
element = element.Parent;
}
return new List(variables.Values);
}
internal static List FindVariablesInScope(ModelItem element)
{
return VariableHelper.FindVariablesInScope(element, null);
}
internal static bool ContainsVariable(this ModelItemCollection variableContainer, string variableName)
{
if (null == variableContainer)
{
throw FxTrace.Exception.AsError(new ArgumentNullException("variableContainer"));
}
if (!variableContainer.ItemType.IsGenericType ||
variableContainer.ItemType.GetGenericArguments().Length != 1 ||
!typeof(Variable).IsAssignableFrom(variableContainer.ItemType.GetGenericArguments()[0]))
{
throw FxTrace.Exception.AsError(new ArgumentException("non variable collection"));
}
return variableContainer.Any(p => string.Equals(p.Properties[DesignTimeVariable.VariableNameProperty].ComputedValue, variableName));
}
internal static string CreateUniqueVariableName(this ModelItemCollection variableContainer, string namePrefix, int countStartValue)
{
if (null == variableContainer)
{
throw FxTrace.Exception.AsError(new ArgumentNullException("variableContainer"));
}
if (!variableContainer.ItemType.IsGenericType ||
variableContainer.ItemType.GetGenericArguments().Length != 1 ||
!typeof(Variable).IsAssignableFrom(variableContainer.ItemType.GetGenericArguments()[0]))
{
throw FxTrace.Exception.AsError(new ArgumentException("non variable collection"));
}
string name = string.Empty;
//in order to generate unique variable name, browse all scopes from current to the root - variable name should be unique in whole tree up to
//the root. we don't check unique check below current scope - it would be too expensive to ---- whole tree
var variables = VariableHelper.FindVariablesInScope(variableContainer);
while (true)
{
name = string.Format(CultureInfo.CurrentUICulture, "{0}{1}", namePrefix, countStartValue++);
if (!variables.Any(p => string.Equals(p.Properties[DesignTimeVariable.VariableNameProperty].ComputedValue, name)))
{
break;
}
}
return name;
}
internal static ModelItem FindRootVariableScope(ModelItem element)
{
if (null == element)
{
throw FxTrace.Exception.ArgumentNull("element");
}
ModelItem result = element.GetParentEnumerator().Where(p => null != VariableHelper.GetVariableCollection(p)).LastOrDefault();
return result;
}
internal static ModelItem FindCommonVariableScope(ModelItem scope1, ModelItem scope2)
{
if (null == scope1 || null == scope2)
{
throw FxTrace.Exception.AsError(new ArgumentNullException(null == scope1 ? "scope1" : "scope2"));
}
var scope1List = scope1.GetParentEnumerator().Where(p => null != VariableHelper.GetVariableCollection(p)).ToList();
var scope2List = scope2.GetParentEnumerator().Where(p => null != VariableHelper.GetVariableCollection(p)).ToList();
if (null != VariableHelper.GetVariableCollection(scope1))
{
scope1List.Insert(0, scope1);
}
if (null != VariableHelper.GetVariableCollection(scope2))
{
scope2List.Insert(0, scope2);
}
if (scope1 == scope2)
{
return scope1List.FirstOrDefault();
}
return scope1List.Intersect(scope2List).FirstOrDefault();
}
}
sealed class DesignTimeVariable : DesignObjectWrapper
{
internal static readonly string VariableNameProperty = "Name";
internal static readonly string VariableTypeProperty = "Type";
internal static readonly string VariableScopeProperty = "Scope";
internal static readonly string VariableValueProperty = "Default";
internal static readonly string ToolTipProperty = "ToolTip";
internal static readonly string VariableScopeLevelProperty = "ScopeLevel";
internal static readonly string VariableModifiersProperty = "Modifiers";
static readonly string[] Properties =
new string[] { VariableNameProperty, VariableTypeProperty, VariableScopeProperty, VariableValueProperty,
ToolTipProperty, VariableScopeLevelProperty, VariableModifiersProperty };
bool variableTypeChanged = false;
internal VariableDesigner Editor
{
get;
private set;
}
VBIdentifierName identifierName;
#region Initialize type properties code
public static PropertyDescriptorData[] InitializeTypeProperties()
{
return new PropertyDescriptorData[]
{
new PropertyDescriptorData()
{
PropertyName = VariableNameProperty,
PropertyType = typeof(VBIdentifierName),
PropertyAttributes = TypeDescriptor.GetAttributes(typeof(VBIdentifierName)).OfType().ToArray(),
PropertySetter = (instance, newValue) =>
{
((DesignTimeVariable)instance).SetVariableName((VBIdentifierName)newValue);
},
PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableName()),
PropertyValidator = (instance, value, errors) => (((DesignTimeVariable)instance).ValidateVariableName(value, errors))
},
new PropertyDescriptorData()
{
PropertyName = VariableTypeProperty,
PropertyType = typeof(Type),
PropertyAttributes = TypeDescriptor.GetAttributes(typeof(Type)).OfType().ToArray(),
PropertySetter = (instance, newValue) =>
{
((DesignTimeVariable)instance).SetVariableType((Type)newValue);
},
PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableType()),
PropertyValidator = null
},
new PropertyDescriptorData()
{
PropertyName = VariableScopeProperty,
PropertyType = typeof(ModelItem),
PropertyAttributes = TypeDescriptor.GetAttributes(typeof(ModelItem)).OfType().Union(new Attribute[] { new EditorAttribute(typeof(ScopeValueEditor), typeof(PropertyValueEditor))}).ToArray(),
PropertySetter = (instance, newValue) =>
{
((DesignTimeVariable)instance).SetVariableScope(newValue);
},
PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableScope()),
PropertyValidator = (instance, value, errors) => (((DesignTimeVariable)instance).ValidateVariableScope(value, errors))
},
new PropertyDescriptorData()
{
PropertyName = VariableValueProperty,
PropertyType = typeof(Activity),
PropertyAttributes = TypeDescriptor.GetAttributes(typeof(Activity)).OfType().Union(new Attribute[] { new EditorAttribute(typeof(DesignObjectWrapperDynamicPropertyEditor), typeof(DialogPropertyValueEditor)), new EditorReuseAttribute(false) }).ToArray(),
PropertySetter = (instance, newValue) =>
{
((DesignTimeVariable)instance).SetVariableValue(newValue);
},
PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableValue()),
PropertyValidator = null,
},
new PropertyDescriptorData()
{
PropertyName = ToolTipProperty,
PropertyType = typeof(string),
PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
PropertySetter = null,
PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetToolTip()),
PropertyValidator = null
},
new PropertyDescriptorData()
{
PropertyName = VariableScopeLevelProperty,
PropertyType = typeof(int),
PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
PropertySetter = null,
PropertyValidator = null,
PropertyGetter = (instance) =>
(
((DesignTimeVariable)instance).GetScopeLevel()
),
},
new PropertyDescriptorData()
{
PropertyName = VariableModifiersProperty,
PropertyType = typeof(VariableModifiers),
PropertyAttributes = TypeDescriptor.GetAttributes(typeof(VariableModifiers)).OfType().ToArray(),
PropertySetter = (instance, newValue) =>
{
((DesignTimeVariable)instance).SetVariableModifiers(newValue);
},
PropertyValidator = null,
PropertyGetter = (instance) =>
{
return ((DesignTimeVariable)instance).GetVariableModifiers();
}
}
};
}
#endregion
public DesignTimeVariable()
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.InvalidConstructorCall));
}
internal DesignTimeVariable(ModelItem modelItem, VariableDesigner editor)
: base(modelItem)
{
this.Editor = editor;
this.identifierName = new VBIdentifierName
{
IdentifierName = (string)modelItem.Properties[VariableNameProperty].ComputedValue
};
}
protected override string AutomationId
{
get { return this.GetVariableNameString(); }
}
void SetVariableName(VBIdentifierName identifierName)
{
using (ModelEditingScope change = this.ReflectedObject.BeginEdit((string)this.Editor.FindResource("changeVariableNameDescription")))
{
this.identifierName = identifierName;
string name = this.identifierName.IdentifierName;
this.Editor.NotifyVariableNameChanged(this.identifierName, name, (string)this.ReflectedObject.Properties[VariableNameProperty].ComputedValue);
this.ReflectedObject.Properties[VariableNameProperty].SetValue(name);
change.Complete();
}
}
internal VBIdentifierName GetVariableName()
{
return this.identifierName;
}
string GetVariableNameString()
{
return (string)this.ReflectedObject.Properties[VariableNameProperty].ComputedValue;
}
protected override void OnReflectedObjectPropertyChanged(string propertyName)
{
if (propertyName == VariableNameProperty)
{
string oldValue = this.identifierName.IdentifierName;
string newValue = GetVariableNameString();
//This is invoked in undo stack
if (oldValue != newValue)
{
this.identifierName = new VBIdentifierName
{
IdentifierName = newValue
};
Editor.NotifyVariableNameChanged(this.identifierName, newValue, oldValue);
}
}
}
void SetVariableModifiers(object modifiers)
{
this.ReflectedObject.Properties[VariableModifiersProperty].SetValue(
modifiers is ModelItem ? (modifiers as ModelItem).GetCurrentValue() : modifiers);
}
object GetVariableModifiers()
{
return this.ReflectedObject.Properties[VariableModifiersProperty].ComputedValue;
}
int GetScopeLevel()
{
int level = 0;
ModelItem parent = this.ReflectedObject.Parent;
while (null != parent)
{
++level;
parent = parent.Parent;
}
return level;
}
// Used by screen reader to read the DataGrid row.
public override string ToString()
{
string name = this.GetVariableNameString();
if (!string.IsNullOrEmpty(name))
{
return name;
}
return "Variable";
}
void SetVariableType(Type type)
{
if (!Type.Equals(type, this.GetVariableType()))
{
using (ModelEditingScope change = this.ReflectedObject.BeginEdit((string)this.Editor.FindResource("changeVariableTypeDescription")))
{
this.variableTypeChanged = true;
ModelItemCollection variableContainer = (ModelItemCollection)this.ReflectedObject.Parent;
//proceed only if variable is associated with container
if (null != variableContainer)
{
Variable variable = Variable.Create(this.GetVariableNameString(), type, (VariableModifiers)this.GetVariableModifiers());
//try to preserve expression
ModelItem expressionModelItem = this.ReflectedObject.Properties[VariableValueProperty].Value;
if (expressionModelItem != null)
{
Activity expression = expressionModelItem.GetCurrentValue() as Activity;
string currentExpression = ExpressionHelper.GetExpressionString(expression, expressionModelItem);
//check if there exists expression
if (currentExpression != null)
{
//finally - assign result to default property
variable.Default = ExpressionHelper.CreateExpression(type, currentExpression, false, null);
}
}
Editor.ChangeVariableType(this, variable);
ImportDesigner.AddImport(type.Namespace, this.Context);
}
change.Complete();
}
}
}
Type GetVariableType()
{
return (Type)this.ReflectedObject.Properties[VariableTypeProperty].ComputedValue;
}
void SetVariableScope(object newScope)
{
using (ModelEditingScope change = this.ReflectedObject.BeginEdit((string)this.Editor.FindResource("changeVariableScopeDescription")))
{
if (!ModelItem.Equals(newScope, this.GetVariableScope()))
{
ModelItemCollection currentScopeContainer = this.ReflectedObject.Parent.Parent.Properties["Variables"].Collection;
currentScopeContainer.Remove(this.ReflectedObject.GetCurrentValue());
ModelItem scope = (newScope as ModelItem) ?? Editor.ScopesList.FirstOrDefault(p => object.Equals(p.GetCurrentValue(), newScope));
ModelItemCollection newScopeContainer = scope.Properties["Variables"].Collection;
newScopeContainer.Add(this.ReflectedObject.GetCurrentValue());
Editor.NotifyVariableScopeChanged(this);
}
change.Complete();
}
}
object GetVariableScope()
{
return this.ReflectedObject.Parent.Parent;
}
void SetVariableValue(object value)
{
object expression = value is ModelItem ? ((ModelItem)value).GetCurrentValue() : value;
this.ReflectedObject.Properties[VariableValueProperty].SetValue(expression);
}
object GetVariableValue()
{
return this.ReflectedObject.Properties[VariableValueProperty].ComputedValue;
}
string GetToolTip()
{
ModelItem s = this.ReflectedObject.Parent.Parent;
IMultiValueConverter converter = (IMultiValueConverter)(this.Editor.FindResource("scopeToNameConverter"));
return ScopeToTooltipConverter.BuildToolTip(s, converter, CultureInfo.CurrentCulture);
}
protected override Type OnGetDynamicPropertyValueEditorType(string propertyName)
{
var type = this.GetVariableType();
//in case of variables which contain handles - display HandleValueEditor
if (typeof(Handle).IsAssignableFrom(type))
{
return typeof(HandleValueEditor);
}
var referenceType = typeof(PropertyValueEditor);
var expressionEditorType = typeof(ExpressionValueEditor);
//check if there are custom editors on the variable's type
var variableOfType = typeof(Variable<>).MakeGenericType(type);
//check if there are custom type editors associated with given type -
//look for type editor defined for Variable
//in search, skip ExpressionValueEditor instance - it will be returned by default for property grid, but for
//dataGrid nothing should be used - we use default dg template
var customEditorType = TypeDescriptor
.GetAttributes(variableOfType)
.OfType()
.Where(p =>
{
Type currentType = Type.GetType(p.EditorTypeName);
return (expressionEditorType != currentType && referenceType.IsAssignableFrom(currentType));
})
.Select(p => Type.GetType(p.EditorTypeName))
.FirstOrDefault();
//return custom editor type (if any)
if (null != customEditorType)
{
return customEditorType;
}
//otherwise - return default expression value editor
return typeof(ExpressionValueEditor);
}
protected override void OnPropertyChanged(string propertyName)
{
//this method is called by the thread's dispatcher AFTER all prorties have been updated, so all the property values
//are updated, regardless of the editing scope deep
if (string.Equals(propertyName, DesignTimeVariable.VariableScopeProperty))
{
this.RaisePropertyChangedEvent(ToolTipProperty);
this.RaisePropertyChangedEvent(VariableScopeLevelProperty);
}
else if (string.Equals(propertyName, DesignTimeVariable.VariableNameProperty))
{
this.RaisePropertyChangedEvent(AutomationIdProperty);
}
else if (string.Equals(propertyName, DesignTimeVariable.VariableTypeProperty))
{
this.RaisePropertyChangedEvent(VariableValueProperty);
}
else if (string.Equals(propertyName, DesignTimeVariable.TimestampProperty))
{
if (this.variableTypeChanged)
{
this.variableTypeChanged = false;
this.CustomValueEditors.Clear();
this.Editor.UpdateTypeDesigner(this);
}
}
base.OnPropertyChanged(propertyName);
}
bool ValidateVariableName(object newValue, List errors)
{
if (!base.IsUndoRedoInProgress && null != this.ReflectedObject.Parent)
{
VBIdentifierName identifier = newValue as VBIdentifierName;
string newName = null;
if (identifier != null)
{
newName = identifier.IdentifierName;
}
if (!string.IsNullOrEmpty(newName))
{
Func checkForDuplicates =
new Func(p => string.Equals(p.Properties[VariableNameProperty].ComputedValue, newName) && !object.Equals(p, this.ReflectedObject));
bool duplicates = this.ReflectedObject.Parent.Parent.Properties["Variables"].Collection.Any(checkForDuplicates);
if (duplicates)
{
errors.Add(string.Format(CultureInfo.CurrentUICulture, SR.DuplicateVariableName, newName));
}
}
else
{
errors.Add(SR.EmptyVariableName);
}
}
return 0 == errors.Count;
}
bool ValidateVariableScope(object newValue, List errors)
{
if (!base.IsUndoRedoInProgress)
{
ModelItem scope = (newValue as ModelItem) ?? Editor.ScopesList.FirstOrDefault(p => object.Equals(p.GetCurrentValue(), newValue));
string currentName = this.GetVariableNameString();
Func checkForDuplicates =
new Func(p => string.Equals(p.Properties[VariableNameProperty].ComputedValue, currentName) && !object.Equals(p, this.ReflectedObject));
bool duplicates = scope.Properties["Variables"].Collection.Any(checkForDuplicates);
if (duplicates)
{
errors.Add(string.Format(CultureInfo.CurrentUICulture, SR.DuplicateVariableName, currentName));
}
}
return 0 == errors.Count;
}
#region Internal classes
internal sealed class ScopeValueEditor : PropertyValueEditor
{
public ScopeValueEditor()
{
this.InlineEditorTemplate = EditorResources.GetResources()["ScopeEditor_InlineEditorTemplate"] as DataTemplate;
}
}
#endregion
}
sealed class DesignTimeVariableToScopeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ModelItem designTimeVariable = value as ModelItem;
object result = null;
if (null != designTimeVariable && typeof(DesignTimeVariable).IsAssignableFrom(designTimeVariable.ItemType))
{
DesignTimeVariable variable = (DesignTimeVariable)designTimeVariable.GetCurrentValue();
result = variable.Editor.ScopesList;
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw FxTrace.Exception.AsError(new NotSupportedException());
}
}
sealed class ScopeToTooltipConverter : IValueConverter
{
IMultiValueConverter baseConverter = new BreadCrumbTextConverter();
internal static string BuildToolTip(ModelItem entry, IMultiValueConverter displayNameConverter, CultureInfo culture)
{
string result = null;
if (null != entry && null != displayNameConverter)
{
StringBuilder sb = new StringBuilder();
int indent = 0;
ModelItem currentEntry = entry;
while (currentEntry != null)
{
if (null != currentEntry.Properties["Variables"])
{
++indent;
}
currentEntry = currentEntry.Parent;
}
while (entry != null)
{
if (null != entry.Properties["Variables"])
{
if (sb.Length != 0)
{
sb.Insert(0, "/");
sb.Insert(0, " ", --indent);
sb.Insert(0, Environment.NewLine);
}
var input = new object[] { entry, null != entry.Properties["DisplayName"] ? entry.Properties["DisplayName"].Value : null, (double)short.MaxValue };
sb.Insert(0, displayNameConverter.Convert(input, typeof(string), null, culture));
}
entry = entry.Parent;
}
result = sb.ToString();
}
return result;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return BuildToolTip(value as ModelItem, this.baseConverter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw FxTrace.Exception.AsError(new NotSupportedException());
}
}
sealed class ScopeComboBox : ComboBox
{
bool isScopeValid = true;
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
this.Loaded += (s, args) =>
{
//get the binding expression, and hook up exception filter
var expr = this.GetBindingExpression(ScopeComboBox.SelectedItemProperty);
if (null != expr && null != expr.ParentBinding)
{
expr.ParentBinding.UpdateSourceExceptionFilter = this.OnUpdateBindingException;
}
};
}
object OnUpdateBindingException(object sender, Exception err)
{
//if exception occured, the scope as invalid
if (err is TargetInvocationException && err.InnerException is ValidationException || err is ValidationException)
{
this.isScopeValid = false;
}
return null;
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
//if validation succeeded - update the control state with new selection
if (this.isScopeValid)
{
base.OnSelectionChanged(e);
}
//otherwise, get the binding expression and update control with current state from the source
else
{
var expr = this.GetBindingExpression(ScopeComboBox.SelectedItemProperty);
if (null != expr)
{
expr.UpdateTarget();
}
//the next failed validation pass may set this flag to false, but if validation succeeds, it has to be set to true
this.isScopeValid = true;
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SamlAssertion.cs
- DesignerSelectionListAdapter.cs
- HtmlHistory.cs
- BaseCodeDomTreeGenerator.cs
- XmlAttributes.cs
- ColorTransform.cs
- XhtmlConformanceSection.cs
- DefaultSection.cs
- BitmapSizeOptions.cs
- ResourcePermissionBaseEntry.cs
- CustomSignedXml.cs
- ApplicationBuildProvider.cs
- DockAndAnchorLayout.cs
- ArrayTypeMismatchException.cs
- JavascriptCallbackResponseProperty.cs
- SupportsEventValidationAttribute.cs
- WebEventTraceProvider.cs
- TdsValueSetter.cs
- OpCellTreeNode.cs
- Helper.cs
- DictationGrammar.cs
- TextBoxRenderer.cs
- XmlSchemaComplexContentExtension.cs
- FloaterBaseParaClient.cs
- ActivationArguments.cs
- GridLengthConverter.cs
- PropertyChange.cs
- WrapPanel.cs
- ItemDragEvent.cs
- FixedSOMContainer.cs
- BulletedList.cs
- Cell.cs
- TextOnlyOutput.cs
- WebPartDeleteVerb.cs
- AddInDeploymentState.cs
- LabelAutomationPeer.cs
- OleDbConnectionFactory.cs
- HtmlMeta.cs
- BuildResult.cs
- BuildResult.cs
- XmlDataDocument.cs
- ResourceAssociationSetEnd.cs
- EditorZoneBase.cs
- ELinqQueryState.cs
- DetailsViewModeEventArgs.cs
- ContentWrapperAttribute.cs
- FamilyTypeface.cs
- FontStretch.cs
- ReachSerializableProperties.cs
- StylusCollection.cs
- Rectangle.cs
- TypedRowGenerator.cs
- MessageBox.cs
- MessageTraceRecord.cs
- XmlUtf8RawTextWriter.cs
- MethodExpr.cs
- CodeConditionStatement.cs
- MissingManifestResourceException.cs
- EffectiveValueEntry.cs
- BinaryFormatterWriter.cs
- SiteMapDataSourceView.cs
- QfeChecker.cs
- JavaScriptString.cs
- ApplicationFileCodeDomTreeGenerator.cs
- CssStyleCollection.cs
- RemoteWebConfigurationHostStream.cs
- JsonStringDataContract.cs
- WeakReferenceList.cs
- DataException.cs
- UnicastIPAddressInformationCollection.cs
- X509Chain.cs
- EdmPropertyAttribute.cs
- GridPattern.cs
- ResizeGrip.cs
- GridViewEditEventArgs.cs
- StylusButton.cs
- TraceEventCache.cs
- AutomationPatternInfo.cs
- TabletCollection.cs
- TableRowCollection.cs
- ArrangedElement.cs
- CompleteWizardStep.cs
- ContentDisposition.cs
- iisPickupDirectory.cs
- CancellationTokenSource.cs
- MenuItemBinding.cs
- HttpApplicationFactory.cs
- RequestCachePolicy.cs
- SchemaCollectionPreprocessor.cs
- FixedLineResult.cs
- OAVariantLib.cs
- MSG.cs
- SystemResourceKey.cs
- CatalogZone.cs
- PackageRelationshipSelector.cs
- ContentTextAutomationPeer.cs
- EntityDataSourceDesigner.cs
- SqlNodeAnnotation.cs
- PropVariant.cs
- RpcAsyncResult.cs