File: System.Activities.Presentation\System\Activities\Presentation\View\VariableDesigner.xaml.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
namespace System.Activities.Presentation.View
{
    using System;
    using System.Activities.Presentation.Annotations;
    using System.Activities.Presentation.Hosting;
    using System.Activities.Presentation.Model;
    using System.Activities.Presentation.PropertyEditing;
    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.Reflection;
    using System.Runtime;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Threading;
 
    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<ModelItem> scopesList = new List<ModelItem>();
        ObservableCollection<DesignTimeVariable> variableWrapperCollection = new ObservableCollection<DesignTimeVariable>();
        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<ModelItem> 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<DesignTimeVariable, bool> checkForDuplicates = new Func<DesignTimeVariable, bool>(p => string.Equals((string)p.ReflectedObject.Properties["Name"].ComputedValue, newName, StringComparison.OrdinalIgnoreCase) && !object.Equals(p.GetVariableName(), identifierName));
            DesignTimeVariable duplicate = this.variableWrapperCollection.FirstOrDefault<DesignTimeVariable>(checkForDuplicates);
            if (duplicate != null)
            {
                identifierName.IsValid = false;
                identifierName.ErrorMessage = string.Format(CultureInfo.CurrentUICulture, SR.DuplicateIdentifier, newName);
                VBIdentifierName duplicateIdentifier = duplicate.GetVariableName();
                if (duplicateIdentifier.IsValid)
                {
                    duplicateIdentifier.IsValid = false;
                    duplicateIdentifier.ErrorMessage = string.Format(CultureInfo.CurrentUICulture, SR.DuplicateIdentifier, 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<DesignTimeVariable, bool> checkForOldNameDuplicates = new Func<DesignTimeVariable, bool>(p => string.Equals((string)p.ReflectedObject.Properties["Name"].ComputedValue, oldName, StringComparison.OrdinalIgnoreCase) && !object.Equals(p.GetVariableName(), identifier));
            IEnumerable<DesignTimeVariable> oldDuplicates = this.variableWrapperCollection.Where<DesignTimeVariable>(checkForOldNameDuplicates);
            if (oldDuplicates.Count<DesignTimeVariable>() == 1)
            {
                DesignTimeVariable wrapper = oldDuplicates.First<DesignTimeVariable>();
                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<DesignTimeVariable>(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<ModelTreeManager>();
                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<Selection>().PrimarySelection;
                if (null == current || !typeof(DesignTimeVariable).IsAssignableFrom(current.ItemType))
                {
                    this.lastSelection = current;
                }
            }
        }
 
        internal void SelectVariable(ModelItem variable)
        {
            this.Dispatcher.BeginInvoke(new Action(() =>
            {
                foreach (object item in this.variableDataGrid.Items)
                {
                    if (item is DesignTimeVariable)
                    {
                        if (object.ReferenceEquals(((DesignTimeVariable)item).ReflectedObject, variable))
                        {
                            this.variableDataGrid.SelectedItem = item;
                            this.variableDataGrid.ScrollIntoView(item, null);
                        }
                    }
                }
            }), DispatcherPriority.ApplicationIdle);
 
        }
 
        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
                {
                    // clear variables in selection
                    Selection oldSelection = this.Context.Items.GetValue<Selection>();
                    List<ModelItem> newSelection = new List<ModelItem>();
                    if (oldSelection != null && oldSelection.SelectionCount > 0)
                    {
                        foreach (ModelItem item in oldSelection.SelectedObjects)
                        {
                            if (item.ItemType != typeof(DesignTimeVariable))
                            {
                                newSelection.Add(item);
                            }
                        }
                    }
                    this.Context.Items.SetValue(new Selection(newSelection));
                }
                this.isSelectionSourceInternal = false;
            }
        }
 
        void OnContextChanged()
        {
            if (null != this.Context)
            {
                this.Context.Items.Subscribe<Selection>(new SubscribeContextCallback<Selection>(OnItemSelected));
            }
            this.dgHelper.Context = this.Context;
        }
 
        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<UndoEngine>().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<Selection>().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<DesignTimeVariable>())
            {
                ModelItem element = VariableHelper.GetVariableScopeElement(primarySelection);
                ModelItemCollection newVariableScope = VariableHelper.GetVariableCollection(element);
                if (this.CurrentVariableScope != newVariableScope)
                {
                    this.CurrentVariableScope = newVariableScope;
                }
                else
                {
                    bool selectedVariableIsInSelection = false;
                    Selection selection = this.Context.Items.GetValue<Selection>();
 
                    DesignTimeVariable selectedVariable = this.variableDataGrid.SelectedItem as DesignTimeVariable;
                    if (selectedVariable != null)
                    {
                        foreach (ModelItem item in selection.SelectedObjects)
                        {
                            if (object.ReferenceEquals(selectedVariable, item.GetCurrentValue()))
                            {
                                selectedVariableIsInSelection = true;
                            }
                        }
                    }
 
                    if (!selectedVariableIsInSelection)
                    {
                        this.variableDataGrid.SelectedItem = null;
                    }
                }
            }
        }
 
        void OnVariableScopeChanged()
        {
            this.Populate(null != this.CurrentVariableScope ? this.CurrentVariableScope.Parent : null);
        }
 
        void OnVariableCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            var isUndoRedoInProgress = this.Context.Services.GetService<UndoEngine>().IsUndoRedoInProgress;
 
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (ModelItem variable in e.NewItems)
                    {
                        DesignTimeVariable wrapper = this.variableWrapperCollection
                            .FirstOrDefault<DesignTimeVariable>(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:
                    bool isUndoRedoInProgress = this.Context.Services.GetService<UndoEngine>().IsUndoRedoInProgress;
                    foreach (DesignTimeVariable arg in e.OldItems)
                    {
                        if (!isUndoRedoInProgress)
                        {
                            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.VariableDefaultProperty);
 
            //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.VariableDefaultProperty).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.VariableDefaultProperty) as DialogPropertyValueEditor;
        }
 
        ModelProperty OnShowExtendedValueEditor(DataGridCell cell, object instance)
        {
            var variable = (DesignObjectWrapper)cell.DataContext;
            return variable.Content.Properties[DesignTimeVariable.VariableDefaultProperty];
        }
 
        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();
            }
        }
 
        void OnEditingControlLoaded(object sender, RoutedEventArgs args)
        {
            DataGridHelper.OnEditingControlLoaded(sender, args);
        }
 
        void OnEditingControlUnloaded(object sender, RoutedEventArgs args)
        {
            DataGridHelper.OnEditingControlUnloaded(sender, args);
        }
 
        // This is to workaround a bug that updating ModelItem from outside of VariableDesigner will not update VariableDesigner.
        internal void NotifyAnnotationTextChanged()
        {
            foreach (object item in this.variableDataGrid.Items)
            {
                DesignTimeVariable designTimeVariable = item as DesignTimeVariable;
                if (designTimeVariable != null)
                {
                    designTimeVariable.NotifyPropertyChanged(DesignTimeVariable.AnnotationTextProperty);
                }
            }
        }
 
        protected override void OnContextMenuOpening(ContextMenuEventArgs e)
        {
            base.OnContextMenuOpening(e);
 
            DesignerConfigurationService configurationService = this.Context.Services.GetService<DesignerConfigurationService>();
            Fx.Assert(configurationService != null, "DesignerConfigurationService should not be null");
            if (configurationService.WorkflowDesignerHostId == WorkflowDesignerHostId.Dev10)
            {
                return;
            }
            
            e.Handled = true;
 
            bool openedByKeyboard = e.CursorLeft < 0 && e.CursorTop < 0;
 
            if (openedByKeyboard)
            {
                this.ContextMenu.Placement = PlacementMode.Center;
            }
            else
            {
                this.ContextMenu.Placement = PlacementMode.MousePoint;
            }
            this.ContextMenu.PlacementTarget = this;
            this.ContextMenu.IsOpen = true;
        }
 
        protected void OnDataGridCellGotFocus(object sender, RoutedEventArgs e)
        {
            if (!LocalAppContextSwitches.UseLegacyAccessibilityFeatures)
            {
                DataGridHelper.DataGridCellGotFocus((DataGrid)sender, e);
            }
        }
 
        private void OnDeleteCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            ContextMenuUtilities.OnDeleteCommandCanExecute(e, this.variableDataGrid);
        }
 
        private void OnDeleteCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count > 0)
            {
                List<ModelItem> list = new List<ModelItem>();
                foreach (object item in this.variableDataGrid.SelectedItems)
                {
                    DesignTimeVariable designTimeVariable = item as DesignTimeVariable;
                    if (designTimeVariable != null)
                    {
                        list.Add(designTimeVariable.ReflectedObject);
                    }
                }
 
                foreach (ModelItem modelItem in list)
                {
                    foreach (DesignTimeVariable designTimeVariable in this.variableWrapperCollection)
                    {
                        if (designTimeVariable.ReflectedObject == modelItem)
                        {
                            this.variableWrapperCollection.Remove(designTimeVariable);
                            break;
                        }
                    }
                }
            }
 
            e.Handled = true;
        }
 
        private void OnAnnotationSeparatorLoaded(object sender, RoutedEventArgs e)
        {
            ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
        }
 
        private void OnAddAnnotationMenuLoaded(object sender, RoutedEventArgs e)
        {
            ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
        }
 
        private void OnAddAnnotationCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            ContextMenuUtilities.OnAddAnnotationCommandCanExecute(e, this.Context, this.variableDataGrid);
        }
 
        private void OnAddAnnotationCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count == 1)
            {
                AnnotationDialog dialog = new AnnotationDialog();
                dialog.Context = Context;
                dialog.Title = SR.AddAnnotationTitle;
 
                WindowHelperService service = this.Context.Services.GetService<WindowHelperService>();
                if (null != service)
                {
                    service.TrySetWindowOwner(this, dialog);
                }
                dialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
 
                if (dialog.ShowDialog() == true)
                {
                    string annotationText = dialog.AnnotationText;
 
                    DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItems[0];
                    variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].SetValue(annotationText);
                }
            }
 
            e.Handled = true;
        }
 
        private void OnEditAnnotationMenuLoaded(object sender, RoutedEventArgs e)
        {
            ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
        }
 
        private void OnEditAnnotationCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            // call the same method as delete annotation command
            ContextMenuUtilities.OnDeleteAnnotationCommandCanExecute(e, this.Context, this.variableDataGrid);
        }
 
        private void OnEditAnnotationCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count == 1)
            {
                DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItems[0];
 
                AnnotationDialog dialog = new AnnotationDialog();
                dialog.Context = Context;
                dialog.Title = SR.EditAnnotationTitle;
                dialog.AnnotationText = variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].ComputedValue as string;
 
                WindowHelperService service = this.Context.Services.GetService<WindowHelperService>();
                if (null != service)
                {
                    service.TrySetWindowOwner(this, dialog);
                }
                dialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
 
                if (dialog.ShowDialog() == true)
                {
                    string annotationText = dialog.AnnotationText;
 
                    variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].SetValue(annotationText);
                }
            }
        }
 
        private void OnDeleteAnnotationMenuLoaded(object sender, RoutedEventArgs e)
        {
            ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
        }
 
        private void OnDeleteAnnotationCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            ContextMenuUtilities.OnDeleteAnnotationCommandCanExecute(e, this.Context, this.variableDataGrid);
        }
 
        private void OnDeleteAnnotationCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count == 1)
            {
                DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItems[0];
                variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].ClearValue();
            }
 
            e.Handled = true;
        }
    }
 
    internal static class VariableHelper
    {
        static Type VariablesCollectionType = typeof(Collection<Variable>);
        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<ModelItem> FindDeclaredVariables(this ModelItem element, IList<ModelItem> 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<ModelItem> FindDeclaredVariables(this ModelItem element)
        {
            return VariableHelper.FindDeclaredVariables(element, null);
        }
 
        internal static List<ModelItem> FindActivityDelegateArgumentsInScope(this ModelItem workflowElement)
        {
            List<ModelItem> variables = new List<ModelItem>();
            if (workflowElement != null)
            {
                workflowElement = workflowElement.Parent;
                while (null != workflowElement)
                {
                    variables.AddRange(workflowElement.FindActivityDelegateArguments());
                    workflowElement = workflowElement.Parent;
                }
            }
            return variables;
        }
 
        internal static List<ModelItem> FindActivityDelegateArguments(this ModelItem element)
        {
            List<ModelItem> delegateArguments = new List<ModelItem>();
            if (element.GetCurrentValue() is ActivityDelegate)
            {
                //browse all properties in given ActivityDelegate
                delegateArguments.AddRange(element.Properties
                    //choose only those of base type equal to DelegateArgument
                    .Where<ModelProperty>(p => (typeof(DelegateArgument).IsAssignableFrom(p.PropertyType) && null != p.Value))
                    //from those, take actual ModelItem value
                    .Select<ModelProperty, ModelItem>(p => p.Value));
            }
 
            return delegateArguments;
        }
 
        internal static List<ModelItem> FindVariablesInScope(this ModelItem element, IList<ModelItem> scopeList)
        {
            List<ModelItem> variables = new List<ModelItem>();
            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<ModelItem> FindUniqueVariablesInScope(ModelItem element)
        {
            Dictionary<string, ModelItem> variables = new Dictionary<string, ModelItem>();
            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<ModelItem>(variables.Values);
        }
 
 
        internal static List<ModelItem> 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 VariableDefaultProperty = "Default";
        internal static readonly string ToolTipProperty = "ToolTip";
        internal static readonly string VariableScopeLevelProperty = "ScopeLevel";
        internal static readonly string VariableModifiersProperty = "Modifiers";
        internal static readonly string AnnotationTextProperty = "DesignTimeVariableAnnotationText";
        static readonly string[] Properties =
            new string[] { VariableNameProperty, VariableTypeProperty, VariableScopeProperty, VariableDefaultProperty, 
                           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<Attribute>().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<Attribute>().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<Attribute>().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 = VariableDefaultProperty,
                    PropertyType = typeof(Activity),
                    PropertyAttributes = TypeDescriptor.GetAttributes(typeof(Activity)).OfType<Attribute>().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<Attribute>().ToArray(), 
                    PropertySetter = (instance, newValue) => 
                        {
                            ((DesignTimeVariable)instance).SetVariableModifiers(newValue);
                        },
                    PropertyValidator = null,
                    PropertyGetter = (instance) => 
                        {
                            return ((DesignTimeVariable)instance).GetVariableModifiers();
                        }
                },
                new PropertyDescriptorData()
                {
                    PropertyName = AnnotationTextProperty,
                    PropertyType = typeof(string),
                    PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
                    PropertySetter = (instance, newValue) =>
                        {
                            ((DesignTimeVariable)instance).SetAnnotationText(newValue);
                        },
                    PropertyValidator = null,
                    PropertyGetter = (instance) =>
                        {
                            return ((DesignTimeVariable)instance).GetAnnotationText();
                        }
                }
            };
        }
        #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 == Annotation.AnnotationTextPropertyName)
            {
                RaisePropertyChangedEvent(AnnotationTextProperty);
            }
 
            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());
                        string annotationText = this.GetAnnotationText() as string;
                        if (annotationText != null)
                        {
                            Annotation.SetAnnotationText(variable, annotationText);
                        }
 
                        //try to preserve expression
                        ModelItem expressionModelItem = this.ReflectedObject.Properties[VariableDefaultProperty].Value;
                        if (expressionModelItem != null)
                        {
                            ActivityWithResult expression = expressionModelItem.GetCurrentValue() as ActivityWithResult;
                            //check if there existed expression
                            if (expression != null)
                            {
                                ActivityWithResult morphedExpression = null;
                                if (ExpressionHelper.TryMorphExpression(expression, false, type, this.Context, out morphedExpression))
                                {
                                    variable.Default = morphedExpression;
                                }
                                //Microsoft 
 
                            }
                        }
                        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[VariableDefaultProperty].SetValue(expression);
        }
 
        object GetVariableValue()
        {
            return this.ReflectedObject.Properties[VariableDefaultProperty].ComputedValue;
        }
 
        string GetToolTip()
        {
            ModelItem s = this.ReflectedObject.Parent.Parent;
            IMultiValueConverter converter = (IMultiValueConverter)(this.Editor.FindResource("scopeToNameConverter"));
            return ScopeToTooltipConverter.BuildToolTip(s, converter, CultureInfo.CurrentCulture);
        }
 
        object GetAnnotationText()
        {
            ModelProperty property = this.ReflectedObject.Properties.Find(Annotation.AnnotationTextPropertyName);
 
            if (property != null)
            {
                return property.ComputedValue;
            }
            else
            {
                return null;
            }
        }
 
        void SetAnnotationText(object annotationText)
        {
            ModelProperty property = this.ReflectedObject.Properties.Find(Annotation.AnnotationTextPropertyName);
 
            if (property != null)
            {
                property.SetValue(annotationText);
            }
        }
 
        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<T>
            //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<EditorAttribute>()
                .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(VariableDefaultProperty);
            }
            else if (string.Equals(propertyName, DesignTimeVariable.TimestampProperty))
            {
                if (this.variableTypeChanged)
                {
                    this.RaisePropertyChangedEvent(DesignTimeVariable.VariableTypeProperty);
                    this.variableTypeChanged = false;
                    this.CustomValueEditors.Clear();
                    this.Editor.UpdateTypeDesigner(this);
                }
            }
            base.OnPropertyChanged(propertyName);
        }
 
        bool ValidateVariableName(object newValue, List<string> 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<ModelItem, bool> checkForDuplicates =
                        new Func<ModelItem, bool>(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<string> errors)
        {
            if (!base.IsUndoRedoInProgress)
            {
                ModelItem scope = (newValue as ModelItem) ?? Editor.ScopesList.FirstOrDefault(p => object.Equals(p.GetCurrentValue(), newValue));
                string currentName = this.GetVariableNameString();
 
                Func<ModelItem, bool> checkForDuplicates =
                    new Func<ModelItem, bool>(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;
            }
        }
    }
}