[corlib] Update ValueTuple implementation
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / View / VariableDesigner.xaml.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation.View
6 {
7     using System;
8     using System.Activities.Presentation.Annotations;
9     using System.Activities.Presentation.Hosting;
10     using System.Activities.Presentation.Model;
11     using System.Activities.Presentation.PropertyEditing;
12     using System.Collections;
13     using System.Collections.Generic;
14     using System.Collections.ObjectModel;
15     using System.Collections.Specialized;
16     using System.ComponentModel;
17     using System.Globalization;
18     using System.Linq;
19     using System.Reflection;
20     using System.Runtime;
21     using System.Text;
22     using System.Windows;
23     using System.Windows.Controls;
24     using System.Windows.Controls.Primitives;
25     using System.Windows.Data;
26     using System.Windows.Input;
27     using System.Windows.Threading;
28
29     partial class VariableDesigner
30     {
31         public static readonly DependencyProperty ContextProperty = DependencyProperty.Register(
32             "Context",
33             typeof(EditingContext),
34             typeof(VariableDesigner),
35             new FrameworkPropertyMetadata(null, new PropertyChangedCallback(VariableDesigner.OnContextChanged)));
36
37         static readonly DependencyPropertyKey CurrentVariableScopePropertyKey = DependencyProperty.RegisterReadOnly(
38             "CurrentVariableScope",
39             typeof(ModelItemCollection),
40             typeof(VariableDesigner),
41             new UIPropertyMetadata(null, new PropertyChangedCallback(OnVariableScopeChanged)));
42
43         public static readonly DependencyProperty CurrentVariableScopeProperty = CurrentVariableScopePropertyKey.DependencyProperty;
44
45         public static readonly RoutedEvent VariableCollectionChangedEvent =
46                 EventManager.RegisterRoutedEvent("VariableCollectionChanged",
47                 RoutingStrategy.Bubble,
48                 typeof(RoutedEventHandler),
49                 typeof(VariableDesigner));
50
51         const string DefaultVariableName = "variable";
52
53         List<ModelItem> scopesList = new List<ModelItem>();
54         ObservableCollection<DesignTimeVariable> variableWrapperCollection = new ObservableCollection<DesignTimeVariable>();
55         bool isSelectionSourceInternal = false;
56         ModelItem variableToSelect = null;
57         bool continueScopeEdit = false;
58         ModelItem lastSelection;
59
60         DataGridHelper dgHelper;
61
62         public VariableDesigner()
63         {
64             InitializeComponent();
65
66             this.dgHelper = new DataGridHelper(this.variableDataGrid, this);
67             this.dgHelper.Context = this.Context;
68             this.dgHelper.AddNewRowCommand = DesignerView.CreateVariableCommand;
69             this.dgHelper.AddNewRowContent = (string)this.FindResource("addVariableNewRowLabel");
70             this.dgHelper.ResolveDynamicTemplateCallback = this.OnResolveDynamicContentTemplate;
71             this.dgHelper.LoadDynamicContentDataCallback = this.OnShowExtendedValueEditor;
72             this.dgHelper.LoadCustomPropertyValueEditorCallback = this.OnLoadExtendedValueEditor;
73             this.dgHelper.ShowValidationErrorAsToolTip = false;
74
75             this.variableDataGrid.SelectionChanged += OnDataGridRowSelected;
76             this.variableWrapperCollection.CollectionChanged += OnVariableWrapperCollectionChanged;
77             this.variableDataGrid.ItemsSource = this.variableWrapperCollection;
78
79             var converter = (BreadCrumbTextConverter)this.FindResource("scopeToNameConverter");
80             converter.PixelsPerChar = (this.FontSize - 5);
81         }
82
83         public event RoutedEventHandler VariableCollectionChanged
84         {
85             add
86             {
87                 AddHandler(VariableCollectionChangedEvent, value);
88             }
89             remove
90             {
91                 RemoveHandler(VariableCollectionChangedEvent, value);
92             }
93         }
94
95         public EditingContext Context
96         {
97             get { return (EditingContext)GetValue(ContextProperty); }
98             set { SetValue(ContextProperty, value); }
99         }
100
101         public ModelItemCollection CurrentVariableScope
102         {
103             get { return (ModelItemCollection)GetValue(CurrentVariableScopeProperty); }
104             private set { SetValue(CurrentVariableScopePropertyKey, value); }
105         }
106
107         public List<ModelItem> ScopesList
108         {
109             get { return this.scopesList; }
110         }
111
112         public bool CreateNewVariableWrapper()
113         {
114             bool result = false;
115             ModelItemCollection scope = this.GetDefaultVariableScope();
116             if (null != scope)
117             {
118                 DesignTimeVariable wrapperObject = null;
119                 Variable variable = Variable.Create(this.GetDefaultName(), this.GetDefaultType(), VariableModifiers.None);
120                 using (ModelEditingScope change = scope.BeginEdit((string)this.FindResource("addNewVariableDescription")))
121                 {
122                     ModelItem wrappedVariable = scope.Add(variable);
123                     wrapperObject = new DesignTimeVariable(wrappedVariable, this);
124                     this.variableWrapperCollection.Add(wrapperObject);
125                     change.Complete();
126                     result = true;
127                 }
128                 this.dgHelper.BeginRowEdit(wrapperObject);
129             }
130             return result;
131         }
132
133         internal void ChangeVariableType(DesignTimeVariable oldVariableWrapper, Variable newVariable)
134         {
135             //check who is the sender of the event - data grid or property inspector
136             int index = this.variableDataGrid.Items.IndexOf(this.variableDataGrid.SelectedItem);
137             DataGridCell cell = DataGridHelper.GetCell(this.variableDataGrid, index, 1);
138             bool shouldReselct = cell.IsEditing;
139
140             //get location of the variable
141             ModelItemCollection container = VariableHelper.GetVariableCollection(oldVariableWrapper.ReflectedObject.Parent.Parent);
142             index = container.IndexOf(oldVariableWrapper.ReflectedObject);
143             //remove all value 
144             container.RemoveAt(index);
145             oldVariableWrapper.Dispose();
146             oldVariableWrapper.Initialize(container.Insert(index, newVariable));
147         }
148
149         internal void NotifyVariableScopeChanged(DesignTimeVariable variable)
150         {
151             this.variableToSelect = null != variable ? variable.ReflectedObject : null;
152             int index = this.variableDataGrid.Items.IndexOf(this.variableDataGrid.SelectedItem);
153             DataGridCell cell = DataGridHelper.GetCell(this.variableDataGrid, index, 2);
154             this.continueScopeEdit = cell.IsEditing;
155         }
156
157         //Check case-insensitive duplicates, which are not allowed in VB expressions 
158         internal void CheckCaseInsensitiveDuplicates(VBIdentifierName identifierName, string newName)
159         {
160             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));
161             DesignTimeVariable duplicate = this.variableWrapperCollection.FirstOrDefault<DesignTimeVariable>(checkForDuplicates);
162             if (duplicate != null)
163             {
164                 identifierName.IsValid = false;
165                 identifierName.ErrorMessage = string.Format(CultureInfo.CurrentUICulture, SR.DuplicateIdentifier, newName);
166                 VBIdentifierName duplicateIdentifier = duplicate.GetVariableName();
167                 if (duplicateIdentifier.IsValid)
168                 {
169                     duplicateIdentifier.IsValid = false;
170                     duplicateIdentifier.ErrorMessage = string.Format(CultureInfo.CurrentUICulture, SR.DuplicateIdentifier, duplicateIdentifier.IdentifierName);
171                 }
172             };
173         }
174
175         //Check duplicates with old value. When there's only one variable duplicate with the old value, 
176         //the only one variable should be valid now after the change
177         void ClearCaseInsensitiveDuplicates(VBIdentifierName identifier, string oldName)
178         {
179             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));
180             IEnumerable<DesignTimeVariable> oldDuplicates = this.variableWrapperCollection.Where<DesignTimeVariable>(checkForOldNameDuplicates);
181             if (oldDuplicates.Count<DesignTimeVariable>() == 1)
182             {
183                 DesignTimeVariable wrapper = oldDuplicates.First<DesignTimeVariable>();
184                 VBIdentifierName oldDuplicate = wrapper.GetVariableName();
185                 oldDuplicate.IsValid = true;
186                 oldDuplicate.ErrorMessage = string.Empty;
187             }
188         }
189
190         internal void NotifyVariableNameChanged(VBIdentifierName identifierName, string newName, string oldName)
191         {
192             //Check whether there're any variables' name conflict with the old name which can be cleaned up now
193             this.ClearCaseInsensitiveDuplicates(identifierName, oldName);
194
195             //Check whether there're any duplicates with new name    
196             this.CheckCaseInsensitiveDuplicates(identifierName, newName);
197         }
198
199         internal void UpdateTypeDesigner(DesignTimeVariable variable)
200         {
201             this.dgHelper.UpdateDynamicContentColumns(variable);
202         }
203
204         string GetDefaultName()
205         {
206             return this.variableWrapperCollection.GetUniqueName<DesignTimeVariable>(VariableDesigner.DefaultVariableName, wrapper => wrapper.GetVariableName().IdentifierName);
207         }
208
209         Type GetDefaultType()
210         {
211             return typeof(string);
212         }
213
214         ModelItemCollection GetDefaultVariableScope()
215         {
216             //do a lazdy scope refresh
217             //if we have a valid variable scope
218             if (null != this.CurrentVariableScope)
219             {
220                 //get the tree manager
221                 var treeManager = this.Context.Services.GetService<ModelTreeManager>();
222                 if (null != treeManager)
223                 {
224                     //get the model tree root 
225                     var root = treeManager.Root;
226                     //get the first scope, which is attached to the model tree (in case of undo/redo operations, even though VariableScope might be
227                     //valid, it actually isn't attached to model tree, so using it as a variable scope doesn't make any sense)
228                     var validScope = this.scopesList.FirstOrDefault(p => ModelItem.Equals(p, root) || root.IsParentOf(p));
229                     //check if valid scope is different that current variable scope. most likely - due to undo/redo operation which removed an activity
230                     if (!ModelItem.Equals(validScope, this.CurrentVariableScope.Parent))
231                     {
232                         //it is different - update the current variable scope (this setter will unhook old event handlers, clean the scopesList collection and hook 
233                         //for new event notifications
234                         this.CurrentVariableScope = validScope.GetVariableCollection();
235                     }
236                 }
237             }
238             //return validated variable scope
239             return this.CurrentVariableScope;
240         }
241
242         void Populate(ModelItem workflowElement)
243         {
244             this.scopesList.ForEach(p =>
245             {
246                 p.Properties["Variables"].Collection.CollectionChanged -= OnVariableCollectionChanged;
247             });
248
249             this.scopesList.Clear();
250             this.variableDataGrid.ItemsSource = null;
251             this.variableWrapperCollection.All(p => { p.Dispose(); return true; });
252             this.variableWrapperCollection.Clear();
253             var allVariables = VariableHelper.FindDeclaredVariables(workflowElement, this.scopesList);
254             //fill variable wrapper collection only if designer is visible
255             if (workflowElement != null && this.IsVisible)
256             {
257                 allVariables.ForEach(variable =>
258                     {
259                         this.variableWrapperCollection.Add(new DesignTimeVariable(variable, this));
260                     });
261             }
262             this.variableDataGrid.ItemsSource = this.variableWrapperCollection;
263
264             this.scopesList.ForEach(p =>
265             {
266                 p.Properties["Variables"].Collection.CollectionChanged += OnVariableCollectionChanged;
267             });
268         }
269
270         void StoreLastSelection()
271         {
272             if (!this.isSelectionSourceInternal)
273             {
274                 ModelItem current = this.Context.Items.GetValue<Selection>().PrimarySelection;
275                 if (null == current || !typeof(DesignTimeVariable).IsAssignableFrom(current.ItemType))
276                 {
277                     this.lastSelection = current;
278                 }
279             }
280         }
281
282         internal void SelectVariable(ModelItem variable)
283         {
284             this.Dispatcher.BeginInvoke(new Action(() =>
285             {
286                 foreach (object item in this.variableDataGrid.Items)
287                 {
288                     if (item is DesignTimeVariable)
289                     {
290                         if (object.ReferenceEquals(((DesignTimeVariable)item).ReflectedObject, variable))
291                         {
292                             this.variableDataGrid.SelectedItem = item;
293                             this.variableDataGrid.ScrollIntoView(item, null);
294                         }
295                     }
296                 }
297             }), DispatcherPriority.ApplicationIdle);
298
299         }
300
301         void OnDataGridRowSelected(object sender, RoutedEventArgs e)
302         {
303             if (null != this.Context && !this.isSelectionSourceInternal)
304             {
305                 this.isSelectionSourceInternal = true;
306                 if (null != this.variableDataGrid.SelectedItem && this.variableDataGrid.SelectedItem is DesignTimeVariable)
307                 {
308                     DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItem;
309                     this.Context.Items.SetValue(new Selection(variable.Content));
310                 }
311                 else
312                 {
313                     // clear variables in selection
314                     Selection oldSelection = this.Context.Items.GetValue<Selection>();
315                     List<ModelItem> newSelection = new List<ModelItem>();
316                     if (oldSelection != null && oldSelection.SelectionCount > 0)
317                     {
318                         foreach (ModelItem item in oldSelection.SelectedObjects)
319                         {
320                             if (item.ItemType != typeof(DesignTimeVariable))
321                             {
322                                 newSelection.Add(item);
323                             }
324                         }
325                     }
326                     this.Context.Items.SetValue(new Selection(newSelection));
327                 }
328                 this.isSelectionSourceInternal = false;
329             }
330         }
331
332         void OnContextChanged()
333         {
334             if (null != this.Context)
335             {
336                 this.Context.Items.Subscribe<Selection>(new SubscribeContextCallback<Selection>(OnItemSelected));
337             }
338             this.dgHelper.Context = this.Context;
339         }
340
341         void OnItemSelected(Selection newSelection)
342         {
343             //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
344             if (!this.isSelectionSourceInternal)
345             {
346                 //whenever selection changes:
347                 //1) check if selection is a result of undo/redo operation - if it is, we might be in the middle of collection changed 
348                 //   notification, so i have to postpone variable scope update untill collection update is completed.
349                 if (this.Context.Services.GetService<UndoEngine>().IsUndoRedoInProgress)
350                 {
351                     //delegate call to update selection after update is completed
352                     this.Dispatcher.BeginInvoke(new Action(() =>
353                     {
354                         //get current selection - i can't use newSelection passed into the method, because undo/redo may alter that value
355                         var current = this.Context.Items.GetValue<Selection>().PrimarySelection;
356                         //do the actual selection update
357                         this.OnItemSelectedCore(current);
358                     }), DispatcherPriority.ApplicationIdle);
359                 }
360                 else
361                 {
362                     //store element selected before variable designer started updating selection - when designer is closed, we try to restore that selection
363                     this.StoreLastSelection();
364                     this.OnItemSelectedCore(newSelection.PrimarySelection);
365                 }
366             }
367         }
368
369         void OnItemSelectedCore(ModelItem primarySelection)
370         {
371             //update variable scope - but ignore selection changes made by selecting variables.
372             if (null == primarySelection || !primarySelection.IsAssignableFrom<DesignTimeVariable>())
373             {
374                 ModelItem element = VariableHelper.GetVariableScopeElement(primarySelection);
375                 ModelItemCollection newVariableScope = VariableHelper.GetVariableCollection(element);
376                 if (this.CurrentVariableScope != newVariableScope)
377                 {
378                     this.CurrentVariableScope = newVariableScope;
379                 }
380                 else
381                 {
382                     bool selectedVariableIsInSelection = false;
383                     Selection selection = this.Context.Items.GetValue<Selection>();
384
385                     DesignTimeVariable selectedVariable = this.variableDataGrid.SelectedItem as DesignTimeVariable;
386                     if (selectedVariable != null)
387                     {
388                         foreach (ModelItem item in selection.SelectedObjects)
389                         {
390                             if (object.ReferenceEquals(selectedVariable, item.GetCurrentValue()))
391                             {
392                                 selectedVariableIsInSelection = true;
393                             }
394                         }
395                     }
396
397                     if (!selectedVariableIsInSelection)
398                     {
399                         this.variableDataGrid.SelectedItem = null;
400                     }
401                 }
402             }
403         }
404
405         void OnVariableScopeChanged()
406         {
407             this.Populate(null != this.CurrentVariableScope ? this.CurrentVariableScope.Parent : null);
408         }
409
410         void OnVariableCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
411         {
412             var isUndoRedoInProgress = this.Context.Services.GetService<UndoEngine>().IsUndoRedoInProgress;
413
414             switch (e.Action)
415             {
416                 case NotifyCollectionChangedAction.Add:
417                     foreach (ModelItem variable in e.NewItems)
418                     {
419                         DesignTimeVariable wrapper = this.variableWrapperCollection
420                             .FirstOrDefault<DesignTimeVariable>(p => (ModelItem.Equals(p.ReflectedObject, variable)));
421
422                         if (wrapper == null)
423                         {
424                             wrapper = new DesignTimeVariable(variable, this);
425                             this.variableWrapperCollection.Add(wrapper);
426                         }
427                         if (null != this.variableToSelect && this.variableToSelect == variable)
428                         {
429                             this.variableDataGrid.SelectedItem = wrapper;
430                             this.variableToSelect = null;
431
432                             int index = this.variableDataGrid.Items.IndexOf(this.variableDataGrid.SelectedItem);
433                             DataGridRow row = DataGridHelper.GetRow(this.variableDataGrid, index);
434                             if (!row.IsSelected)
435                             {
436                                 row.IsSelected = true;
437                             }
438
439                             if (this.continueScopeEdit)
440                             {
441                                 DataGridCell cell = DataGridHelper.GetCell(this.variableDataGrid, index, 2);
442                                 cell.IsEditing = true;
443                             }
444                         }
445                     }
446                     break;
447
448                 case NotifyCollectionChangedAction.Remove:
449                     foreach (ModelItem variable in e.OldItems)
450                     {
451                         DesignTimeVariable wrapper = this.variableWrapperCollection.FirstOrDefault(p => ModelItem.Equals(p.ReflectedObject, variable));
452                         if (null != wrapper)
453                         {
454                             //in case of undo/redo operation - just remove old reference to the wrapper, new one will be added be undo stack anyway
455                             if (!this.ScopesList.Contains((sender as ModelItem).Parent) || isUndoRedoInProgress)
456                             {
457                                 this.variableWrapperCollection.Remove(wrapper);
458                             }
459                         }
460                     }
461                     break;
462             }
463             this.RaiseEvent(new RoutedEventArgs(VariableDesigner.VariableCollectionChangedEvent, this));
464         }
465
466         void OnVariableWrapperCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
467         {
468             switch (e.Action)
469             {
470                 case NotifyCollectionChangedAction.Remove:
471                     bool isUndoRedoInProgress = this.Context.Services.GetService<UndoEngine>().IsUndoRedoInProgress;
472                     foreach (DesignTimeVariable arg in e.OldItems)
473                     {
474                         if (!isUndoRedoInProgress)
475                         {
476                             this.ClearCaseInsensitiveDuplicates(arg.GetVariableName(), (string)arg.ReflectedObject.Properties["Name"].ComputedValue);
477                             ModelItemCollection collection = (ModelItemCollection)arg.ReflectedObject.Parent;
478                             collection.Remove(arg.ReflectedObject);
479                         }
480                         arg.Dispose();
481                     }
482                     break;
483                 case NotifyCollectionChangedAction.Add:
484                     foreach (DesignTimeVariable var in e.NewItems)
485                     {
486                         this.CheckCaseInsensitiveDuplicates(var.GetVariableName(), (string)var.ReflectedObject.Properties["Name"].ComputedValue);
487                     }
488                     break;
489             }
490         }
491
492         void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
493         {
494             if (bool.Equals(true, e.NewValue))
495             {
496                 this.StoreLastSelection();
497                 this.OnVariableScopeChanged();
498             }
499             else if (this.variableDataGrid.SelectedItem is DesignTimeVariable)
500             {
501                 Selection newSelection = null == this.lastSelection ? new Selection() : new Selection(this.lastSelection);
502                 this.isSelectionSourceInternal = true;
503                 this.Context.Items.SetValue(newSelection);
504                 this.variableDataGrid.SelectedItem = null;
505                 this.isSelectionSourceInternal = false;
506             }
507         }
508
509         bool OnResolveDynamicContentTemplate(ResolveTemplateParams resolveParams)
510         {
511             var variable = (DesignObjectWrapper)resolveParams.Cell.DataContext;
512
513             //get editor associated with variable's value
514             var editorType = variable.GetDynamicPropertyValueEditorType(DesignTimeVariable.VariableDefaultProperty);
515
516             //if yes there is a custom one - use it
517             if (!typeof(ExpressionValueEditor).IsAssignableFrom(editorType))
518             {
519                 //get inline editor template - it will be used for both templates - view and editing; 
520                 resolveParams.Template = variable.GetDynamicPropertyValueEditor(DesignTimeVariable.VariableDefaultProperty).InlineEditorTemplate;
521                 resolveParams.IsDefaultTemplate = false;
522             }
523             else
524             {
525                 //no custom editor - depending on grid state display either editable or readonly expression template
526                 string key = resolveParams.Cell.IsEditing ? "variableExpressionEditableTemplate" : "variableExpressionReadonlyTemplate";
527                 resolveParams.Template = (DataTemplate)this.FindResource(key);
528                 resolveParams.IsDefaultTemplate = true;
529             }
530             return true;
531         }
532
533         DialogPropertyValueEditor OnLoadExtendedValueEditor(DataGridCell cell, object instance)
534         {
535             var variable = (DesignObjectWrapper)cell.DataContext;
536             return variable.GetDynamicPropertyValueEditor(DesignTimeVariable.VariableDefaultProperty) as DialogPropertyValueEditor;
537         }
538
539         ModelProperty OnShowExtendedValueEditor(DataGridCell cell, object instance)
540         {
541             var variable = (DesignObjectWrapper)cell.DataContext;
542             return variable.Content.Properties[DesignTimeVariable.VariableDefaultProperty];
543         }
544
545         static void OnContextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
546         {
547             ((VariableDesigner)sender).OnContextChanged();
548         }
549
550         static void OnVariableScopeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
551         {
552             VariableDesigner control = (VariableDesigner)sender;
553             if (!object.Equals(e.OldValue, e.NewValue))
554             {
555                 control.OnVariableScopeChanged();
556             }
557         }
558
559         void OnEditingControlLoaded(object sender, RoutedEventArgs args)
560         {
561             DataGridHelper.OnEditingControlLoaded(sender, args);
562         }
563
564         void OnEditingControlUnloaded(object sender, RoutedEventArgs args)
565         {
566             DataGridHelper.OnEditingControlUnloaded(sender, args);
567         }
568
569         // This is to workaround a bug that updating ModelItem from outside of VariableDesigner will not update VariableDesigner.
570         internal void NotifyAnnotationTextChanged()
571         {
572             foreach (object item in this.variableDataGrid.Items)
573             {
574                 DesignTimeVariable designTimeVariable = item as DesignTimeVariable;
575                 if (designTimeVariable != null)
576                 {
577                     designTimeVariable.NotifyPropertyChanged(DesignTimeVariable.AnnotationTextProperty);
578                 }
579             }
580         }
581
582         protected override void OnContextMenuOpening(ContextMenuEventArgs e)
583         {
584             base.OnContextMenuOpening(e);
585
586             DesignerConfigurationService configurationService = this.Context.Services.GetService<DesignerConfigurationService>();
587             Fx.Assert(configurationService != null, "DesignerConfigurationService should not be null");
588             if (configurationService.WorkflowDesignerHostId == WorkflowDesignerHostId.Dev10)
589             {
590                 return;
591             }
592             
593             e.Handled = true;
594
595             bool openedByKeyboard = e.CursorLeft < 0 && e.CursorTop < 0;
596
597             if (openedByKeyboard)
598             {
599                 this.ContextMenu.Placement = PlacementMode.Center;
600             }
601             else
602             {
603                 this.ContextMenu.Placement = PlacementMode.MousePoint;
604             }
605             this.ContextMenu.PlacementTarget = this;
606             this.ContextMenu.IsOpen = true;
607         }
608
609         private void OnDeleteCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
610         {
611             ContextMenuUtilities.OnDeleteCommandCanExecute(e, this.variableDataGrid);
612         }
613
614         private void OnDeleteCommandExecuted(object sender, ExecutedRoutedEventArgs e)
615         {
616             if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count > 0)
617             {
618                 List<ModelItem> list = new List<ModelItem>();
619                 foreach (object item in this.variableDataGrid.SelectedItems)
620                 {
621                     DesignTimeVariable designTimeVariable = item as DesignTimeVariable;
622                     if (designTimeVariable != null)
623                     {
624                         list.Add(designTimeVariable.ReflectedObject);
625                     }
626                 }
627
628                 foreach (ModelItem modelItem in list)
629                 {
630                     foreach (DesignTimeVariable designTimeVariable in this.variableWrapperCollection)
631                     {
632                         if (designTimeVariable.ReflectedObject == modelItem)
633                         {
634                             this.variableWrapperCollection.Remove(designTimeVariable);
635                             break;
636                         }
637                     }
638                 }
639             }
640
641             e.Handled = true;
642         }
643
644         private void OnAnnotationSeparatorLoaded(object sender, RoutedEventArgs e)
645         {
646             ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
647         }
648
649         private void OnAddAnnotationMenuLoaded(object sender, RoutedEventArgs e)
650         {
651             ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
652         }
653
654         private void OnAddAnnotationCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
655         {
656             ContextMenuUtilities.OnAddAnnotationCommandCanExecute(e, this.Context, this.variableDataGrid);
657         }
658
659         private void OnAddAnnotationCommandExecuted(object sender, ExecutedRoutedEventArgs e)
660         {
661             if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count == 1)
662             {
663                 AnnotationDialog dialog = new AnnotationDialog();
664                 dialog.Context = Context;
665                 dialog.Title = SR.AddAnnotationTitle;
666
667                 WindowHelperService service = this.Context.Services.GetService<WindowHelperService>();
668                 if (null != service)
669                 {
670                     service.TrySetWindowOwner(this, dialog);
671                 }
672                 dialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
673
674                 if (dialog.ShowDialog() == true)
675                 {
676                     string annotationText = dialog.AnnotationText;
677
678                     DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItems[0];
679                     variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].SetValue(annotationText);
680                 }
681             }
682
683             e.Handled = true;
684         }
685
686         private void OnEditAnnotationMenuLoaded(object sender, RoutedEventArgs e)
687         {
688             ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
689         }
690
691         private void OnEditAnnotationCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
692         {
693             // call the same method as delete annotation command
694             ContextMenuUtilities.OnDeleteAnnotationCommandCanExecute(e, this.Context, this.variableDataGrid);
695         }
696
697         private void OnEditAnnotationCommandExecuted(object sender, ExecutedRoutedEventArgs e)
698         {
699             if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count == 1)
700             {
701                 DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItems[0];
702
703                 AnnotationDialog dialog = new AnnotationDialog();
704                 dialog.Context = Context;
705                 dialog.Title = SR.EditAnnotationTitle;
706                 dialog.AnnotationText = variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].ComputedValue as string;
707
708                 WindowHelperService service = this.Context.Services.GetService<WindowHelperService>();
709                 if (null != service)
710                 {
711                     service.TrySetWindowOwner(this, dialog);
712                 }
713                 dialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
714
715                 if (dialog.ShowDialog() == true)
716                 {
717                     string annotationText = dialog.AnnotationText;
718
719                     variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].SetValue(annotationText);
720                 }
721             }
722         }
723
724         private void OnDeleteAnnotationMenuLoaded(object sender, RoutedEventArgs e)
725         {
726             ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
727         }
728
729         private void OnDeleteAnnotationCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
730         {
731             ContextMenuUtilities.OnDeleteAnnotationCommandCanExecute(e, this.Context, this.variableDataGrid);
732         }
733
734         private void OnDeleteAnnotationCommandExecuted(object sender, ExecutedRoutedEventArgs e)
735         {
736             if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count == 1)
737             {
738                 DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItems[0];
739                 variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].ClearValue();
740             }
741
742             e.Handled = true;
743         }
744     }
745
746     internal static class VariableHelper
747     {
748         static Type VariablesCollectionType = typeof(Collection<Variable>);
749         static Type CodeActivityType = typeof(CodeActivity);
750         static Type GenericCodeActivityType = typeof(CodeActivity<>);
751         static Type AsyncCodeActivityType = typeof(AsyncCodeActivity);
752         static Type GenericAsyncCodeActivityType = typeof(AsyncCodeActivity<>);
753
754         internal static ModelItemCollection GetVariableCollection(this ModelItem element)
755         {
756             if (null != element)
757             {
758                 Type elementType = element.ItemType;
759                 if (!((CodeActivityType.IsAssignableFrom(elementType)) || (GenericAsyncCodeActivityType.IsAssignableFrom(elementType)) ||
760                     (AsyncCodeActivityType.IsAssignableFrom(elementType)) || (GenericAsyncCodeActivityType.IsAssignableFrom(elementType))))
761                 {
762                     ModelProperty variablesProperty = element.Properties["Variables"];
763                     if ((variablesProperty != null) && (variablesProperty.PropertyType == VariablesCollectionType))
764                     {
765                         return variablesProperty.Collection;
766                     }
767                 }
768             }
769             return null;
770         }
771
772         internal static ModelItem GetVariableScopeElement(this ModelItem element)
773         {
774             while (null != element && null == VariableHelper.GetVariableCollection(element))
775             {
776                 element = element.Parent;
777             }
778             return element;
779         }
780
781         internal static List<ModelItem> FindDeclaredVariables(this ModelItem element, IList<ModelItem> scopeList)
782         {
783             var variables = VariableHelper.FindVariablesInScope(element, scopeList);
784             var contained = VariableHelper.GetVariableCollection(element);
785             if (null != contained)
786             {
787                 if (null != scopeList)
788                 {
789                     scopeList.Insert(0, element);
790                 }
791                 variables.InsertRange(0, contained.AsEnumerable());
792             }
793             return variables;
794         }
795
796         internal static List<ModelItem> FindDeclaredVariables(this ModelItem element)
797         {
798             return VariableHelper.FindDeclaredVariables(element, null);
799         }
800
801         internal static List<ModelItem> FindActivityDelegateArgumentsInScope(this ModelItem workflowElement)
802         {
803             List<ModelItem> variables = new List<ModelItem>();
804             if (workflowElement != null)
805             {
806                 workflowElement = workflowElement.Parent;
807                 while (null != workflowElement)
808                 {
809                     variables.AddRange(workflowElement.FindActivityDelegateArguments());
810                     workflowElement = workflowElement.Parent;
811                 }
812             }
813             return variables;
814         }
815
816         internal static List<ModelItem> FindActivityDelegateArguments(this ModelItem element)
817         {
818             List<ModelItem> delegateArguments = new List<ModelItem>();
819             if (element.GetCurrentValue() is ActivityDelegate)
820             {
821                 //browse all properties in given ActivityDelegate
822                 delegateArguments.AddRange(element.Properties
823                     //choose only those of base type equal to DelegateArgument
824                     .Where<ModelProperty>(p => (typeof(DelegateArgument).IsAssignableFrom(p.PropertyType) && null != p.Value))
825                     //from those, take actual ModelItem value
826                     .Select<ModelProperty, ModelItem>(p => p.Value));
827             }
828
829             return delegateArguments;
830         }
831
832         internal static List<ModelItem> FindVariablesInScope(this ModelItem element, IList<ModelItem> scopeList)
833         {
834             List<ModelItem> variables = new List<ModelItem>();
835             if (null != scopeList)
836             {
837                 scopeList.Clear();
838             }
839             if (null != element)
840             {
841                 element = element.Parent;
842                 while (element != null)
843                 {
844                     ModelItemCollection variablesInElement = VariableHelper.GetVariableCollection(element);
845                     if (null != variablesInElement)
846                     {
847                         if (null != scopeList)
848                         {
849                             scopeList.Add(element);
850                         }
851                         variables.AddRange(variablesInElement.AsEnumerable());
852                     }
853                     element = element.Parent;
854                 }
855             }
856             return variables;
857         }
858
859         internal static List<ModelItem> FindUniqueVariablesInScope(ModelItem element)
860         {
861             Dictionary<string, ModelItem> variables = new Dictionary<string, ModelItem>();
862             while (element != null)
863             {
864                 ModelItemCollection variablesInElement = VariableHelper.GetVariableCollection(element);
865                 if (null != variablesInElement)
866                 {
867                     foreach (ModelItem modelVariable in variablesInElement)
868                     {
869                         LocationReference locationReference = modelVariable.GetCurrentValue() as LocationReference;
870                         if (locationReference != null && !string.IsNullOrWhiteSpace(locationReference.Name) && !variables.ContainsKey(locationReference.Name))
871                         {
872                             variables.Add(locationReference.Name, modelVariable);
873                         }
874                     }
875                 }
876                 element = element.Parent;
877             }
878             return new List<ModelItem>(variables.Values);
879         }
880
881
882         internal static List<ModelItem> FindVariablesInScope(ModelItem element)
883         {
884             return VariableHelper.FindVariablesInScope(element, null);
885         }
886
887         internal static bool ContainsVariable(this ModelItemCollection variableContainer, string variableName)
888         {
889             if (null == variableContainer)
890             {
891                 throw FxTrace.Exception.AsError(new ArgumentNullException("variableContainer"));
892             }
893             if (!variableContainer.ItemType.IsGenericType ||
894                 variableContainer.ItemType.GetGenericArguments().Length != 1 ||
895                 !typeof(Variable).IsAssignableFrom(variableContainer.ItemType.GetGenericArguments()[0]))
896             {
897                 throw FxTrace.Exception.AsError(new ArgumentException("non variable collection"));
898             }
899
900             return variableContainer.Any(p => string.Equals(p.Properties[DesignTimeVariable.VariableNameProperty].ComputedValue, variableName));
901         }
902
903         internal static string CreateUniqueVariableName(this ModelItemCollection variableContainer, string namePrefix, int countStartValue)
904         {
905             if (null == variableContainer)
906             {
907                 throw FxTrace.Exception.AsError(new ArgumentNullException("variableContainer"));
908             }
909             if (!variableContainer.ItemType.IsGenericType ||
910                 variableContainer.ItemType.GetGenericArguments().Length != 1 ||
911                 !typeof(Variable).IsAssignableFrom(variableContainer.ItemType.GetGenericArguments()[0]))
912             {
913                 throw FxTrace.Exception.AsError(new ArgumentException("non variable collection"));
914             }
915
916             string name = string.Empty;
917
918             //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 
919             //the root. we don't check unique check below current scope - it would be too expensive to ---- whole tree
920             var variables = VariableHelper.FindVariablesInScope(variableContainer);
921             while (true)
922             {
923                 name = string.Format(CultureInfo.CurrentUICulture, "{0}{1}", namePrefix, countStartValue++);
924                 if (!variables.Any(p => string.Equals(p.Properties[DesignTimeVariable.VariableNameProperty].ComputedValue, name)))
925                 {
926                     break;
927                 }
928             }
929
930             return name;
931         }
932
933         internal static ModelItem FindRootVariableScope(ModelItem element)
934         {
935             if (null == element)
936             {
937                 throw FxTrace.Exception.ArgumentNull("element");
938             }
939             ModelItem result = element.GetParentEnumerator().Where(p => null != VariableHelper.GetVariableCollection(p)).LastOrDefault();
940             return result;
941         }
942
943         internal static ModelItem FindCommonVariableScope(ModelItem scope1, ModelItem scope2)
944         {
945             if (null == scope1 || null == scope2)
946             {
947                 throw FxTrace.Exception.AsError(new ArgumentNullException(null == scope1 ? "scope1" : "scope2"));
948             }
949
950             var scope1List = scope1.GetParentEnumerator().Where(p => null != VariableHelper.GetVariableCollection(p)).ToList();
951             var scope2List = scope2.GetParentEnumerator().Where(p => null != VariableHelper.GetVariableCollection(p)).ToList();
952
953             if (null != VariableHelper.GetVariableCollection(scope1))
954             {
955                 scope1List.Insert(0, scope1);
956             }
957             if (null != VariableHelper.GetVariableCollection(scope2))
958             {
959                 scope2List.Insert(0, scope2);
960             }
961
962             if (scope1 == scope2)
963             {
964                 return scope1List.FirstOrDefault();
965             }
966
967             return scope1List.Intersect(scope2List).FirstOrDefault();
968         }
969     }
970
971     sealed class DesignTimeVariable : DesignObjectWrapper
972     {
973         internal static readonly string VariableNameProperty = "Name";
974         internal static readonly string VariableTypeProperty = "Type";
975         internal static readonly string VariableScopeProperty = "Scope";
976         internal static readonly string VariableDefaultProperty = "Default";
977         internal static readonly string ToolTipProperty = "ToolTip";
978         internal static readonly string VariableScopeLevelProperty = "ScopeLevel";
979         internal static readonly string VariableModifiersProperty = "Modifiers";
980         internal static readonly string AnnotationTextProperty = "DesignTimeVariableAnnotationText";
981         static readonly string[] Properties =
982             new string[] { VariableNameProperty, VariableTypeProperty, VariableScopeProperty, VariableDefaultProperty, 
983                            ToolTipProperty, VariableScopeLevelProperty, VariableModifiersProperty };
984
985         bool variableTypeChanged = false;
986
987         internal VariableDesigner Editor
988         {
989             get;
990             private set;
991         }
992
993         VBIdentifierName identifierName;
994
995         #region Initialize type properties code
996         public static PropertyDescriptorData[] InitializeTypeProperties()
997         {
998             return new PropertyDescriptorData[]
999             {
1000                 new PropertyDescriptorData()
1001                 {
1002                     PropertyName = VariableNameProperty,
1003                     PropertyType = typeof(VBIdentifierName),
1004                     PropertyAttributes = TypeDescriptor.GetAttributes(typeof(VBIdentifierName)).OfType<Attribute>().ToArray(),
1005                     PropertySetter = (instance, newValue) =>
1006                         {
1007                             ((DesignTimeVariable)instance).SetVariableName((VBIdentifierName)newValue);
1008                         },
1009                     PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableName()),
1010                     PropertyValidator  = (instance, value, errors) => (((DesignTimeVariable)instance).ValidateVariableName(value, errors))
1011                 },
1012                 new PropertyDescriptorData()
1013                 {
1014                     PropertyName = VariableTypeProperty,
1015                     PropertyType = typeof(Type),
1016                     PropertyAttributes = TypeDescriptor.GetAttributes(typeof(Type)).OfType<Attribute>().ToArray(),
1017                     PropertySetter = (instance, newValue) =>
1018                         {
1019                             ((DesignTimeVariable)instance).SetVariableType((Type)newValue);
1020                         },
1021                     PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableType()),
1022                     PropertyValidator = null
1023                 },
1024                 new PropertyDescriptorData()
1025                 {
1026                     PropertyName = VariableScopeProperty,
1027                     PropertyType = typeof(ModelItem),
1028                     PropertyAttributes = TypeDescriptor.GetAttributes(typeof(ModelItem)).OfType<Attribute>().Union(new Attribute[] { new EditorAttribute(typeof(ScopeValueEditor), typeof(PropertyValueEditor)) }).ToArray(),
1029                     PropertySetter = (instance, newValue) =>
1030                         {
1031                             ((DesignTimeVariable)instance).SetVariableScope(newValue);
1032                         },
1033                     PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableScope()),
1034                     PropertyValidator  = (instance, value, errors) => (((DesignTimeVariable)instance).ValidateVariableScope(value, errors))
1035                 },
1036                 new PropertyDescriptorData()
1037                 {
1038                     PropertyName = VariableDefaultProperty,
1039                     PropertyType = typeof(Activity),
1040                     PropertyAttributes = TypeDescriptor.GetAttributes(typeof(Activity)).OfType<Attribute>().Union(new Attribute[] { new EditorAttribute(typeof(DesignObjectWrapperDynamicPropertyEditor), typeof(DialogPropertyValueEditor)), new EditorReuseAttribute(false) }).ToArray(),
1041                     PropertySetter = (instance, newValue) =>
1042                         {
1043                             ((DesignTimeVariable)instance).SetVariableValue(newValue);
1044                         },
1045                     PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableValue()),
1046                     PropertyValidator  = null,
1047                 },
1048                 new PropertyDescriptorData()
1049                 {
1050                     PropertyName = ToolTipProperty,
1051                     PropertyType = typeof(string),
1052                     PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
1053                     PropertySetter = null,
1054                     PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetToolTip()),
1055                     PropertyValidator = null
1056                 },
1057                 new PropertyDescriptorData()
1058                 {
1059                     PropertyName = VariableScopeLevelProperty,
1060                     PropertyType = typeof(int),
1061                     PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
1062                     PropertySetter = null,
1063                     PropertyValidator = null,
1064                     PropertyGetter = (instance) =>
1065                         (
1066                             ((DesignTimeVariable)instance).GetScopeLevel()
1067                         ),
1068                 },
1069                 new PropertyDescriptorData()
1070                 {
1071                     PropertyName = VariableModifiersProperty,
1072                     PropertyType = typeof(VariableModifiers), 
1073                     PropertyAttributes = TypeDescriptor.GetAttributes(typeof(VariableModifiers)).OfType<Attribute>().ToArray(), 
1074                     PropertySetter = (instance, newValue) => 
1075                         {
1076                             ((DesignTimeVariable)instance).SetVariableModifiers(newValue);
1077                         },
1078                     PropertyValidator = null,
1079                     PropertyGetter = (instance) => 
1080                         {
1081                             return ((DesignTimeVariable)instance).GetVariableModifiers();
1082                         }
1083                 },
1084                 new PropertyDescriptorData()
1085                 {
1086                     PropertyName = AnnotationTextProperty,
1087                     PropertyType = typeof(string),
1088                     PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
1089                     PropertySetter = (instance, newValue) =>
1090                         {
1091                             ((DesignTimeVariable)instance).SetAnnotationText(newValue);
1092                         },
1093                     PropertyValidator = null,
1094                     PropertyGetter = (instance) =>
1095                         {
1096                             return ((DesignTimeVariable)instance).GetAnnotationText();
1097                         }
1098                 }
1099             };
1100         }
1101         #endregion
1102
1103         public DesignTimeVariable()
1104         {
1105             throw FxTrace.Exception.AsError(new NotSupportedException(SR.InvalidConstructorCall));
1106         }
1107
1108         internal DesignTimeVariable(ModelItem modelItem, VariableDesigner editor)
1109             : base(modelItem)
1110         {
1111             this.Editor = editor;
1112             this.identifierName = new VBIdentifierName
1113             {
1114                 IdentifierName = (string)modelItem.Properties[VariableNameProperty].ComputedValue
1115             };
1116         }
1117
1118         protected override string AutomationId
1119         {
1120             get { return this.GetVariableNameString(); }
1121         }
1122
1123         void SetVariableName(VBIdentifierName identifierName)
1124         {
1125             using (ModelEditingScope change = this.ReflectedObject.BeginEdit((string)this.Editor.FindResource("changeVariableNameDescription")))
1126             {
1127                 this.identifierName = identifierName;
1128                 string name = this.identifierName.IdentifierName;
1129                 this.Editor.NotifyVariableNameChanged(this.identifierName, name, (string)this.ReflectedObject.Properties[VariableNameProperty].ComputedValue);
1130                 this.ReflectedObject.Properties[VariableNameProperty].SetValue(name);
1131
1132                 change.Complete();
1133             }
1134         }
1135
1136         internal VBIdentifierName GetVariableName()
1137         {
1138             return this.identifierName;
1139         }
1140
1141         string GetVariableNameString()
1142         {
1143             return (string)this.ReflectedObject.Properties[VariableNameProperty].ComputedValue;
1144         }
1145
1146         protected override void OnReflectedObjectPropertyChanged(string propertyName)
1147         {
1148             if (propertyName == Annotation.AnnotationTextPropertyName)
1149             {
1150                 RaisePropertyChangedEvent(AnnotationTextProperty);
1151             }
1152
1153             if (propertyName == VariableNameProperty)
1154             {
1155                 string oldValue = this.identifierName.IdentifierName;
1156                 string newValue = GetVariableNameString();
1157
1158                 //This is invoked in undo stack
1159                 if (oldValue != newValue)
1160                 {
1161                     this.identifierName = new VBIdentifierName
1162                     {
1163                         IdentifierName = newValue
1164                     };
1165                     Editor.NotifyVariableNameChanged(this.identifierName, newValue, oldValue);
1166                 }
1167             }
1168         }
1169
1170         void SetVariableModifiers(object modifiers)
1171         {
1172             this.ReflectedObject.Properties[VariableModifiersProperty].SetValue(
1173                 modifiers is ModelItem ? (modifiers as ModelItem).GetCurrentValue() : modifiers);
1174         }
1175
1176         object GetVariableModifiers()
1177         {
1178             return this.ReflectedObject.Properties[VariableModifiersProperty].ComputedValue;
1179         }
1180
1181         int GetScopeLevel()
1182         {
1183             int level = 0;
1184             ModelItem parent = this.ReflectedObject.Parent;
1185             while (null != parent)
1186             {
1187                 ++level;
1188                 parent = parent.Parent;
1189             }
1190             return level;
1191         }
1192
1193         // Used by screen reader to read the DataGrid row.
1194         public override string ToString()
1195         {
1196             string name = this.GetVariableNameString();
1197             if (!string.IsNullOrEmpty(name))
1198             {
1199                 return name;
1200             }
1201             return "Variable";
1202         }
1203
1204         void SetVariableType(Type type)
1205         {
1206             if (!Type.Equals(type, this.GetVariableType()))
1207             {
1208                 using (ModelEditingScope change = this.ReflectedObject.BeginEdit((string)this.Editor.FindResource("changeVariableTypeDescription")))
1209                 {
1210                     this.variableTypeChanged = true;
1211                     ModelItemCollection variableContainer = (ModelItemCollection)this.ReflectedObject.Parent;
1212                     //proceed only if variable is associated with container
1213                     if (null != variableContainer)
1214                     {
1215                         Variable variable = Variable.Create(this.GetVariableNameString(), type, (VariableModifiers)this.GetVariableModifiers());
1216                         string annotationText = this.GetAnnotationText() as string;
1217                         if (annotationText != null)
1218                         {
1219                             Annotation.SetAnnotationText(variable, annotationText);
1220                         }
1221
1222                         //try to preserve expression
1223                         ModelItem expressionModelItem = this.ReflectedObject.Properties[VariableDefaultProperty].Value;
1224                         if (expressionModelItem != null)
1225                         {
1226                             ActivityWithResult expression = expressionModelItem.GetCurrentValue() as ActivityWithResult;
1227                             //check if there existed expression
1228                             if (expression != null)
1229                             {
1230                                 ActivityWithResult morphedExpression = null;
1231                                 if (ExpressionHelper.TryMorphExpression(expression, false, type, this.Context, out morphedExpression))
1232                                 {
1233                                     variable.Default = morphedExpression;
1234                                 }
1235                                 //[....] 
1236
1237                             }
1238                         }
1239                         Editor.ChangeVariableType(this, variable);
1240                         ImportDesigner.AddImport(type.Namespace, this.Context);
1241                         change.Complete();
1242                     }
1243                 }
1244             }
1245         }
1246
1247         Type GetVariableType()
1248         {
1249             return (Type)this.ReflectedObject.Properties[VariableTypeProperty].ComputedValue;
1250         }
1251
1252         void SetVariableScope(object newScope)
1253         {
1254             using (ModelEditingScope change = this.ReflectedObject.BeginEdit((string)this.Editor.FindResource("changeVariableScopeDescription")))
1255             {
1256                 if (!ModelItem.Equals(newScope, this.GetVariableScope()))
1257                 {
1258                     ModelItemCollection currentScopeContainer = this.ReflectedObject.Parent.Parent.Properties["Variables"].Collection;
1259                     currentScopeContainer.Remove(this.ReflectedObject.GetCurrentValue());
1260                     ModelItem scope = (newScope as ModelItem) ?? Editor.ScopesList.FirstOrDefault(p => object.Equals(p.GetCurrentValue(), newScope));
1261                     ModelItemCollection newScopeContainer = scope.Properties["Variables"].Collection;
1262                     newScopeContainer.Add(this.ReflectedObject.GetCurrentValue());
1263                     Editor.NotifyVariableScopeChanged(this);
1264                 }
1265                 change.Complete();
1266             }
1267         }
1268
1269         object GetVariableScope()
1270         {
1271             return this.ReflectedObject.Parent.Parent;
1272         }
1273
1274         void SetVariableValue(object value)
1275         {
1276             object expression = value is ModelItem ? ((ModelItem)value).GetCurrentValue() : value;
1277             this.ReflectedObject.Properties[VariableDefaultProperty].SetValue(expression);
1278         }
1279
1280         object GetVariableValue()
1281         {
1282             return this.ReflectedObject.Properties[VariableDefaultProperty].ComputedValue;
1283         }
1284
1285         string GetToolTip()
1286         {
1287             ModelItem s = this.ReflectedObject.Parent.Parent;
1288             IMultiValueConverter converter = (IMultiValueConverter)(this.Editor.FindResource("scopeToNameConverter"));
1289             return ScopeToTooltipConverter.BuildToolTip(s, converter, CultureInfo.CurrentCulture);
1290         }
1291
1292         object GetAnnotationText()
1293         {
1294             ModelProperty property = this.ReflectedObject.Properties.Find(Annotation.AnnotationTextPropertyName);
1295
1296             if (property != null)
1297             {
1298                 return property.ComputedValue;
1299             }
1300             else
1301             {
1302                 return null;
1303             }
1304         }
1305
1306         void SetAnnotationText(object annotationText)
1307         {
1308             ModelProperty property = this.ReflectedObject.Properties.Find(Annotation.AnnotationTextPropertyName);
1309
1310             if (property != null)
1311             {
1312                 property.SetValue(annotationText);
1313             }
1314         }
1315
1316         protected override Type OnGetDynamicPropertyValueEditorType(string propertyName)
1317         {
1318             var type = this.GetVariableType();
1319
1320             //in case of variables which contain handles - display HandleValueEditor
1321             if (typeof(Handle).IsAssignableFrom(type))
1322             {
1323                 return typeof(HandleValueEditor);
1324             }
1325
1326             var referenceType = typeof(PropertyValueEditor);
1327             var expressionEditorType = typeof(ExpressionValueEditor);
1328
1329             //check if there are custom editors on the variable's type
1330             var variableOfType = typeof(Variable<>).MakeGenericType(type);
1331
1332             //check if there are custom type editors associated with given type - 
1333             //look for type editor defined for Variable<T>
1334             //in search, skip ExpressionValueEditor instance - it will be returned by default for property grid, but for
1335             //dataGrid nothing should be used - we use default dg template
1336             var customEditorType = TypeDescriptor
1337                 .GetAttributes(variableOfType)
1338                 .OfType<EditorAttribute>()
1339                 .Where(p =>
1340                     {
1341                         Type currentType = Type.GetType(p.EditorTypeName);
1342                         return (expressionEditorType != currentType && referenceType.IsAssignableFrom(currentType));
1343                     })
1344                 .Select(p => Type.GetType(p.EditorTypeName))
1345                 .FirstOrDefault();
1346
1347
1348             //return custom editor type (if any)
1349             if (null != customEditorType)
1350             {
1351                 return customEditorType;
1352             }
1353
1354             //otherwise - return default expression value editor
1355             return typeof(ExpressionValueEditor);
1356         }
1357
1358         protected override void OnPropertyChanged(string propertyName)
1359         {
1360             //this method is called by the thread's dispatcher AFTER all prorties have been updated, so all the property values
1361             //are updated, regardless of the editing scope deep
1362             if (string.Equals(propertyName, DesignTimeVariable.VariableScopeProperty))
1363             {
1364                 this.RaisePropertyChangedEvent(ToolTipProperty);
1365                 this.RaisePropertyChangedEvent(VariableScopeLevelProperty);
1366             }
1367             else if (string.Equals(propertyName, DesignTimeVariable.VariableNameProperty))
1368             {
1369                 this.RaisePropertyChangedEvent(AutomationIdProperty);
1370             }
1371             else if (string.Equals(propertyName, DesignTimeVariable.VariableTypeProperty))
1372             {
1373                 this.RaisePropertyChangedEvent(VariableDefaultProperty);
1374             }
1375             else if (string.Equals(propertyName, DesignTimeVariable.TimestampProperty))
1376             {
1377                 if (this.variableTypeChanged)
1378                 {
1379                     this.RaisePropertyChangedEvent(DesignTimeVariable.VariableTypeProperty);
1380                     this.variableTypeChanged = false;
1381                     this.CustomValueEditors.Clear();
1382                     this.Editor.UpdateTypeDesigner(this);
1383                 }
1384             }
1385             base.OnPropertyChanged(propertyName);
1386         }
1387
1388         bool ValidateVariableName(object newValue, List<string> errors)
1389         {
1390             if (!base.IsUndoRedoInProgress && null != this.ReflectedObject.Parent)
1391             {
1392                 VBIdentifierName identifier = newValue as VBIdentifierName;
1393
1394                 string newName = null;
1395                 if (identifier != null)
1396                 {
1397                     newName = identifier.IdentifierName;
1398                 }
1399
1400
1401                 if (!string.IsNullOrEmpty(newName))
1402                 {
1403                     Func<ModelItem, bool> checkForDuplicates =
1404                         new Func<ModelItem, bool>(p => string.Equals(p.Properties[VariableNameProperty].ComputedValue, newName) && !object.Equals(p, this.ReflectedObject));
1405
1406                     bool duplicates = this.ReflectedObject.Parent.Parent.Properties["Variables"].Collection.Any(checkForDuplicates);
1407
1408                     if (duplicates)
1409                     {
1410                         errors.Add(string.Format(CultureInfo.CurrentUICulture, SR.DuplicateVariableName, newName));
1411                     }
1412                 }
1413                 else
1414                 {
1415                     errors.Add(SR.EmptyVariableName);
1416                 }
1417             }
1418             return 0 == errors.Count;
1419         }
1420
1421         bool ValidateVariableScope(object newValue, List<string> errors)
1422         {
1423             if (!base.IsUndoRedoInProgress)
1424             {
1425                 ModelItem scope = (newValue as ModelItem) ?? Editor.ScopesList.FirstOrDefault(p => object.Equals(p.GetCurrentValue(), newValue));
1426                 string currentName = this.GetVariableNameString();
1427
1428                 Func<ModelItem, bool> checkForDuplicates =
1429                     new Func<ModelItem, bool>(p => string.Equals(p.Properties[VariableNameProperty].ComputedValue, currentName) && !object.Equals(p, this.ReflectedObject));
1430
1431                 bool duplicates = scope.Properties["Variables"].Collection.Any(checkForDuplicates);
1432                 if (duplicates)
1433                 {
1434                     errors.Add(string.Format(CultureInfo.CurrentUICulture, SR.DuplicateVariableName, currentName));
1435                 }
1436             }
1437             return 0 == errors.Count;
1438         }
1439
1440         #region Internal classes
1441         internal sealed class ScopeValueEditor : PropertyValueEditor
1442         {
1443             public ScopeValueEditor()
1444             {
1445                 this.InlineEditorTemplate = EditorResources.GetResources()["ScopeEditor_InlineEditorTemplate"] as DataTemplate;
1446             }
1447         }
1448
1449         #endregion
1450     }
1451
1452     sealed class DesignTimeVariableToScopeConverter : IValueConverter
1453     {
1454         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
1455         {
1456             ModelItem designTimeVariable = value as ModelItem;
1457             object result = null;
1458             if (null != designTimeVariable && typeof(DesignTimeVariable).IsAssignableFrom(designTimeVariable.ItemType))
1459             {
1460                 DesignTimeVariable variable = (DesignTimeVariable)designTimeVariable.GetCurrentValue();
1461                 result = variable.Editor.ScopesList;
1462             }
1463             return result;
1464         }
1465
1466         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
1467         {
1468             throw FxTrace.Exception.AsError(new NotSupportedException());
1469         }
1470     }
1471
1472     sealed class ScopeToTooltipConverter : IValueConverter
1473     {
1474         IMultiValueConverter baseConverter = new BreadCrumbTextConverter();
1475
1476         internal static string BuildToolTip(ModelItem entry, IMultiValueConverter displayNameConverter, CultureInfo culture)
1477         {
1478             string result = null;
1479             if (null != entry && null != displayNameConverter)
1480             {
1481                 StringBuilder sb = new StringBuilder();
1482                 int indent = 0;
1483                 ModelItem currentEntry = entry;
1484                 while (currentEntry != null)
1485                 {
1486                     if (null != currentEntry.Properties["Variables"])
1487                     {
1488                         ++indent;
1489                     }
1490                     currentEntry = currentEntry.Parent;
1491                 }
1492
1493                 while (entry != null)
1494                 {
1495                     if (null != entry.Properties["Variables"])
1496                     {
1497                         if (sb.Length != 0)
1498                         {
1499                             sb.Insert(0, "/");
1500                             sb.Insert(0, " ", --indent);
1501                             sb.Insert(0, Environment.NewLine);
1502                         }
1503                         var input = new object[] { entry, null != entry.Properties["DisplayName"] ? entry.Properties["DisplayName"].Value : null, (double)short.MaxValue };
1504                         sb.Insert(0, displayNameConverter.Convert(input, typeof(string), null, culture));
1505                     }
1506                     entry = entry.Parent;
1507                 }
1508                 result = sb.ToString();
1509             }
1510             return result;
1511         }
1512
1513         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
1514         {
1515             return BuildToolTip(value as ModelItem, this.baseConverter, culture);
1516         }
1517
1518         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
1519         {
1520             throw FxTrace.Exception.AsError(new NotSupportedException());
1521         }
1522     }
1523
1524     sealed class ScopeComboBox : ComboBox
1525     {
1526         bool isScopeValid = true;
1527
1528         protected override void OnInitialized(EventArgs e)
1529         {
1530             base.OnInitialized(e);
1531             this.Loaded += (s, args) =>
1532                 {
1533                     //get the binding expression, and hook up exception filter
1534                     var expr = this.GetBindingExpression(ScopeComboBox.SelectedItemProperty);
1535                     if (null != expr && null != expr.ParentBinding)
1536                     {
1537                         expr.ParentBinding.UpdateSourceExceptionFilter = this.OnUpdateBindingException;
1538                     }
1539                 };
1540         }
1541
1542         object OnUpdateBindingException(object sender, Exception err)
1543         {
1544             //if exception occured, the scope as invalid
1545             if (err is TargetInvocationException && err.InnerException is ValidationException || err is ValidationException)
1546             {
1547                 this.isScopeValid = false;
1548             }
1549             return null;
1550         }
1551
1552         protected override void OnSelectionChanged(SelectionChangedEventArgs e)
1553         {
1554             //if validation succeeded - update the control state with new selection
1555             if (this.isScopeValid)
1556             {
1557                 base.OnSelectionChanged(e);
1558             }
1559             //otherwise, get the binding expression and update control with current state from the source
1560             else
1561             {
1562                 var expr = this.GetBindingExpression(ScopeComboBox.SelectedItemProperty);
1563                 if (null != expr)
1564                 {
1565                     expr.UpdateTarget();
1566                 }
1567                 //the next failed validation pass may set this flag to false, but if validation succeeds, it has to be set to true
1568                 this.isScopeValid = true;
1569             }
1570         }
1571     }
1572 }