1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
5 namespace System.Activities.Presentation.View
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;
19 using System.Reflection;
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;
29 partial class VariableDesigner
31 public static readonly DependencyProperty ContextProperty = DependencyProperty.Register(
33 typeof(EditingContext),
34 typeof(VariableDesigner),
35 new FrameworkPropertyMetadata(null, new PropertyChangedCallback(VariableDesigner.OnContextChanged)));
37 static readonly DependencyPropertyKey CurrentVariableScopePropertyKey = DependencyProperty.RegisterReadOnly(
38 "CurrentVariableScope",
39 typeof(ModelItemCollection),
40 typeof(VariableDesigner),
41 new UIPropertyMetadata(null, new PropertyChangedCallback(OnVariableScopeChanged)));
43 public static readonly DependencyProperty CurrentVariableScopeProperty = CurrentVariableScopePropertyKey.DependencyProperty;
45 public static readonly RoutedEvent VariableCollectionChangedEvent =
46 EventManager.RegisterRoutedEvent("VariableCollectionChanged",
47 RoutingStrategy.Bubble,
48 typeof(RoutedEventHandler),
49 typeof(VariableDesigner));
51 const string DefaultVariableName = "variable";
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;
60 DataGridHelper dgHelper;
62 public VariableDesigner()
64 InitializeComponent();
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;
75 this.variableDataGrid.SelectionChanged += OnDataGridRowSelected;
76 this.variableWrapperCollection.CollectionChanged += OnVariableWrapperCollectionChanged;
77 this.variableDataGrid.ItemsSource = this.variableWrapperCollection;
79 var converter = (BreadCrumbTextConverter)this.FindResource("scopeToNameConverter");
80 converter.PixelsPerChar = (this.FontSize - 5);
83 public event RoutedEventHandler VariableCollectionChanged
87 AddHandler(VariableCollectionChangedEvent, value);
91 RemoveHandler(VariableCollectionChangedEvent, value);
95 public EditingContext Context
97 get { return (EditingContext)GetValue(ContextProperty); }
98 set { SetValue(ContextProperty, value); }
101 public ModelItemCollection CurrentVariableScope
103 get { return (ModelItemCollection)GetValue(CurrentVariableScopeProperty); }
104 private set { SetValue(CurrentVariableScopePropertyKey, value); }
107 public List<ModelItem> ScopesList
109 get { return this.scopesList; }
112 public bool CreateNewVariableWrapper()
115 ModelItemCollection scope = this.GetDefaultVariableScope();
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")))
122 ModelItem wrappedVariable = scope.Add(variable);
123 wrapperObject = new DesignTimeVariable(wrappedVariable, this);
124 this.variableWrapperCollection.Add(wrapperObject);
128 this.dgHelper.BeginRowEdit(wrapperObject);
133 internal void ChangeVariableType(DesignTimeVariable oldVariableWrapper, Variable newVariable)
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;
140 //get location of the variable
141 ModelItemCollection container = VariableHelper.GetVariableCollection(oldVariableWrapper.ReflectedObject.Parent.Parent);
142 index = container.IndexOf(oldVariableWrapper.ReflectedObject);
144 container.RemoveAt(index);
145 oldVariableWrapper.Dispose();
146 oldVariableWrapper.Initialize(container.Insert(index, newVariable));
149 internal void NotifyVariableScopeChanged(DesignTimeVariable variable)
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;
157 //Check case-insensitive duplicates, which are not allowed in VB expressions
158 internal void CheckCaseInsensitiveDuplicates(VBIdentifierName identifierName, string newName)
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)
164 identifierName.IsValid = false;
165 identifierName.ErrorMessage = string.Format(CultureInfo.CurrentUICulture, SR.DuplicateIdentifier, newName);
166 VBIdentifierName duplicateIdentifier = duplicate.GetVariableName();
167 if (duplicateIdentifier.IsValid)
169 duplicateIdentifier.IsValid = false;
170 duplicateIdentifier.ErrorMessage = string.Format(CultureInfo.CurrentUICulture, SR.DuplicateIdentifier, duplicateIdentifier.IdentifierName);
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)
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)
183 DesignTimeVariable wrapper = oldDuplicates.First<DesignTimeVariable>();
184 VBIdentifierName oldDuplicate = wrapper.GetVariableName();
185 oldDuplicate.IsValid = true;
186 oldDuplicate.ErrorMessage = string.Empty;
190 internal void NotifyVariableNameChanged(VBIdentifierName identifierName, string newName, string oldName)
192 //Check whether there're any variables' name conflict with the old name which can be cleaned up now
193 this.ClearCaseInsensitiveDuplicates(identifierName, oldName);
195 //Check whether there're any duplicates with new name
196 this.CheckCaseInsensitiveDuplicates(identifierName, newName);
199 internal void UpdateTypeDesigner(DesignTimeVariable variable)
201 this.dgHelper.UpdateDynamicContentColumns(variable);
204 string GetDefaultName()
206 return this.variableWrapperCollection.GetUniqueName<DesignTimeVariable>(VariableDesigner.DefaultVariableName, wrapper => wrapper.GetVariableName().IdentifierName);
209 Type GetDefaultType()
211 return typeof(string);
214 ModelItemCollection GetDefaultVariableScope()
216 //do a lazdy scope refresh
217 //if we have a valid variable scope
218 if (null != this.CurrentVariableScope)
220 //get the tree manager
221 var treeManager = this.Context.Services.GetService<ModelTreeManager>();
222 if (null != treeManager)
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))
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();
238 //return validated variable scope
239 return this.CurrentVariableScope;
242 void Populate(ModelItem workflowElement)
244 this.scopesList.ForEach(p =>
246 p.Properties["Variables"].Collection.CollectionChanged -= OnVariableCollectionChanged;
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)
257 allVariables.ForEach(variable =>
259 this.variableWrapperCollection.Add(new DesignTimeVariable(variable, this));
262 this.variableDataGrid.ItemsSource = this.variableWrapperCollection;
264 this.scopesList.ForEach(p =>
266 p.Properties["Variables"].Collection.CollectionChanged += OnVariableCollectionChanged;
270 void StoreLastSelection()
272 if (!this.isSelectionSourceInternal)
274 ModelItem current = this.Context.Items.GetValue<Selection>().PrimarySelection;
275 if (null == current || !typeof(DesignTimeVariable).IsAssignableFrom(current.ItemType))
277 this.lastSelection = current;
282 internal void SelectVariable(ModelItem variable)
284 this.Dispatcher.BeginInvoke(new Action(() =>
286 foreach (object item in this.variableDataGrid.Items)
288 if (item is DesignTimeVariable)
290 if (object.ReferenceEquals(((DesignTimeVariable)item).ReflectedObject, variable))
292 this.variableDataGrid.SelectedItem = item;
293 this.variableDataGrid.ScrollIntoView(item, null);
297 }), DispatcherPriority.ApplicationIdle);
301 void OnDataGridRowSelected(object sender, RoutedEventArgs e)
303 if (null != this.Context && !this.isSelectionSourceInternal)
305 this.isSelectionSourceInternal = true;
306 if (null != this.variableDataGrid.SelectedItem && this.variableDataGrid.SelectedItem is DesignTimeVariable)
308 DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItem;
309 this.Context.Items.SetValue(new Selection(variable.Content));
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)
318 foreach (ModelItem item in oldSelection.SelectedObjects)
320 if (item.ItemType != typeof(DesignTimeVariable))
322 newSelection.Add(item);
326 this.Context.Items.SetValue(new Selection(newSelection));
328 this.isSelectionSourceInternal = false;
332 void OnContextChanged()
334 if (null != this.Context)
336 this.Context.Items.Subscribe<Selection>(new SubscribeContextCallback<Selection>(OnItemSelected));
338 this.dgHelper.Context = this.Context;
341 void OnItemSelected(Selection newSelection)
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)
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)
351 //delegate call to update selection after update is completed
352 this.Dispatcher.BeginInvoke(new Action(() =>
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);
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);
369 void OnItemSelectedCore(ModelItem primarySelection)
371 //update variable scope - but ignore selection changes made by selecting variables.
372 if (null == primarySelection || !primarySelection.IsAssignableFrom<DesignTimeVariable>())
374 ModelItem element = VariableHelper.GetVariableScopeElement(primarySelection);
375 ModelItemCollection newVariableScope = VariableHelper.GetVariableCollection(element);
376 if (this.CurrentVariableScope != newVariableScope)
378 this.CurrentVariableScope = newVariableScope;
382 bool selectedVariableIsInSelection = false;
383 Selection selection = this.Context.Items.GetValue<Selection>();
385 DesignTimeVariable selectedVariable = this.variableDataGrid.SelectedItem as DesignTimeVariable;
386 if (selectedVariable != null)
388 foreach (ModelItem item in selection.SelectedObjects)
390 if (object.ReferenceEquals(selectedVariable, item.GetCurrentValue()))
392 selectedVariableIsInSelection = true;
397 if (!selectedVariableIsInSelection)
399 this.variableDataGrid.SelectedItem = null;
405 void OnVariableScopeChanged()
407 this.Populate(null != this.CurrentVariableScope ? this.CurrentVariableScope.Parent : null);
410 void OnVariableCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
412 var isUndoRedoInProgress = this.Context.Services.GetService<UndoEngine>().IsUndoRedoInProgress;
416 case NotifyCollectionChangedAction.Add:
417 foreach (ModelItem variable in e.NewItems)
419 DesignTimeVariable wrapper = this.variableWrapperCollection
420 .FirstOrDefault<DesignTimeVariable>(p => (ModelItem.Equals(p.ReflectedObject, variable)));
424 wrapper = new DesignTimeVariable(variable, this);
425 this.variableWrapperCollection.Add(wrapper);
427 if (null != this.variableToSelect && this.variableToSelect == variable)
429 this.variableDataGrid.SelectedItem = wrapper;
430 this.variableToSelect = null;
432 int index = this.variableDataGrid.Items.IndexOf(this.variableDataGrid.SelectedItem);
433 DataGridRow row = DataGridHelper.GetRow(this.variableDataGrid, index);
436 row.IsSelected = true;
439 if (this.continueScopeEdit)
441 DataGridCell cell = DataGridHelper.GetCell(this.variableDataGrid, index, 2);
442 cell.IsEditing = true;
448 case NotifyCollectionChangedAction.Remove:
449 foreach (ModelItem variable in e.OldItems)
451 DesignTimeVariable wrapper = this.variableWrapperCollection.FirstOrDefault(p => ModelItem.Equals(p.ReflectedObject, variable));
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)
457 this.variableWrapperCollection.Remove(wrapper);
463 this.RaiseEvent(new RoutedEventArgs(VariableDesigner.VariableCollectionChangedEvent, this));
466 void OnVariableWrapperCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
470 case NotifyCollectionChangedAction.Remove:
471 bool isUndoRedoInProgress = this.Context.Services.GetService<UndoEngine>().IsUndoRedoInProgress;
472 foreach (DesignTimeVariable arg in e.OldItems)
474 if (!isUndoRedoInProgress)
476 this.ClearCaseInsensitiveDuplicates(arg.GetVariableName(), (string)arg.ReflectedObject.Properties["Name"].ComputedValue);
477 ModelItemCollection collection = (ModelItemCollection)arg.ReflectedObject.Parent;
478 collection.Remove(arg.ReflectedObject);
483 case NotifyCollectionChangedAction.Add:
484 foreach (DesignTimeVariable var in e.NewItems)
486 this.CheckCaseInsensitiveDuplicates(var.GetVariableName(), (string)var.ReflectedObject.Properties["Name"].ComputedValue);
492 void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
494 if (bool.Equals(true, e.NewValue))
496 this.StoreLastSelection();
497 this.OnVariableScopeChanged();
499 else if (this.variableDataGrid.SelectedItem is DesignTimeVariable)
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;
509 bool OnResolveDynamicContentTemplate(ResolveTemplateParams resolveParams)
511 var variable = (DesignObjectWrapper)resolveParams.Cell.DataContext;
513 //get editor associated with variable's value
514 var editorType = variable.GetDynamicPropertyValueEditorType(DesignTimeVariable.VariableDefaultProperty);
516 //if yes there is a custom one - use it
517 if (!typeof(ExpressionValueEditor).IsAssignableFrom(editorType))
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;
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;
533 DialogPropertyValueEditor OnLoadExtendedValueEditor(DataGridCell cell, object instance)
535 var variable = (DesignObjectWrapper)cell.DataContext;
536 return variable.GetDynamicPropertyValueEditor(DesignTimeVariable.VariableDefaultProperty) as DialogPropertyValueEditor;
539 ModelProperty OnShowExtendedValueEditor(DataGridCell cell, object instance)
541 var variable = (DesignObjectWrapper)cell.DataContext;
542 return variable.Content.Properties[DesignTimeVariable.VariableDefaultProperty];
545 static void OnContextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
547 ((VariableDesigner)sender).OnContextChanged();
550 static void OnVariableScopeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
552 VariableDesigner control = (VariableDesigner)sender;
553 if (!object.Equals(e.OldValue, e.NewValue))
555 control.OnVariableScopeChanged();
559 void OnEditingControlLoaded(object sender, RoutedEventArgs args)
561 DataGridHelper.OnEditingControlLoaded(sender, args);
564 void OnEditingControlUnloaded(object sender, RoutedEventArgs args)
566 DataGridHelper.OnEditingControlUnloaded(sender, args);
569 // This is to workaround a bug that updating ModelItem from outside of VariableDesigner will not update VariableDesigner.
570 internal void NotifyAnnotationTextChanged()
572 foreach (object item in this.variableDataGrid.Items)
574 DesignTimeVariable designTimeVariable = item as DesignTimeVariable;
575 if (designTimeVariable != null)
577 designTimeVariable.NotifyPropertyChanged(DesignTimeVariable.AnnotationTextProperty);
582 protected override void OnContextMenuOpening(ContextMenuEventArgs e)
584 base.OnContextMenuOpening(e);
586 DesignerConfigurationService configurationService = this.Context.Services.GetService<DesignerConfigurationService>();
587 Fx.Assert(configurationService != null, "DesignerConfigurationService should not be null");
588 if (configurationService.WorkflowDesignerHostId == WorkflowDesignerHostId.Dev10)
595 bool openedByKeyboard = e.CursorLeft < 0 && e.CursorTop < 0;
597 if (openedByKeyboard)
599 this.ContextMenu.Placement = PlacementMode.Center;
603 this.ContextMenu.Placement = PlacementMode.MousePoint;
605 this.ContextMenu.PlacementTarget = this;
606 this.ContextMenu.IsOpen = true;
609 private void OnDeleteCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
611 ContextMenuUtilities.OnDeleteCommandCanExecute(e, this.variableDataGrid);
614 private void OnDeleteCommandExecuted(object sender, ExecutedRoutedEventArgs e)
616 if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count > 0)
618 List<ModelItem> list = new List<ModelItem>();
619 foreach (object item in this.variableDataGrid.SelectedItems)
621 DesignTimeVariable designTimeVariable = item as DesignTimeVariable;
622 if (designTimeVariable != null)
624 list.Add(designTimeVariable.ReflectedObject);
628 foreach (ModelItem modelItem in list)
630 foreach (DesignTimeVariable designTimeVariable in this.variableWrapperCollection)
632 if (designTimeVariable.ReflectedObject == modelItem)
634 this.variableWrapperCollection.Remove(designTimeVariable);
644 private void OnAnnotationSeparatorLoaded(object sender, RoutedEventArgs e)
646 ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
649 private void OnAddAnnotationMenuLoaded(object sender, RoutedEventArgs e)
651 ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
654 private void OnAddAnnotationCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
656 ContextMenuUtilities.OnAddAnnotationCommandCanExecute(e, this.Context, this.variableDataGrid);
659 private void OnAddAnnotationCommandExecuted(object sender, ExecutedRoutedEventArgs e)
661 if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count == 1)
663 AnnotationDialog dialog = new AnnotationDialog();
664 dialog.Context = Context;
665 dialog.Title = SR.AddAnnotationTitle;
667 WindowHelperService service = this.Context.Services.GetService<WindowHelperService>();
670 service.TrySetWindowOwner(this, dialog);
672 dialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
674 if (dialog.ShowDialog() == true)
676 string annotationText = dialog.AnnotationText;
678 DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItems[0];
679 variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].SetValue(annotationText);
686 private void OnEditAnnotationMenuLoaded(object sender, RoutedEventArgs e)
688 ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
691 private void OnEditAnnotationCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
693 // call the same method as delete annotation command
694 ContextMenuUtilities.OnDeleteAnnotationCommandCanExecute(e, this.Context, this.variableDataGrid);
697 private void OnEditAnnotationCommandExecuted(object sender, ExecutedRoutedEventArgs e)
699 if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count == 1)
701 DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItems[0];
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;
708 WindowHelperService service = this.Context.Services.GetService<WindowHelperService>();
711 service.TrySetWindowOwner(this, dialog);
713 dialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
715 if (dialog.ShowDialog() == true)
717 string annotationText = dialog.AnnotationText;
719 variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].SetValue(annotationText);
724 private void OnDeleteAnnotationMenuLoaded(object sender, RoutedEventArgs e)
726 ContextMenuUtilities.OnAnnotationMenuLoaded(this.Context, (Control)sender, e);
729 private void OnDeleteAnnotationCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
731 ContextMenuUtilities.OnDeleteAnnotationCommandCanExecute(e, this.Context, this.variableDataGrid);
734 private void OnDeleteAnnotationCommandExecuted(object sender, ExecutedRoutedEventArgs e)
736 if (this.variableDataGrid != null && this.variableDataGrid.SelectedItems != null && this.variableDataGrid.SelectedItems.Count == 1)
738 DesignTimeVariable variable = (DesignTimeVariable)this.variableDataGrid.SelectedItems[0];
739 variable.Content.Properties[DesignTimeVariable.AnnotationTextProperty].ClearValue();
746 internal static class VariableHelper
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<>);
754 internal static ModelItemCollection GetVariableCollection(this ModelItem element)
758 Type elementType = element.ItemType;
759 if (!((CodeActivityType.IsAssignableFrom(elementType)) || (GenericAsyncCodeActivityType.IsAssignableFrom(elementType)) ||
760 (AsyncCodeActivityType.IsAssignableFrom(elementType)) || (GenericAsyncCodeActivityType.IsAssignableFrom(elementType))))
762 ModelProperty variablesProperty = element.Properties["Variables"];
763 if ((variablesProperty != null) && (variablesProperty.PropertyType == VariablesCollectionType))
765 return variablesProperty.Collection;
772 internal static ModelItem GetVariableScopeElement(this ModelItem element)
774 while (null != element && null == VariableHelper.GetVariableCollection(element))
776 element = element.Parent;
781 internal static List<ModelItem> FindDeclaredVariables(this ModelItem element, IList<ModelItem> scopeList)
783 var variables = VariableHelper.FindVariablesInScope(element, scopeList);
784 var contained = VariableHelper.GetVariableCollection(element);
785 if (null != contained)
787 if (null != scopeList)
789 scopeList.Insert(0, element);
791 variables.InsertRange(0, contained.AsEnumerable());
796 internal static List<ModelItem> FindDeclaredVariables(this ModelItem element)
798 return VariableHelper.FindDeclaredVariables(element, null);
801 internal static List<ModelItem> FindActivityDelegateArgumentsInScope(this ModelItem workflowElement)
803 List<ModelItem> variables = new List<ModelItem>();
804 if (workflowElement != null)
806 workflowElement = workflowElement.Parent;
807 while (null != workflowElement)
809 variables.AddRange(workflowElement.FindActivityDelegateArguments());
810 workflowElement = workflowElement.Parent;
816 internal static List<ModelItem> FindActivityDelegateArguments(this ModelItem element)
818 List<ModelItem> delegateArguments = new List<ModelItem>();
819 if (element.GetCurrentValue() is ActivityDelegate)
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));
829 return delegateArguments;
832 internal static List<ModelItem> FindVariablesInScope(this ModelItem element, IList<ModelItem> scopeList)
834 List<ModelItem> variables = new List<ModelItem>();
835 if (null != scopeList)
841 element = element.Parent;
842 while (element != null)
844 ModelItemCollection variablesInElement = VariableHelper.GetVariableCollection(element);
845 if (null != variablesInElement)
847 if (null != scopeList)
849 scopeList.Add(element);
851 variables.AddRange(variablesInElement.AsEnumerable());
853 element = element.Parent;
859 internal static List<ModelItem> FindUniqueVariablesInScope(ModelItem element)
861 Dictionary<string, ModelItem> variables = new Dictionary<string, ModelItem>();
862 while (element != null)
864 ModelItemCollection variablesInElement = VariableHelper.GetVariableCollection(element);
865 if (null != variablesInElement)
867 foreach (ModelItem modelVariable in variablesInElement)
869 LocationReference locationReference = modelVariable.GetCurrentValue() as LocationReference;
870 if (locationReference != null && !string.IsNullOrWhiteSpace(locationReference.Name) && !variables.ContainsKey(locationReference.Name))
872 variables.Add(locationReference.Name, modelVariable);
876 element = element.Parent;
878 return new List<ModelItem>(variables.Values);
882 internal static List<ModelItem> FindVariablesInScope(ModelItem element)
884 return VariableHelper.FindVariablesInScope(element, null);
887 internal static bool ContainsVariable(this ModelItemCollection variableContainer, string variableName)
889 if (null == variableContainer)
891 throw FxTrace.Exception.AsError(new ArgumentNullException("variableContainer"));
893 if (!variableContainer.ItemType.IsGenericType ||
894 variableContainer.ItemType.GetGenericArguments().Length != 1 ||
895 !typeof(Variable).IsAssignableFrom(variableContainer.ItemType.GetGenericArguments()[0]))
897 throw FxTrace.Exception.AsError(new ArgumentException("non variable collection"));
900 return variableContainer.Any(p => string.Equals(p.Properties[DesignTimeVariable.VariableNameProperty].ComputedValue, variableName));
903 internal static string CreateUniqueVariableName(this ModelItemCollection variableContainer, string namePrefix, int countStartValue)
905 if (null == variableContainer)
907 throw FxTrace.Exception.AsError(new ArgumentNullException("variableContainer"));
909 if (!variableContainer.ItemType.IsGenericType ||
910 variableContainer.ItemType.GetGenericArguments().Length != 1 ||
911 !typeof(Variable).IsAssignableFrom(variableContainer.ItemType.GetGenericArguments()[0]))
913 throw FxTrace.Exception.AsError(new ArgumentException("non variable collection"));
916 string name = string.Empty;
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);
923 name = string.Format(CultureInfo.CurrentUICulture, "{0}{1}", namePrefix, countStartValue++);
924 if (!variables.Any(p => string.Equals(p.Properties[DesignTimeVariable.VariableNameProperty].ComputedValue, name)))
933 internal static ModelItem FindRootVariableScope(ModelItem element)
937 throw FxTrace.Exception.ArgumentNull("element");
939 ModelItem result = element.GetParentEnumerator().Where(p => null != VariableHelper.GetVariableCollection(p)).LastOrDefault();
943 internal static ModelItem FindCommonVariableScope(ModelItem scope1, ModelItem scope2)
945 if (null == scope1 || null == scope2)
947 throw FxTrace.Exception.AsError(new ArgumentNullException(null == scope1 ? "scope1" : "scope2"));
950 var scope1List = scope1.GetParentEnumerator().Where(p => null != VariableHelper.GetVariableCollection(p)).ToList();
951 var scope2List = scope2.GetParentEnumerator().Where(p => null != VariableHelper.GetVariableCollection(p)).ToList();
953 if (null != VariableHelper.GetVariableCollection(scope1))
955 scope1List.Insert(0, scope1);
957 if (null != VariableHelper.GetVariableCollection(scope2))
959 scope2List.Insert(0, scope2);
962 if (scope1 == scope2)
964 return scope1List.FirstOrDefault();
967 return scope1List.Intersect(scope2List).FirstOrDefault();
971 sealed class DesignTimeVariable : DesignObjectWrapper
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 };
985 bool variableTypeChanged = false;
987 internal VariableDesigner Editor
993 VBIdentifierName identifierName;
995 #region Initialize type properties code
996 public static PropertyDescriptorData[] InitializeTypeProperties()
998 return new PropertyDescriptorData[]
1000 new PropertyDescriptorData()
1002 PropertyName = VariableNameProperty,
1003 PropertyType = typeof(VBIdentifierName),
1004 PropertyAttributes = TypeDescriptor.GetAttributes(typeof(VBIdentifierName)).OfType<Attribute>().ToArray(),
1005 PropertySetter = (instance, newValue) =>
1007 ((DesignTimeVariable)instance).SetVariableName((VBIdentifierName)newValue);
1009 PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableName()),
1010 PropertyValidator = (instance, value, errors) => (((DesignTimeVariable)instance).ValidateVariableName(value, errors))
1012 new PropertyDescriptorData()
1014 PropertyName = VariableTypeProperty,
1015 PropertyType = typeof(Type),
1016 PropertyAttributes = TypeDescriptor.GetAttributes(typeof(Type)).OfType<Attribute>().ToArray(),
1017 PropertySetter = (instance, newValue) =>
1019 ((DesignTimeVariable)instance).SetVariableType((Type)newValue);
1021 PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableType()),
1022 PropertyValidator = null
1024 new PropertyDescriptorData()
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) =>
1031 ((DesignTimeVariable)instance).SetVariableScope(newValue);
1033 PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableScope()),
1034 PropertyValidator = (instance, value, errors) => (((DesignTimeVariable)instance).ValidateVariableScope(value, errors))
1036 new PropertyDescriptorData()
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) =>
1043 ((DesignTimeVariable)instance).SetVariableValue(newValue);
1045 PropertyGetter = (instance) => (((DesignTimeVariable)instance).GetVariableValue()),
1046 PropertyValidator = null,
1048 new PropertyDescriptorData()
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
1057 new PropertyDescriptorData()
1059 PropertyName = VariableScopeLevelProperty,
1060 PropertyType = typeof(int),
1061 PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
1062 PropertySetter = null,
1063 PropertyValidator = null,
1064 PropertyGetter = (instance) =>
1066 ((DesignTimeVariable)instance).GetScopeLevel()
1069 new PropertyDescriptorData()
1071 PropertyName = VariableModifiersProperty,
1072 PropertyType = typeof(VariableModifiers),
1073 PropertyAttributes = TypeDescriptor.GetAttributes(typeof(VariableModifiers)).OfType<Attribute>().ToArray(),
1074 PropertySetter = (instance, newValue) =>
1076 ((DesignTimeVariable)instance).SetVariableModifiers(newValue);
1078 PropertyValidator = null,
1079 PropertyGetter = (instance) =>
1081 return ((DesignTimeVariable)instance).GetVariableModifiers();
1084 new PropertyDescriptorData()
1086 PropertyName = AnnotationTextProperty,
1087 PropertyType = typeof(string),
1088 PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
1089 PropertySetter = (instance, newValue) =>
1091 ((DesignTimeVariable)instance).SetAnnotationText(newValue);
1093 PropertyValidator = null,
1094 PropertyGetter = (instance) =>
1096 return ((DesignTimeVariable)instance).GetAnnotationText();
1103 public DesignTimeVariable()
1105 throw FxTrace.Exception.AsError(new NotSupportedException(SR.InvalidConstructorCall));
1108 internal DesignTimeVariable(ModelItem modelItem, VariableDesigner editor)
1111 this.Editor = editor;
1112 this.identifierName = new VBIdentifierName
1114 IdentifierName = (string)modelItem.Properties[VariableNameProperty].ComputedValue
1118 protected override string AutomationId
1120 get { return this.GetVariableNameString(); }
1123 void SetVariableName(VBIdentifierName identifierName)
1125 using (ModelEditingScope change = this.ReflectedObject.BeginEdit((string)this.Editor.FindResource("changeVariableNameDescription")))
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);
1136 internal VBIdentifierName GetVariableName()
1138 return this.identifierName;
1141 string GetVariableNameString()
1143 return (string)this.ReflectedObject.Properties[VariableNameProperty].ComputedValue;
1146 protected override void OnReflectedObjectPropertyChanged(string propertyName)
1148 if (propertyName == Annotation.AnnotationTextPropertyName)
1150 RaisePropertyChangedEvent(AnnotationTextProperty);
1153 if (propertyName == VariableNameProperty)
1155 string oldValue = this.identifierName.IdentifierName;
1156 string newValue = GetVariableNameString();
1158 //This is invoked in undo stack
1159 if (oldValue != newValue)
1161 this.identifierName = new VBIdentifierName
1163 IdentifierName = newValue
1165 Editor.NotifyVariableNameChanged(this.identifierName, newValue, oldValue);
1170 void SetVariableModifiers(object modifiers)
1172 this.ReflectedObject.Properties[VariableModifiersProperty].SetValue(
1173 modifiers is ModelItem ? (modifiers as ModelItem).GetCurrentValue() : modifiers);
1176 object GetVariableModifiers()
1178 return this.ReflectedObject.Properties[VariableModifiersProperty].ComputedValue;
1184 ModelItem parent = this.ReflectedObject.Parent;
1185 while (null != parent)
1188 parent = parent.Parent;
1193 // Used by screen reader to read the DataGrid row.
1194 public override string ToString()
1196 string name = this.GetVariableNameString();
1197 if (!string.IsNullOrEmpty(name))
1204 void SetVariableType(Type type)
1206 if (!Type.Equals(type, this.GetVariableType()))
1208 using (ModelEditingScope change = this.ReflectedObject.BeginEdit((string)this.Editor.FindResource("changeVariableTypeDescription")))
1210 this.variableTypeChanged = true;
1211 ModelItemCollection variableContainer = (ModelItemCollection)this.ReflectedObject.Parent;
1212 //proceed only if variable is associated with container
1213 if (null != variableContainer)
1215 Variable variable = Variable.Create(this.GetVariableNameString(), type, (VariableModifiers)this.GetVariableModifiers());
1216 string annotationText = this.GetAnnotationText() as string;
1217 if (annotationText != null)
1219 Annotation.SetAnnotationText(variable, annotationText);
1222 //try to preserve expression
1223 ModelItem expressionModelItem = this.ReflectedObject.Properties[VariableDefaultProperty].Value;
1224 if (expressionModelItem != null)
1226 ActivityWithResult expression = expressionModelItem.GetCurrentValue() as ActivityWithResult;
1227 //check if there existed expression
1228 if (expression != null)
1230 ActivityWithResult morphedExpression = null;
1231 if (ExpressionHelper.TryMorphExpression(expression, false, type, this.Context, out morphedExpression))
1233 variable.Default = morphedExpression;
1239 Editor.ChangeVariableType(this, variable);
1240 ImportDesigner.AddImport(type.Namespace, this.Context);
1247 Type GetVariableType()
1249 return (Type)this.ReflectedObject.Properties[VariableTypeProperty].ComputedValue;
1252 void SetVariableScope(object newScope)
1254 using (ModelEditingScope change = this.ReflectedObject.BeginEdit((string)this.Editor.FindResource("changeVariableScopeDescription")))
1256 if (!ModelItem.Equals(newScope, this.GetVariableScope()))
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);
1269 object GetVariableScope()
1271 return this.ReflectedObject.Parent.Parent;
1274 void SetVariableValue(object value)
1276 object expression = value is ModelItem ? ((ModelItem)value).GetCurrentValue() : value;
1277 this.ReflectedObject.Properties[VariableDefaultProperty].SetValue(expression);
1280 object GetVariableValue()
1282 return this.ReflectedObject.Properties[VariableDefaultProperty].ComputedValue;
1287 ModelItem s = this.ReflectedObject.Parent.Parent;
1288 IMultiValueConverter converter = (IMultiValueConverter)(this.Editor.FindResource("scopeToNameConverter"));
1289 return ScopeToTooltipConverter.BuildToolTip(s, converter, CultureInfo.CurrentCulture);
1292 object GetAnnotationText()
1294 ModelProperty property = this.ReflectedObject.Properties.Find(Annotation.AnnotationTextPropertyName);
1296 if (property != null)
1298 return property.ComputedValue;
1306 void SetAnnotationText(object annotationText)
1308 ModelProperty property = this.ReflectedObject.Properties.Find(Annotation.AnnotationTextPropertyName);
1310 if (property != null)
1312 property.SetValue(annotationText);
1316 protected override Type OnGetDynamicPropertyValueEditorType(string propertyName)
1318 var type = this.GetVariableType();
1320 //in case of variables which contain handles - display HandleValueEditor
1321 if (typeof(Handle).IsAssignableFrom(type))
1323 return typeof(HandleValueEditor);
1326 var referenceType = typeof(PropertyValueEditor);
1327 var expressionEditorType = typeof(ExpressionValueEditor);
1329 //check if there are custom editors on the variable's type
1330 var variableOfType = typeof(Variable<>).MakeGenericType(type);
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>()
1341 Type currentType = Type.GetType(p.EditorTypeName);
1342 return (expressionEditorType != currentType && referenceType.IsAssignableFrom(currentType));
1344 .Select(p => Type.GetType(p.EditorTypeName))
1348 //return custom editor type (if any)
1349 if (null != customEditorType)
1351 return customEditorType;
1354 //otherwise - return default expression value editor
1355 return typeof(ExpressionValueEditor);
1358 protected override void OnPropertyChanged(string propertyName)
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))
1364 this.RaisePropertyChangedEvent(ToolTipProperty);
1365 this.RaisePropertyChangedEvent(VariableScopeLevelProperty);
1367 else if (string.Equals(propertyName, DesignTimeVariable.VariableNameProperty))
1369 this.RaisePropertyChangedEvent(AutomationIdProperty);
1371 else if (string.Equals(propertyName, DesignTimeVariable.VariableTypeProperty))
1373 this.RaisePropertyChangedEvent(VariableDefaultProperty);
1375 else if (string.Equals(propertyName, DesignTimeVariable.TimestampProperty))
1377 if (this.variableTypeChanged)
1379 this.RaisePropertyChangedEvent(DesignTimeVariable.VariableTypeProperty);
1380 this.variableTypeChanged = false;
1381 this.CustomValueEditors.Clear();
1382 this.Editor.UpdateTypeDesigner(this);
1385 base.OnPropertyChanged(propertyName);
1388 bool ValidateVariableName(object newValue, List<string> errors)
1390 if (!base.IsUndoRedoInProgress && null != this.ReflectedObject.Parent)
1392 VBIdentifierName identifier = newValue as VBIdentifierName;
1394 string newName = null;
1395 if (identifier != null)
1397 newName = identifier.IdentifierName;
1401 if (!string.IsNullOrEmpty(newName))
1403 Func<ModelItem, bool> checkForDuplicates =
1404 new Func<ModelItem, bool>(p => string.Equals(p.Properties[VariableNameProperty].ComputedValue, newName) && !object.Equals(p, this.ReflectedObject));
1406 bool duplicates = this.ReflectedObject.Parent.Parent.Properties["Variables"].Collection.Any(checkForDuplicates);
1410 errors.Add(string.Format(CultureInfo.CurrentUICulture, SR.DuplicateVariableName, newName));
1415 errors.Add(SR.EmptyVariableName);
1418 return 0 == errors.Count;
1421 bool ValidateVariableScope(object newValue, List<string> errors)
1423 if (!base.IsUndoRedoInProgress)
1425 ModelItem scope = (newValue as ModelItem) ?? Editor.ScopesList.FirstOrDefault(p => object.Equals(p.GetCurrentValue(), newValue));
1426 string currentName = this.GetVariableNameString();
1428 Func<ModelItem, bool> checkForDuplicates =
1429 new Func<ModelItem, bool>(p => string.Equals(p.Properties[VariableNameProperty].ComputedValue, currentName) && !object.Equals(p, this.ReflectedObject));
1431 bool duplicates = scope.Properties["Variables"].Collection.Any(checkForDuplicates);
1434 errors.Add(string.Format(CultureInfo.CurrentUICulture, SR.DuplicateVariableName, currentName));
1437 return 0 == errors.Count;
1440 #region Internal classes
1441 internal sealed class ScopeValueEditor : PropertyValueEditor
1443 public ScopeValueEditor()
1445 this.InlineEditorTemplate = EditorResources.GetResources()["ScopeEditor_InlineEditorTemplate"] as DataTemplate;
1452 sealed class DesignTimeVariableToScopeConverter : IValueConverter
1454 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
1456 ModelItem designTimeVariable = value as ModelItem;
1457 object result = null;
1458 if (null != designTimeVariable && typeof(DesignTimeVariable).IsAssignableFrom(designTimeVariable.ItemType))
1460 DesignTimeVariable variable = (DesignTimeVariable)designTimeVariable.GetCurrentValue();
1461 result = variable.Editor.ScopesList;
1466 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
1468 throw FxTrace.Exception.AsError(new NotSupportedException());
1472 sealed class ScopeToTooltipConverter : IValueConverter
1474 IMultiValueConverter baseConverter = new BreadCrumbTextConverter();
1476 internal static string BuildToolTip(ModelItem entry, IMultiValueConverter displayNameConverter, CultureInfo culture)
1478 string result = null;
1479 if (null != entry && null != displayNameConverter)
1481 StringBuilder sb = new StringBuilder();
1483 ModelItem currentEntry = entry;
1484 while (currentEntry != null)
1486 if (null != currentEntry.Properties["Variables"])
1490 currentEntry = currentEntry.Parent;
1493 while (entry != null)
1495 if (null != entry.Properties["Variables"])
1500 sb.Insert(0, " ", --indent);
1501 sb.Insert(0, Environment.NewLine);
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));
1506 entry = entry.Parent;
1508 result = sb.ToString();
1513 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
1515 return BuildToolTip(value as ModelItem, this.baseConverter, culture);
1518 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
1520 throw FxTrace.Exception.AsError(new NotSupportedException());
1524 sealed class ScopeComboBox : ComboBox
1526 bool isScopeValid = true;
1528 protected override void OnInitialized(EventArgs e)
1530 base.OnInitialized(e);
1531 this.Loaded += (s, args) =>
1533 //get the binding expression, and hook up exception filter
1534 var expr = this.GetBindingExpression(ScopeComboBox.SelectedItemProperty);
1535 if (null != expr && null != expr.ParentBinding)
1537 expr.ParentBinding.UpdateSourceExceptionFilter = this.OnUpdateBindingException;
1542 object OnUpdateBindingException(object sender, Exception err)
1544 //if exception occured, the scope as invalid
1545 if (err is TargetInvocationException && err.InnerException is ValidationException || err is ValidationException)
1547 this.isScopeValid = false;
1552 protected override void OnSelectionChanged(SelectionChangedEventArgs e)
1554 //if validation succeeded - update the control state with new selection
1555 if (this.isScopeValid)
1557 base.OnSelectionChanged(e);
1559 //otherwise, get the binding expression and update control with current state from the source
1562 var expr = this.GetBindingExpression(ScopeComboBox.SelectedItemProperty);
1565 expr.UpdateTarget();
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;