VariableDesigner.xaml.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / 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.
//---------------------------------------------------------------- 
// 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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK