1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
5 namespace System.Activities.Presentation.View
7 using System.Activities.ExpressionParser;
8 using System.Activities.Expressions;
9 using System.Activities.Presentation.Expressions;
10 using System.Activities.Presentation.Hosting;
11 using System.Activities.Presentation.Internal.PropertyEditing;
12 using System.Activities.Presentation.Model;
13 using System.Activities.Presentation.Services;
14 using System.Activities.Presentation.Validation;
15 using System.Activities.Presentation.Xaml;
16 using System.Activities.XamlIntegration;
17 using System.Collections.Generic;
18 using System.ComponentModel;
19 using System.Diagnostics;
20 using System.Diagnostics.CodeAnalysis;
21 using System.Globalization;
24 using System.Runtime.Versioning;
25 using System.Threading;
27 using System.Windows.Automation;
28 using System.Windows.Automation.Peers;
29 using System.Windows.Controls;
30 using System.Windows.Data;
31 using System.Windows.Input;
32 using System.Windows.Media;
33 using System.Windows.Threading;
34 using Microsoft.VisualBasic.Activities;
35 using Microsoft.Activities.Presentation;
37 //This control is used for expression editing in activity designers.
38 //It uses the Activity<T> TypeConverter to convert between a Activity<T> and its string representation.
39 //It defines 3 dependency properties - OwnerActivity, ExpressionModelItem and ExpressionType.
40 //ActivityModelItem is used to create the parser context required by the TypeConverter.
41 //ExpressionType is the type of the expression associated with this text box. This is required by the TypeConverter.
43 internal sealed partial class VisualBasicEditor : TextualExpressionEditor
45 private static readonly Type VariableValueType = typeof(VariableValue<>);
46 private static readonly Type VariableReferenceType = typeof(VariableReference<>);
47 private static readonly Type LiteralType = typeof(Literal<>);
48 private static readonly Type VisualBasicValueType = typeof(VisualBasicValue<>);
49 private static readonly Type VisualBasicReferenceType = typeof(VisualBasicReference<>);
51 internal static string ExpressionLanguageName = (new VisualBasicValue<string>() as ITextExpression).Language;
53 internal static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(VisualBasicEditor),
54 new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnTextChanged), new CoerceValueCallback(OnTextCoerceValue)));
56 internal static readonly DependencyProperty ValidationStateProperty = DependencyProperty.Register("ValidationState", typeof(ValidationState), typeof(VisualBasicEditor),
57 new FrameworkPropertyMetadata(ValidationState.Valid));
59 internal static readonly DependencyProperty EditingStateProperty = DependencyProperty.Register("EditingState", typeof(EditingState), typeof(VisualBasicEditor),
60 new PropertyMetadata(EditingState.Idle));
62 internal static readonly DependencyProperty HasValidationErrorProperty = DependencyProperty.Register("HasValidationError", typeof(bool), typeof(VisualBasicEditor),
63 new PropertyMetadata(false));
65 internal static readonly DependencyProperty ValidationErrorMessageProperty = DependencyProperty.Register("ValidationErrorMessage", typeof(string), typeof(VisualBasicEditor),
66 new PropertyMetadata(null));
68 internal static readonly DependencyProperty ExpressionTextProperty = DependencyProperty.Register("ExpressionText", typeof(string), typeof(VisualBasicEditor),
69 new PropertyMetadata(null));
71 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
72 public static readonly ICommand CompleteWordCommand = new RoutedCommand("CompleteWordCommand", typeof(VisualBasicEditor));
73 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
74 public static readonly ICommand GlobalIntellisenseCommand = new RoutedCommand("GlobalIntellisenseCommand", typeof(VisualBasicEditor));
75 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
76 public static readonly ICommand ParameterInfoCommand = new RoutedCommand("ParameterInfoCommand", typeof(VisualBasicEditor));
77 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
78 public static readonly ICommand QuickInfoCommand = new RoutedCommand("QuickInfoCommand", typeof(VisualBasicEditor));
79 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
80 public static readonly ICommand IncreaseFilterLevelCommand = new RoutedCommand("IncreaseFilterLevelCommand", typeof(VisualBasicEditor));
81 [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)]
82 public static readonly ICommand DecreaseFilterLevelCommand = new RoutedCommand("DecreaseFilterLevelCommand", typeof(VisualBasicEditor));
84 bool internalModelItemChange = false;
85 string previousText = null;
86 ModelProperty expressionModelProperty;
87 TypeConverter expressionConverter;
88 bool initialized = false;
89 bool isEditorLoaded = false;
91 IExpressionEditorService expressionEditorService;
92 IExpressionEditorInstance expressionEditorInstance;
93 TextBox editingTextBox;
97 double blockHeight = double.NaN;
98 double blockWidth = double.NaN;
99 bool isExpressionLoaded = false;
100 bool isBeginEditPending = false;
102 DesignerPerfEventProvider perfProvider;
103 ModelItem boundedExpression = null;
104 BackgroundWorker validator = null;
105 const int ValidationWaitTime = 800;
107 PropertyChangedEventHandler onExpressionModelItemChangedHandler;
109 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.InitializeReferenceTypeStaticFieldsInline,
110 Justification = "This is recommended way by WPF to override metadata of DependencyProperty in derived class")]
111 static VisualBasicEditor()
113 ExpressionActivityEditor.HintTextProperty.OverrideMetadata(typeof(VisualBasicEditor), new FrameworkPropertyMetadata(SR.ExpressionDefaultText));
116 public VisualBasicEditor()
118 InitializeComponent();
120 this.MinHeight = this.FontSize + 4; /* 4 pixels for border*/
122 this.editorName = null;
124 this.ContentTemplate = (DataTemplate)FindResource("textblock");
125 this.Loaded += this.OnExpressionTextBoxLoaded;
126 this.Unloaded += this.OnExpressionTextBoxUnloaded;
129 PropertyChangedEventHandler OnExpressionModelItemChanged
133 if (this.onExpressionModelItemChangedHandler == null)
135 this.onExpressionModelItemChangedHandler = new PropertyChangedEventHandler(this.expressionModelItem_PropertyChanged);
138 return this.onExpressionModelItemChangedHandler;
142 protected override void OnContextMenuOpening(ContextMenuEventArgs e)
147 protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
149 switch (e.Property.Name)
152 this.OnHintTextChanged(e);
155 this.OnExpressionChanged(e);
157 case "ExpressionType":
158 this.OnExpressionTypeChanged(e);
160 case "OwnerActivity":
161 this.OnOwnerModelItemChanged(e);
163 case "UseLocationExpression":
164 this.OnUseLocationExpressionChanged(e);
166 case "PathToArgument":
167 this.OnPathToArgumentChanged(e);
171 this.OnLinesChanged(e);
173 case "AcceptsReturn":
174 this.OnAcceptsReturnChanged(e);
177 this.OnAcceptsTabChanged(e);
179 case "IsIndependentExpressionProperty":
180 this.OnIsIndependentExpressionChanged();
183 base.OnPropertyChanged(e);
186 protected override void OnPreviewMouseRightButtonDown(MouseButtonEventArgs e)
188 base.OnPreviewMouseRightButtonDown(e);
192 static object OnTextCoerceValue(DependencyObject dp, object value)
194 string tempText = value as string;
195 VisualBasicEditor etb = dp as VisualBasicEditor;
198 if (tempText != null)
200 tempText = tempText.Trim();
206 protected override AutomationPeer OnCreateAutomationPeer()
208 return new VisualBasicEditorAutomationPeer(this);
211 protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
213 base.OnLostKeyboardFocus(e);
215 if (this.expressionEditorInstance != null &&
216 (this.expressionEditorInstance.HasAggregateFocus ||
217 (this.hostControl != null && this.hostControl.IsFocused)))
225 private void DoLostFocus()
229 ValidateExpression(this);
231 if (this.Context != null)
232 { // Unselect if this is the currently selected one.
233 ExpressionSelection current = this.Context.Items.GetValue<ExpressionSelection>();
234 if (current != null && current.ModelItem == this.Expression)
236 ExpressionSelection emptySelection = new ExpressionSelection(null);
237 this.Context.Items.SetValue(emptySelection);
241 // Generate and validate the expression.
242 // Get the text from the snapshot and set it to the Text property
243 if (this.expressionEditorInstance != null)
245 this.expressionEditorInstance.ClearSelection();
248 bool committed = false;
249 if (!this.ExplicitCommit)
251 //commit change and let the commit change code do the revert
252 committed = Commit(false);
254 //reset the error icon if we didn't get to set it in the commit
255 if (!committed || this.IsIndependentExpression)
257 this.EditingState = EditingState.Idle;
258 // Switch the control back to a textbox -
259 // but give it the text from the editor (textbox should be bound to the Text property, so should
260 // automatically be filled with the correct text, from when we set the Text property earlier)
261 if (!this.ContentTemplate.Equals((DataTemplate)FindResource("textblock")))
263 this.ContentTemplate = (DataTemplate)FindResource("textblock");
268 //raise EditorLostLogical focus - in case when some clients need to do explicit commit
269 this.RaiseEvent(new RoutedEventArgs(ExpressionTextBox.EditorLostLogicalFocusEvent, this));
272 private void KillValidator()
274 if (validator != null)
276 this.validator.CancelAsync();
277 this.validator.Dispose();
278 this.validator = null;
282 internal static bool ShouldGenerateExpression(string oldText, string newText)
284 return newText != null && !string.Equals(newText, oldText) && !(oldText == null && newText.Equals(string.Empty));
287 public override bool Commit(bool isExplicitCommit)
289 bool committed = false;
290 //only generate and validate the expression when when we don't require explicit commit change
291 //or when the commit is explicit
292 if (!this.ExplicitCommit || isExplicitCommit)
294 // Generate and validate the expression.
295 // Get the text from the snapshot and set it to the Text property
296 this.previousText = null;
298 if (this.expressionEditorInstance != null)
300 this.previousText = this.Text;
301 this.Text = this.expressionEditorInstance.GetCommittedText();
306 if (this.Expression != null)
308 Activity expression = this.Expression.GetCurrentValue() as Activity;
309 // if expression is null, GetExpressionString will return null
310 this.previousText = ExpressionHelper.GetExpressionString(expression, this.OwnerActivity);
314 this.previousText = null;
317 if (this.editingTextBox != null)
319 this.editingTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
323 // If the Text is null, or equal to the previous value, or changed from null to empty, don't bother generating the expression
324 // We still need to generate the expression when it is changed from other value to EMPTY however - otherwise
325 // the case where you had an expression (valid or invalid), then deleted the whole thing will not be evaluated.
326 if (ShouldGenerateExpression(this.previousText, this.Text))
328 GenerateExpression();
332 if (!this.ContentTemplate.Equals((DataTemplate)FindResource("textblock")))
334 this.ContentTemplate = (DataTemplate)FindResource("textblock");
339 protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
341 base.OnPreviewMouseLeftButtonDown(e);
343 if (this.Context != null)
345 ExpressionSelection expressionSelection = new ExpressionSelection(this.Expression);
346 this.Context.Items.SetValue(expressionSelection);
350 public override void BeginEdit()
352 //am i loaded? is current template a textblock?
353 if (this.isExpressionLoaded || null == this.Expression)
355 this.isBeginEditPending = false;
356 this.IsReadOnly = false;
357 if (this.IsLoaded && this.ContentTemplate.Equals(this.FindResource("textblock")))
359 //get control's content presenter
360 ContentPresenter presenter = VisualTreeUtils.GetTemplateChild<ContentPresenter>(this);
361 if (null != presenter)
363 //and look for the loaded textblock
364 TextBlock tb = (TextBlock)this.ContentTemplate.FindName("expresionTextBlock", presenter);
367 //now - give focus to the textblock - it will trigger OnGotTextBlockFocus event, which eventually
368 //swithc ETB into expression editing mode.
376 this.isBeginEditPending = true;
380 internal bool HasAggregateFocus()
386 if (this.expressionEditorInstance != null)
388 result = (this.hostControl != null && this.hostControl.IsFocused) || this.expressionEditorInstance.HasAggregateFocus;
392 result = !this.IsKeyboardFocused && this.IsKeyboardFocusWithin;
399 void OnTextBlockMouseLeftButtonDown(object sender, RoutedEventArgs e)
401 if (!this.IsReadOnly)
403 TextBlock textBlock = sender as TextBlock;
404 if (textBlock != null)
406 Keyboard.Focus(textBlock);
412 void OnGotTextBlockFocus(object sender, RoutedEventArgs e)
414 if (this.Context == null)
419 DesignerView designerView = this.Context.Services.GetService<DesignerView>();
421 if (!designerView.IsMultipleSelectionMode)
423 TextBlock textBlock = sender as TextBlock;
424 bool isInReadOnlyMode = this.IsReadOnly;
425 if (this.Context != null)
427 ReadOnlyState readOnlyState = this.Context.Items.GetValue<ReadOnlyState>();
428 isInReadOnlyMode |= readOnlyState.IsReadOnly;
430 if (null != textBlock)
432 this.blockHeight = textBlock.ActualHeight;
433 this.blockHeight = Math.Max(this.blockHeight, textBlock.MinHeight);
434 this.blockHeight = Math.Min(this.blockHeight, textBlock.MaxHeight);
435 this.blockWidth = textBlock.ActualWidth;
436 this.blockWidth = Math.Max(this.blockWidth, textBlock.MinWidth);
437 this.blockWidth = Math.Min(this.blockWidth, textBlock.MaxWidth);
439 // If it's already an editor, don't need to switch it/reload it (don't create another editor/grid if we don't need to)
440 // Also don't create editor when we are in read only mode
441 if (this.ContentTemplate.Equals((DataTemplate)FindResource("textblock")) && !isInReadOnlyMode)
443 if (this.Context != null)
445 // Get the ExpressionEditorService
446 this.expressionEditorService = this.Context.Services.GetService<IExpressionEditorService>();
449 // If the service exists, use the editor template - else switch to the textbox template
450 if (this.expressionEditorService != null)
452 this.PerfProvider.WorkflowDesignerExpressionEditorLoadStart();
453 this.ContentTemplate = (DataTemplate)FindResource("editor");
457 this.ContentTemplate = (DataTemplate)FindResource("textbox");
462 if (!isInReadOnlyMode)
464 //disable the error icon
465 this.StartValidator();
466 this.EditingState = EditingState.Editing;
472 void OnGotEditingFocus(object sender, RoutedEventArgs e)
474 //disable the error icon
475 this.EditingState = EditingState.Editing;
476 this.StartValidator();
479 // This method is called when the editor data template is loaded - when the editor data template
480 // is loaded, create the editor session and the expression editor
481 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
482 Justification = "CreateExpressionEditor is part of a public API. Propagating exceptions might lead to VS crash.")]
483 [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
484 Justification = "CreateExpressionEditor is part of a public API. Propagating exceptions might lead to VS crash.")]
485 void OnEditorLoaded(object sender, RoutedEventArgs e)
487 if (!this.isEditorLoaded)
489 // If the service exists, create an expression editor and add it to the grid - else switch to the textbox data template
490 if (this.expressionEditorService != null)
492 Border border = (Border)sender;
493 // Get the references and variables in scope
494 AssemblyContextControlItem assemblies = (AssemblyContextControlItem)this.Context.Items.GetValue(typeof(AssemblyContextControlItem));
495 List<ModelItem> declaredVariables = VisualBasicEditor.GetVariablesInScope(this.OwnerActivity);
497 ImportedNamespaceContextItem importedNamespaces = this.Context.Items.GetValue<ImportedNamespaceContextItem>();
498 importedNamespaces.EnsureInitialized(this.Context);
499 //if the expression text is empty and the expression type is set, then we initialize the text to prompt text
500 if (String.Equals(this.ExpressionText, string.Empty, StringComparison.OrdinalIgnoreCase) && this.ExpressionType != null)
502 this.Text = TypeToPromptTextConverter.GetPromptText(this.ExpressionType);
506 this.blockWidth = Math.Max(this.ActualWidth - 8, 0); //8 is the margin
509 this.blockWidth = Math.Max(this.blockWidth - 16, 0); //give 16 for error icon
513 if (this.ExpressionType != null)
515 this.expressionEditorInstance = this.expressionEditorService.CreateExpressionEditor(assemblies, importedNamespaces, declaredVariables, this.Text, this.ExpressionType, new Size(this.blockWidth, this.blockHeight));
519 this.expressionEditorInstance = this.expressionEditorService.CreateExpressionEditor(assemblies, importedNamespaces, declaredVariables, this.Text, new Size(this.blockWidth, this.blockHeight));
524 Trace.WriteLine(ex.Message);
527 if (this.expressionEditorInstance != null)
531 this.expressionEditorInstance.VerticalScrollBarVisibility = this.VerticalScrollBarVisibility;
532 this.expressionEditorInstance.HorizontalScrollBarVisibility = this.HorizontalScrollBarVisibility;
534 this.expressionEditorInstance.AcceptsReturn = this.AcceptsReturn;
535 this.expressionEditorInstance.AcceptsTab = this.AcceptsTab;
537 // Add the expression editor to the text panel, at column 1
538 this.hostControl = this.expressionEditorInstance.HostControl;
540 // Subscribe to this event to change scrollbar visibility on the fly for auto, and to resize the hostable editor
542 this.expressionEditorInstance.LostAggregateFocus += new EventHandler(OnEditorLostAggregateFocus);
543 this.expressionEditorInstance.Closing += new EventHandler(OnEditorClosing);
545 // Set up Hostable Editor properties
546 this.expressionEditorInstance.MinLines = this.MinLines;
547 this.expressionEditorInstance.MaxLines = this.MaxLines;
549 this.expressionEditorInstance.HostControl.Style = (Style)FindResource("editorStyle");
551 border.Child = this.hostControl;
552 this.expressionEditorInstance.Focus();
554 catch (KeyNotFoundException ex)
556 Debug.Fail("Unable to find editor with the following editor name: " + this.editorName, ex.Message);
561 if (this.expressionEditorInstance == null)
563 this.ContentTemplate = (DataTemplate)FindResource("textbox");
565 this.PerfProvider.WorkflowDesignerExpressionEditorLoaded();
567 this.isEditorLoaded = true;
571 void OnEditorClosing(object sender, EventArgs e)
573 if (this.expressionEditorInstance != null)
575 //these events are expected to be unregistered during lost focus event, but
576 //we are unregistering them during unload just in case. Ideally we want to
577 //do this in the CloseExpressionEditor method
578 this.expressionEditorInstance.LostAggregateFocus -= new EventHandler(OnEditorLostAggregateFocus);
580 this.expressionEditorInstance.Closing -= new EventHandler(OnEditorClosing);
581 this.expressionEditorInstance = null;
583 Border boarder = this.hostControl.Parent as Border;
586 boarder.Child = null;
588 this.hostControl = null;
589 this.editorName = null;
593 void OnEditorLostAggregateFocus(object sender, EventArgs e)
598 //void BindEditorProperties()
600 // this.hostControl.SetBinding(Control.ContextMenuProperty, "ContextMenu");
601 // this.hostControl.SetBinding(Control.FlowDirectionProperty, "FlowDirection");
602 // this.hostControl.SetBinding(Control.FontFamilyProperty, "FontFamily");
603 // this.hostControl.SetBinding(Control.FontSizeProperty, "FontSize");
604 // this.hostControl.SetBinding(Control.FontStretchProperty, "FontStretch");
605 // this.hostControl.SetBinding(Control.FontStyleProperty, "FontStyle");
606 // this.hostControl.SetBinding(Control.FontWeightProperty, "FontWeight");
607 // this.hostControl.SetBinding(Control.HeightProperty, "Height");
608 // this.hostControl.SetBinding(Control.LanguageProperty, "Language");
609 // this.hostControl.SetBinding(Control.SnapsToDevicePixelsProperty, "SnapsToDevicePixels");
612 // This method is called when the editor data template is unloaded - when the editor data template
613 // is unloaded, close the editor session and set the expression editor and editor session to null
614 void OnEditorUnloaded(object sender, RoutedEventArgs e)
616 // Blank the editorSession and the expressionEditor so as not to use up memory
617 // Destroy both as you can only ever spawn one editor per session
618 if (this.expressionEditorInstance != null)
620 //if we are unloaded during editing, this means we got here by someone clicking breadcrumb, we should try to commit
621 if (this.EditingState == EditingState.Editing)
625 this.expressionEditorInstance.Close();
629 this.editingTextBox = null;
632 this.isEditorLoaded = false;
635 // This method is to give focus and set the caret position when the TextBox DataTemplate is loaded
636 void OnTextBoxLoaded(object sender, RoutedEventArgs e)
638 TextBox textbox = (TextBox)sender;
639 this.editingTextBox = textbox;
640 textbox.ContextMenu = null;
642 //to workaround a but in the TextBox layout code
643 Binding binding = new Binding();
644 binding.Source = this;
645 binding.Path = new PropertyPath(VisualBasicEditor.TextProperty);
646 binding.Mode = BindingMode.TwoWay;
647 binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
649 textbox.SetBinding(TextBox.TextProperty, binding);
651 // Set the cursor to correct text position
652 int index = GetCharacterIndexFromPoint(textbox);
654 textbox.SelectionStart = index;
655 textbox.SelectionLength = 0;
660 // This method is to workaround the fact that textbox.GetCharacterIndexFromPoint returns the caret
661 // to the left of the character... Thus you can never get the caret after the last character in the
662 // expression string.
663 int GetCharacterIndexFromPoint(TextBox textbox)
665 Point position = Mouse.GetPosition(textbox);
666 int index = textbox.GetCharacterIndexFromPoint(position, false);
670 // May have clicked outside the text area, get the index of nearest char
671 index = textbox.GetCharacterIndexFromPoint(position, true);
677 // Adjust the cursor position if we clicked to the right of returned character
678 Rect charRect = textbox.GetRectFromCharacterIndex(index, true);
679 if (position.X > charRect.Left + charRect.Width / 2)
688 static void ValidateExpression(VisualBasicEditor etb)
691 if (etb.DoValidation(new ExpressionValidationContext(etb), out errorMessage))
693 etb.UpdateValidationError(errorMessage);
697 [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters, Justification = "Existing code")]
698 void OnLinesChanged(DependencyPropertyChangedEventArgs e)
700 if (this.MinLines > this.MaxLines)
702 this.MaxLines = this.MinLines;
706 static void OnTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
708 VisualBasicEditor textBox = (VisualBasicEditor)dependencyObject;
710 if (textBox.ExpressionEditorService != null && textBox.expressionEditorInstance != null)
712 textBox.expressionEditorInstance.Text = textBox.Text;
717 void OnOwnerModelItemChanged(DependencyPropertyChangedEventArgs e)
719 this.InitializeContext(e);
720 this.OnPathToArgumentChanged(this.PathToArgument);
723 public override IExpressionEditorService ExpressionEditorService
725 get { return this.expressionEditorService; }
730 get { return (string)GetValue(TextProperty); }
731 set { SetValue(TextProperty, value); }
734 internal string ExpressionText
736 get { return (string)GetValue(ExpressionTextProperty); }
737 set { SetValue(ExpressionTextProperty, value); }
740 internal ValidationState ValidationState
742 get { return (ValidationState)GetValue(VisualBasicEditor.ValidationStateProperty); }
743 set { SetValue(VisualBasicEditor.ValidationStateProperty, value); }
746 internal bool HasErrors
750 bool hasErrors = false;
751 if (this.EditingState == EditingState.Idle
752 && !this.IsIndependentExpression)
754 if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null)
756 ValidationState state = this.ValidationService.ValidationStateProperty.Getter(this.Expression);
757 hasErrors = state == ValidationState.Error;
762 hasErrors = this.HasValidationError;
768 internal string ErrorMessage
772 string errorMessage = string.Empty;
774 if (this.EditingState == EditingState.Idle && !this.IsIndependentExpression)
776 if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null)
778 errorMessage = this.ValidationService.ValidationMessageProperty.Getter(this.Expression);
783 errorMessage = this.ValidationErrorMessage;
789 internal EditingState EditingState
791 get { return (EditingState)GetValue(EditingStateProperty); }
792 set { SetValue(EditingStateProperty, value); }
795 internal bool HasValidationError
797 get { return (bool)GetValue(HasValidationErrorProperty); }
798 set { SetValue(HasValidationErrorProperty, value); }
801 internal string ValidationErrorMessage
803 get { return (string)GetValue(ValidationErrorMessageProperty); }
804 set { SetValue(ValidationErrorMessageProperty, value); }
807 DesignerPerfEventProvider PerfProvider
811 if (this.perfProvider == null && this.Context != null)
813 perfProvider = this.Context.Services.GetService<DesignerPerfEventProvider>();
815 return this.perfProvider;
819 ValidationService ValidationService
823 if (this.Context != null)
825 return this.Context.Services.GetService<ValidationService>();
834 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
835 Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")]
836 [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
837 Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")]
838 internal void GenerateExpression()
840 Activity valueExpression = null;
842 // If the text is null we don't need to bother generating the expression (this would be the case the
843 // first time you enter an ETB. We still need to generate the expression when it is EMPTY however - otherwise
844 // the case where you had an expression (valid or invalid), then deleted the whole thing will not be evaluated.
845 if (this.Text != null)
847 using (ModelEditingScope scope = this.OwnerActivity.BeginEdit(SR.PropertyChangeEditingScopeDescription))
849 this.EditingState = EditingState.Validating;
850 // we set the expression to null
851 // a) when the expressionText is empty AND it's a reference expression or
852 // b) when the expressionText is empty AND the DefaultValue property is null
853 if (this.Text.Length == 0 &&
854 (this.UseLocationExpression || (this.DefaultValue == null)))
856 valueExpression = null;
860 if (this.Text.Length == 0)
862 this.Text = this.DefaultValue;
864 valueExpression = CreateVBExpression();
866 CreateExpressionModelItem(valueExpression);
872 void OnValidationCompleted(object sender, EventArgs e)
874 if (this.EditingState != EditingState.Editing)
876 if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null)
878 this.ValidationState = this.ValidationService.ValidationStateProperty.Getter(this.Expression);
881 this.EditingState = EditingState.Idle;
885 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
886 Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")]
887 [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
888 Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")]
889 private void CreateExpressionModelItem(object valueExpression)
891 // try to wrap the droppedObject in a ModelItem.
892 ModelItem expressionModelItem = null;
893 if (valueExpression != null)
895 ModelServiceImpl modelService = (ModelServiceImpl)this.Context.Services.GetService<ModelService>();
896 expressionModelItem = modelService.WrapAsModelItem(valueExpression);
897 expressionModelItem.PropertyChanged += this.OnExpressionModelItemChanged;
898 this.boundedExpression = expressionModelItem;
902 this.internalModelItemChange = true;
903 this.EditingState = EditingState.Validating;
904 SetValue(ExpressionProperty, expressionModelItem);
906 catch (Exception err)
908 Trace.WriteLine(string.Format(CultureInfo.CurrentUICulture, "{0}\r\n{1}", err.Message, err.StackTrace));
909 this.internalModelItemChange = false;
913 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
914 Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")]
915 [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
916 Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")]
917 private Activity CreateVBExpression()
919 Activity valueExpression = null;
920 if (this.OwnerActivity != null)
922 ExpressionValidationContext validationContext = new ExpressionValidationContext(this);
924 Type expReturnType = null;
925 string newExpressionText = null;
926 SourceExpressionException compileErrorMessages;
930 VisualBasicSettings settings = null;
931 valueExpression = CreateVBExpression(validationContext, out newExpressionText, out expReturnType, out compileErrorMessages, out settings);
933 if (settings != null)
935 Fx.Assert(this.Context != null, "editing context cannot be null");
936 //merge with import designer
937 foreach (VisualBasicImportReference reference in settings.ImportReferences)
939 ImportDesigner.AddImport(reference.Import, this.Context);
943 if (!string.IsNullOrEmpty(newExpressionText))
945 this.previousText = this.Text;
946 this.Text = newExpressionText;
949 catch (Exception err)
951 //if the VisualBasicDesignerHelper were able to resolve the type we use that
952 if (expReturnType == null)
954 //if not we try to use the expression type
955 expReturnType = this.ExpressionType;
958 //if expression type is also null, the we use object
959 if (expReturnType == null)
961 expReturnType = typeof(object);
964 valueExpression = VisualBasicEditor.CreateExpressionFromString(expReturnType, this.Text, UseLocationExpression, validationContext.ParserContext);
966 Trace.WriteLine(string.Format(CultureInfo.CurrentUICulture, "{0}\r\n{1}", err.Message, err.StackTrace));
968 this.ExpressionText = this.Text;
972 // If the OwnerActivity is null, do not try to compile the expression otherwise VS will crash
973 // Inform the user that OwnerActivity is null (i.e. there is a error in their code)
974 Trace.WriteLine("ExpressionTextBox OwnerActivity is null.");
976 return valueExpression;
979 internal static ActivityWithResult CreateExpressionFromString(string expressionText, bool isLocation, Type type)
981 return VisualBasicEditor.CreateExpressionFromString(type, expressionText, isLocation, new ParserContext());
984 internal static ActivityWithResult CreateExpressionFromString(Type type, string expressionText, bool isLocation, ParserContext context)
986 ActivityWithResult newExpression;
990 newExpression = ExpressionHelper.TryCreateLiteral(type, expressionText, context);
992 if (newExpression != null)
994 return newExpression;
998 Type targetExpressionType = null;
1001 targetExpressionType = typeof(VisualBasicReference<>).MakeGenericType(type);
1005 targetExpressionType = typeof(VisualBasicValue<>).MakeGenericType(type);
1008 //create new visual basic value and pass expression text into it
1009 newExpression = (ActivityWithResult)Activator.CreateInstance(targetExpressionType, expressionText);
1010 //targetExpressionType.GetProperty("Settings").SetValue(newExpression, settings, null);
1011 //this code below is never executed - it is placed here only to enable compilation support whenver VisualBasicValue constructor
1012 //changes its parameter list.
1013 if (null == newExpression)
1015 //if this gives compilation error, please update CreateInstance parameter list above as well!
1016 newExpression = new VisualBasicValue<string>(expressionText);
1019 return newExpression;
1022 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
1023 Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")]
1024 [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
1025 Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")]
1026 private static Activity CreateVBExpression(ExpressionValidationContext context, out string newExpressionText, out Type expReturnType, out SourceExpressionException compileErrorMessages, out VisualBasicSettings vbSettings)
1028 expReturnType = null;
1029 newExpressionText = null;
1030 compileErrorMessages = null;
1033 //try easy way first - look if there is a type converter which supports conversion between expression type and string
1034 ActivityWithResult literal = null;
1037 if (!context.UseLocationExpression)
1039 literal = ExpressionHelper.TryCreateLiteral(context.ExpressionType, context.ExpressionText, context.ParserContext);
1042 if (literal != null)
1044 //need to get new expression text - converter might have changed its format, and we want it to be up to date
1045 IValueSerializableExpression serializableExpression = literal as IValueSerializableExpression;
1046 Fx.Assert(serializableExpression != null, "the expression has to be a Literal<>, which should be IValueSerializableExpression");
1047 if (serializableExpression.CanConvertToString(context.ParserContext))
1049 bool shouldBeQuoted = typeof(string) == context.ExpressionType || typeof(Uri) == context.ExpressionType;
1051 //whether string begins and ends with quotes '"'. also, if there are
1052 //more quotes within than those begining and ending ones, do not bother with literal - assume this is an expression.
1053 bool isQuotedString = shouldBeQuoted &&
1054 context.ExpressionText.StartsWith("\"", StringComparison.CurrentCulture) &&
1055 context.ExpressionText.EndsWith("\"", StringComparison.CurrentCulture) &&
1056 context.ExpressionText.IndexOf("\"", 1, StringComparison.CurrentCulture) == context.ExpressionText.Length - 1;
1057 var formatString = isQuotedString ? "\"{0}\"" : "{0}";
1058 newExpressionText = string.Format(CultureInfo.InvariantCulture, formatString, serializableExpression.ConvertToString(context.ParserContext));
1062 //conversion failed - do nothing, let VB compiler take care of the expression
1067 Activity valueExpression = literal;
1069 if (null == valueExpression)
1071 if (!context.UseLocationExpression)
1073 //Compile for validation.
1074 valueExpression = VisualBasicDesignerHelper.CreatePrecompiledVisualBasicValue(context.ExpressionType, context.ExpressionText, context.ParserContext.Namespaces, context.ReferencedAssemblies, context.ParserContext, out expReturnType, out compileErrorMessages, out vbSettings);
1078 //Compile for validation.
1079 valueExpression = VisualBasicDesignerHelper.CreatePrecompiledVisualBasicReference(context.ExpressionType, context.ExpressionText, context.ParserContext.Namespaces, context.ReferencedAssemblies, context.ParserContext, out expReturnType, out compileErrorMessages, out vbSettings);
1082 ////It's possible the inferred type of expression is a dynamic type (e.g. delegate type), in this case it will cause serialization failure.
1083 ////To prevent this, we'll always convert the expression type to be object if the inferred type is in dynamic assembly and user doesn't specify any ExpressionType property
1084 if ((expReturnType.Assembly.IsDynamic) && (context.ExpressionType == null))
1086 ActivityWithResult originalExpression = valueExpression as ActivityWithResult;
1087 ActivityWithResult morphedExpression;
1088 if (ExpressionHelper.TryMorphExpression(originalExpression, ExpressionHelper.IsGenericLocationExpressionType(originalExpression), typeof(object), context.EditingContext, out morphedExpression))
1090 valueExpression = morphedExpression;
1095 return valueExpression;
1098 [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters, Justification = "Existing code")]
1099 void OnExpressionTypeChanged(DependencyPropertyChangedEventArgs e)
1101 //for independent expressions, when the type changes, we need to validate the expressions
1102 if (this.initialized
1103 && this.IsIndependentExpression
1104 && this.EditingState == EditingState.Idle)
1106 ValidateExpression(this);
1110 [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters, Justification = "Existing code")]
1111 void OnUseLocationExpressionChanged(DependencyPropertyChangedEventArgs e)
1113 //for independent expressions, when the type changes, we need to validate the expressions
1114 if (this.initialized
1115 && this.IsIndependentExpression
1116 && this.EditingState == EditingState.Idle
1117 && this.OwnerActivity != null)
1119 ValidateExpression(this);
1123 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
1124 Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")]
1125 [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
1126 Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")]
1127 void OnExpressionChanged(DependencyPropertyChangedEventArgs e)
1129 ModelItem oldExpression = e.OldValue as ModelItem;
1130 if (oldExpression != null)
1132 oldExpression.PropertyChanged -= this.OnExpressionModelItemChanged;
1134 ModelItem expression = e.NewValue as ModelItem;
1135 if (expression != null && expression != this.boundedExpression)
1137 expression.PropertyChanged += this.OnExpressionModelItemChanged;
1142 this.boundedExpression = expression;
1144 this.OnExpressionChanged();
1146 catch (Exception err)
1148 //if context is set - use error reporting
1149 if (null != this.Context)
1151 this.Context.Items.SetValue(new ErrorItem() { Message = err.Message, Details = err.ToString() });
1153 //otherwise - fallback to message box
1156 MessageBox.Show(err.ToString(), err.Message);
1162 void expressionModelItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
1164 ModelItem item = sender as ModelItem;
1167 if (e.PropertyName.Equals("ExpressionText", StringComparison.Ordinal))
1169 this.OnExpressionChanged();
1174 void OnPathToArgumentChanged(DependencyPropertyChangedEventArgs e)
1176 this.OnPathToArgumentChanged((string)e.NewValue);
1179 void OnIsIndependentExpressionChanged()
1181 //if this is an independent expression, we need to initialize the validation error because validation service will not validate it
1182 if (this.initialized
1183 && this.IsIndependentExpression
1184 && this.EditingState == EditingState.Idle)
1186 ValidateExpression(this);
1190 void UpdateValidationState()
1192 if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null)
1194 this.ValidationState = this.ValidationService.ValidationStateProperty.Getter(this.Expression);
1198 this.ValidationState = ValidationState.Valid;
1202 //We need to react to OnExpressionChanged, since there might be multiple ExpressionTextBoxes(ETB) associated to a single Expression.
1203 //All the ETBs should be updated if the value in any of the ETBs is changed.
1204 void OnExpressionChanged()
1206 if (this.HintText == SR.UnsupportedExpressionHintText)
1208 this.HintText = SR.ExpressionDefaultText;
1209 this.InitializeHintText();
1212 if (!this.internalModelItemChange)
1214 if (this.Expression == null)
1216 //new expression is null - there is no text, no previous text, erros should be clear as well as error message
1217 this.Text = string.Empty;
1218 this.previousText = this.Text;
1219 this.ExpressionText = null;
1220 this.ValidationState = ValidationState.Valid;
1224 this.UpdateValidationState();
1226 object expressionObject = this.Expression.GetCurrentValue();
1227 ActivityWithResult expression = expressionObject as ActivityWithResult;
1228 if (VisualBasicEditor.IsSupportedExpressionType(expression))
1230 String expressionString = null;
1231 //create parser context - do not pass ownerActivity - it might be null at this time
1232 ParserContext context = new ParserContext();
1233 expressionString = ExpressionHelper.GetExpressionString(expression, context);
1235 this.Text = expressionString;
1236 this.ExpressionText = expressionString;
1237 this.previousText = this.Text;
1238 this.IsSupportedExpression = true;
1242 this.Text = string.Empty;
1243 this.IsSupportedExpression = false;
1244 this.HintText = SR.UnsupportedExpressionHintText;
1247 this.isExpressionLoaded = true;
1248 if (this.isBeginEditPending)
1254 internalModelItemChange = false;
1257 private static bool IsSupportedExpressionType(object expressionObject)
1259 ActivityWithResult expression = expressionObject as ActivityWithResult;
1260 bool isSupported = false;
1261 if (expression != null)
1263 Type expressionType = expression.GetType();
1264 Type genericExpressionType = null;
1265 if (expressionType.IsGenericType)
1267 genericExpressionType = expressionType.GetGenericTypeDefinition();
1270 if (genericExpressionType == VisualBasicValueType ||
1271 genericExpressionType == VisualBasicReferenceType ||
1272 genericExpressionType == VariableReferenceType ||
1273 genericExpressionType == VariableValueType ||
1274 (genericExpressionType == LiteralType && ExpressionHelper.CanTypeBeSerializedAsLiteral(expression.ResultType)))
1283 void OnPathToArgumentChanged(string pathAsString)
1285 this.expressionModelProperty = null;
1286 this.expressionConverter = null;
1287 if (!string.IsNullOrEmpty(pathAsString) && null != this.OwnerActivity)
1289 string[] path = pathAsString.Split('.');
1290 if (path.Length > 0)
1292 this.expressionModelProperty = this.OwnerActivity.Properties[path[0]];
1293 for (int i = 1; i < path.Length; ++i)
1295 if (null != this.expressionModelProperty && null != this.expressionModelProperty.Value)
1297 this.expressionModelProperty = this.expressionModelProperty.Value.Properties[path[i]];
1301 this.expressionModelProperty = null;
1307 if (null != this.expressionModelProperty)
1309 this.expressionConverter = ((ModelPropertyImpl)this.expressionModelProperty).PropertyDescriptor.Converter;
1311 this.InitializeHintText();
1314 void InitializeHintText()
1316 DescriptionAttribute customHint = null;
1317 if (this.expressionModelProperty != null && this.expressionModelProperty.Attributes.Count != 0)
1319 customHint = this.expressionModelProperty.Attributes
1320 .OfType<DescriptionAttribute>()
1323 this.HintText = (null == customHint || string.IsNullOrEmpty(customHint.Description) ?
1324 (string.Equals(this.HintText, SR.ExpressionDefaultText) ? SR.ExpressionDefaultText : this.HintText) : customHint.Description);
1327 if (this.HintText != null)
1329 hint = this.HintText.Trim(new char[] { '<', '>' });
1330 this.SetValue(AutomationProperties.HelpTextProperty, hint);
1334 void InitializeContext(DependencyPropertyChangedEventArgs e)
1336 if (e.OldValue != null)
1338 // remove the OnValidationCompleted event handler on the old value
1339 ModelItem modelItem = (ModelItem)e.OldValue;
1340 if (modelItem != null)
1342 EditingContext context = modelItem.GetEditingContext();
1343 ValidationService validationService = context.Services.GetService<ValidationService>();
1344 if (validationService != null)
1346 validationService.ValidationCompleted -= this.OnValidationCompleted;
1352 if (null != this.OwnerActivity)
1354 if (this.ValidationService != null)
1356 this.ValidationService.ValidationCompleted += this.OnValidationCompleted;
1357 this.UpdateValidationState();
1362 #region Command Handlers
1364 public override bool CanCommit()
1366 string currentText = this.Text;
1367 if (this.expressionEditorInstance != null)
1369 currentText = this.expressionEditorInstance.Text ?? this.expressionEditorInstance.Text.Trim();
1371 else if (this.editingTextBox != null)
1373 currentText = this.editingTextBox.Text ?? this.editingTextBox.Text.Trim();
1376 //we dont need to commit change if currentText and previousText is the same
1377 //null and empty should be considered equal in this context
1379 return !string.Equals(currentText, this.previousText) &&
1380 !(string.IsNullOrEmpty(currentText) && string.IsNullOrEmpty(this.previousText));
1383 void OnExpressionTextBoxLoaded(object sender, RoutedEventArgs e)
1385 this.InitializeHintText();
1387 //if this is an independent expression, we need to initialize the validation error because validation service will not validate it
1388 if (this.IsIndependentExpression)
1390 ValidateExpression(this);
1393 this.initialized = true;
1396 void OnExpressionTextBoxUnloaded(object sender, RoutedEventArgs e)
1398 if (this.ValidationService != null)
1400 this.ValidationService.ValidationCompleted -= this.OnValidationCompleted;
1404 if (this.boundedExpression != null)
1406 this.boundedExpression.PropertyChanged -= this.OnExpressionModelItemChanged;
1410 public override void OnCompleteWordCommandCanExecute(CanExecuteRoutedEventArgs e)
1412 if (this.expressionEditorInstance != null)
1415 e.CanExecute = this.expressionEditorInstance.CanCompleteWord();
1424 public override void OnGlobalIntellisenseCommandCanExecute(CanExecuteRoutedEventArgs e)
1427 if (this.expressionEditorInstance != null)
1429 e.CanExecute = this.expressionEditorInstance.CanGlobalIntellisense();
1438 public override void OnParameterInfoCommandCanExecute(CanExecuteRoutedEventArgs e)
1441 if (this.expressionEditorInstance != null)
1443 e.CanExecute = this.expressionEditorInstance.CanParameterInfo();
1452 public override void OnQuickInfoCommandCanExecute(CanExecuteRoutedEventArgs e)
1454 if (this.expressionEditorInstance != null)
1456 e.CanExecute = this.expressionEditorInstance.CanQuickInfo();
1465 public override void OnIncreaseFilterLevelCommandCanExecute(CanExecuteRoutedEventArgs e)
1467 if (this.expressionEditorInstance != null)
1469 e.CanExecute = this.expressionEditorInstance.CanIncreaseFilterLevel();
1478 public override void OnDecreaseFilterLevelCommandCanExecute(CanExecuteRoutedEventArgs e)
1480 if (this.expressionEditorInstance != null)
1482 e.CanExecute = this.expressionEditorInstance.CanDecreaseFilterLevel();
1491 void OnCutCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
1493 if (this.expressionEditorInstance != null)
1495 e.CanExecute = this.expressionEditorInstance.CanCut();
1504 public override void OnCompleteWordCommandExecute(ExecutedRoutedEventArgs e)
1506 if (this.expressionEditorInstance != null)
1508 e.Handled = this.expressionEditorInstance.CompleteWord();
1517 public override void OnGlobalIntellisenseCommandExecute(ExecutedRoutedEventArgs e)
1519 if (this.expressionEditorInstance != null)
1521 e.Handled = this.expressionEditorInstance.GlobalIntellisense();
1530 public override void OnParameterInfoCommandExecute(ExecutedRoutedEventArgs e)
1532 if (this.expressionEditorInstance != null)
1534 e.Handled = this.expressionEditorInstance.ParameterInfo();
1543 public override void OnQuickInfoCommandExecute(ExecutedRoutedEventArgs e)
1545 if (this.expressionEditorInstance != null)
1547 e.Handled = this.expressionEditorInstance.QuickInfo();
1556 public override void OnDecreaseFilterLevelCommandExecute(ExecutedRoutedEventArgs e)
1558 if (this.expressionEditorInstance != null)
1560 e.Handled = this.expressionEditorInstance.DecreaseFilterLevel();
1569 public override void OnIncreaseFilterLevelCommandExecute(ExecutedRoutedEventArgs e)
1571 if (this.expressionEditorInstance != null)
1573 e.Handled = this.expressionEditorInstance.IncreaseFilterLevel();
1582 void OnCutCommandExecute(object sender, ExecutedRoutedEventArgs e)
1584 if (this.expressionEditorInstance != null)
1586 e.Handled = this.expressionEditorInstance.Cut();
1594 void OnCopyCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
1596 if (this.expressionEditorInstance != null)
1598 e.CanExecute = this.expressionEditorInstance.CanCopy();
1607 void OnCopyCommandExecute(object sender, ExecutedRoutedEventArgs e)
1609 if (this.expressionEditorInstance != null)
1611 e.Handled = this.expressionEditorInstance.Copy();
1619 void OnPasteCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
1621 if (this.expressionEditorInstance != null)
1623 e.CanExecute = this.expressionEditorInstance.CanPaste();
1632 void OnPasteCommandExecute(object sender, ExecutedRoutedEventArgs e)
1634 if (this.expressionEditorInstance != null)
1636 e.Handled = this.expressionEditorInstance.Paste();
1644 void OnUndoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
1646 if (this.expressionEditorInstance != null)
1648 e.CanExecute = this.expressionEditorInstance.CanUndo();
1657 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
1658 Justification = "Catch all execeptions to prevent crash.")]
1659 [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
1660 Justification = "Catch all execeptions to prevent crash.")]
1661 void OnUndoCommandExecute(object sender, ExecutedRoutedEventArgs e)
1663 if (this.expressionEditorInstance != null)
1667 e.Handled = this.expressionEditorInstance.Undo();
1669 catch (Exception ex)
1671 MessageBox.Show(ex.Message);
1680 void OnRedoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
1682 if (this.expressionEditorInstance != null)
1684 e.CanExecute = this.expressionEditorInstance.CanRedo();
1693 void OnRedoCommandExecute(object sender, ExecutedRoutedEventArgs e)
1695 if (this.expressionEditorInstance != null)
1697 e.Handled = this.expressionEditorInstance.Redo();
1705 void OnHelpExecute(object sender, ExecutedRoutedEventArgs e)
1707 IIntegratedHelpService help = this.Context.Services.GetService<IIntegratedHelpService>();
1710 help.ShowHelpFromKeyword(HelpKeywords.ExpressionEditorPage);
1714 System.Diagnostics.Process.Start(SR.DefaultHelpUrl);
1720 void OnHintTextChanged(DependencyPropertyChangedEventArgs e)
1722 if (!string.Equals(SR.ExpressionDefaultText, e.NewValue) && !string.Equals(e.OldValue, e.NewValue))
1724 this.InitializeHintText();
1728 void OnAcceptsReturnChanged(DependencyPropertyChangedEventArgs e)
1730 if (this.expressionEditorInstance != null)
1732 this.expressionEditorInstance.AcceptsReturn = (bool)e.NewValue;
1736 void OnAcceptsTabChanged(DependencyPropertyChangedEventArgs e)
1738 if (this.expressionEditorInstance != null)
1740 this.expressionEditorInstance.AcceptsTab = (bool)e.NewValue;
1744 void StartValidator()
1746 if (this.validator == null)
1748 this.validator = new BackgroundWorker();
1749 this.validator.WorkerReportsProgress = true;
1750 this.validator.WorkerSupportsCancellation = true;
1752 this.validator.DoWork += delegate(object obj, DoWorkEventArgs args)
1754 BackgroundWorker worker = obj as BackgroundWorker;
1755 if (worker.CancellationPending)
1760 ExpressionValidationContext validationContext = args.Argument as ExpressionValidationContext;
1761 if (validationContext != null)
1763 string errorMessage;
1764 if (DoValidation(validationContext, out errorMessage))
1766 worker.ReportProgress(0, errorMessage);
1770 if (worker.CancellationPending)
1776 Thread.Sleep(ValidationWaitTime);
1777 args.Result = validationContext;
1782 this.validator.RunWorkerCompleted += delegate(object obj, RunWorkerCompletedEventArgs args)
1784 if (!args.Cancelled)
1786 ExpressionValidationContext validationContext = args.Result as ExpressionValidationContext;
1787 if (validationContext != null)
1789 Dispatcher.BeginInvoke(new Action<ExpressionValidationContext>((target) =>
1791 //validator could be null by the time we try to validate again or
1792 //if it's already busy
1793 if (this.validator != null && !this.validator.IsBusy)
1795 target.Update(this);
1796 this.validator.RunWorkerAsync(target);
1798 }), validationContext);
1803 this.validator.ProgressChanged += delegate(object obj, ProgressChangedEventArgs args)
1805 string error = args.UserState as string;
1806 Dispatcher.BeginInvoke(new Action<string>(UpdateValidationError), error);
1809 this.validator.RunWorkerAsync(new ExpressionValidationContext(this));
1813 //perform one validation synchronously
1814 //return value indicates whether errorMessage is updated.
1815 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
1816 Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")]
1817 [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
1818 Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")]
1819 bool DoValidation(ExpressionValidationContext validationContext, out string errorMessage)
1821 Fx.Assert(validationContext != null, "only work when context is not null");
1822 errorMessage = null;
1825 //if the text is empty we clear the error message
1826 if (string.IsNullOrEmpty(validationContext.ExpressionText))
1828 errorMessage = null;
1831 // if the expression text is different from the last time we run the validation we run the validation
1832 else if (!string.Equals(validationContext.ExpressionText, validationContext.ValidatedExpressionText))
1834 Type expReturnType = null;
1835 string newExpressionText = null;
1836 SourceExpressionException compileErrorMessages = null;
1837 VisualBasicSettings settings = null;
1840 CreateVBExpression(validationContext, out newExpressionText, out expReturnType, out compileErrorMessages, out settings);
1841 if (compileErrorMessages != null)
1843 errorMessage = compileErrorMessages.Message;
1846 catch (Exception err)
1848 errorMessage = err.Message;
1857 void UpdateValidationError(string errorMessage)
1859 if (!string.IsNullOrEmpty(errorMessage))
1862 this.HasValidationError = true;
1863 this.ValidationErrorMessage = errorMessage;
1867 this.HasValidationError = false;
1868 this.ValidationErrorMessage = null;
1872 private class ExpressionValidationContext
1874 internal ParserContext ParserContext { get; set; }
1875 internal Type ExpressionType { get; set; }
1876 internal String ExpressionText { get; set; }
1877 internal EditingContext EditingContext { get; set; }
1878 internal String ValidatedExpressionText { get; set; }
1879 internal bool UseLocationExpression { get; set; }
1881 internal ExpressionValidationContext(VisualBasicEditor etb)
1886 internal void Update(VisualBasicEditor etb)
1888 Fx.Assert(etb.OwnerActivity != null, "Owner Activity is null");
1889 this.EditingContext = etb.OwnerActivity.GetEditingContext();
1891 //setup ParserContext
1892 this.ParserContext = new ParserContext(etb.OwnerActivity)
1894 //callee is a ExpressionTextBox
1896 //pass property descriptor belonging to epression's model property (if one exists)
1897 PropertyDescriptor = (null != etb.expressionModelProperty ? ((ModelPropertyImpl)etb.expressionModelProperty).PropertyDescriptor : null),
1900 this.ExpressionType = etb.ExpressionType;
1901 this.ValidatedExpressionText = this.ExpressionText;
1902 if (etb.expressionEditorInstance != null)
1904 this.ExpressionText = etb.expressionEditorInstance.Text;
1906 else if (etb.editingTextBox != null)
1908 this.ExpressionText = etb.editingTextBox.Text;
1912 this.ExpressionText = etb.Text;
1914 this.UseLocationExpression = etb.UseLocationExpression;
1917 internal IEnumerable<string> ReferencedAssemblies
1921 Fx.Assert(this.EditingContext != null, "ModelItem.Context = null");
1922 AssemblyContextControlItem assemblyContext = this.EditingContext.Items.GetValue<AssemblyContextControlItem>();
1923 if (assemblyContext != null)
1925 return assemblyContext.AllAssemblyNamesInContext;
1932 private static List<ModelItem> GetVariablesInScopeWithShadowing(ModelItem ownerActivity, bool includeArguments)
1934 List<ModelItem> variablesInScope = new List<ModelItem>();
1935 if (ownerActivity != null)
1937 HashSet<string> variableNames = new HashSet<string>();
1938 ModelItem currentItem = ownerActivity;
1939 Func<ModelItem, bool> filterDelegate = new Func<ModelItem, bool>((variable) =>
1941 string variableName = (string)variable.Properties["Name"].ComputedValue;
1942 if (variableName == null)
1948 return !variableNames.Contains(variableName.ToUpperInvariant());
1952 while (currentItem != null)
1954 List<ModelItem> variables = new List<ModelItem>();
1955 ModelItemCollection variablesCollection = currentItem.GetVariableCollection();
1956 if (variablesCollection != null)
1958 variables.AddRange(variablesCollection);
1960 variables.AddRange(currentItem.FindActivityDelegateArguments());
1962 // For the variables defined at the same level, shadowing doesn't apply. If there're multiple variables defined at the same level
1963 // have duplicate names when case is ignored, all of these variables should bee added as variables in scope and let validation reports
1964 // ambiguous reference error. So that we need to scan all variables defined at the same level first and then add names to the HashSet.
1965 IEnumerable<ModelItem> filteredVariables = variables.Where<ModelItem>(filterDelegate);
1966 variablesInScope.AddRange(filteredVariables);
1967 foreach (ModelItem variable in filteredVariables)
1969 variableNames.Add(((string)variable.Properties["Name"].ComputedValue).ToUpperInvariant());
1972 currentItem = currentItem.Parent;
1975 if (includeArguments)
1977 List<ModelItem> arguments = VisualBasicEditor.GetVariablesForArguments(ownerActivity.Root);
1978 variablesInScope.AddRange(arguments.Where<ModelItem>(filterDelegate));
1982 return variablesInScope;
1985 private static List<ModelItem> GetVariablesForArguments(ModelItem modelItem)
1987 List<ModelItem> arguments = new List<ModelItem>();
1988 //if expression editor is loaded in the WF which is hosted within ActivityBuilder, there is a need to pickup defined arguments
1989 //and feed them as variables, so intellisense can include them
1990 if (null != modelItem && ActivityBuilderHelper.IsActivityBuilderType(modelItem))
1992 ModelTreeManager treeManager = ((IModelTreeItem)modelItem).ModelTreeManager;
1993 //call ActivityBuilderHelper.GetVariables - it will create a collection of Variable - each variable corresponds to a specific argument
1995 ActivityBuilderHelper.GetVariables(modelItem)
1996 //create a fake model item implementation - there is no need to store that model item anywhere in the model tree, it is required
1997 //of the expression editor interface to pass instances of model items wrapping variables, rather than actual variables
1998 .Select<Variable, ModelItem>(entry => new FakeModelItemImpl(treeManager, typeof(Variable), entry, null)));
2004 internal static List<ModelItem> GetVariablesInScope(ModelItem ownerActivity)
2006 List<ModelItem> declaredVariables = new List<ModelItem>();
2007 if (ownerActivity != null)
2009 bool includeArguments = !(ownerActivity.GetCurrentValue() is ActivityBuilder);
2010 FrameworkName targetFramework = WorkflowDesigner.GetTargetFramework(ownerActivity.GetEditingContext());
2011 if ((targetFramework != null) && (targetFramework.IsLessThan45()))
2013 declaredVariables.AddRange(VariableHelper.FindVariablesInScope(ownerActivity));
2014 declaredVariables.AddRange(VariableHelper.FindActivityDelegateArgumentsInScope(ownerActivity));
2015 if (includeArguments)
2017 declaredVariables.AddRange(VisualBasicEditor.GetVariablesForArguments(ownerActivity.Root));
2022 declaredVariables.AddRange(VisualBasicEditor.GetVariablesInScopeWithShadowing(ownerActivity, includeArguments));
2026 return declaredVariables;
2030 internal sealed class LineToHeightConverter : IMultiValueConverter
2032 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
2034 double convertedValue = Double.NaN;
2035 bool isDefault = true;
2037 // Calculate the height for the textblock as ExpressionTextBox exposes lines properties,
2038 // and TextBlock doesn't have lines properties.
2039 FontFamily fontFamily = values.OfType<FontFamily>().FirstOrDefault();
2040 int lines = values.OfType<int>().FirstOrDefault();
2041 double[] doubleArray = values.OfType<double>().ToArray<double>();
2043 if (doubleArray.Length == 2)
2045 double height = doubleArray[0]; // The first element of the array is going to be the height
2046 double fontSize = doubleArray[1]; // The seconed element of the array is going to be the fontSize
2048 // 0.0 is default for MinHeight, PositiveInfinity is default for MaxHeight
2049 if (string.Equals(parameter as string, "MinHeight"))
2051 isDefault = (height == 0.0);
2053 else if (string.Equals(parameter as string, "MaxHeight"))
2055 isDefault = (double.IsPositiveInfinity(height));
2058 // If the height value we are evaluating is default, use Lines for sizing...
2059 // If no heights (height or lines) have been explicitly specified, we would rather default the height
2060 // as if the Line was 1 - so use the line heights, rather than 0.0 and/or PositiveInfinity.
2063 double lineHeight = fontSize * fontFamily.LineSpacing;
2065 if (fontFamily != null)
2067 convertedValue = lineHeight * (double)lines + 4;
2072 convertedValue = height;
2076 return convertedValue;
2079 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
2081 throw FxTrace.Exception.AsError(new NotSupportedException());
2085 internal sealed class ValidationStateToErrorConverter : IMultiValueConverter
2088 #region IMultiValueConverter Members
2090 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
2092 VisualBasicEditor etb = values[0] as VisualBasicEditor;
2093 if (values[0] == DependencyProperty.UnsetValue || etb == null)
2097 return etb.HasErrors;
2100 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
2102 throw FxTrace.Exception.AsError(new NotImplementedException());
2108 internal sealed class ValidationErrorMessageConverter : IMultiValueConverter
2110 #region IMultiValueConverter Members
2112 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
2114 VisualBasicEditor etb = values[0] as VisualBasicEditor;
2115 if (values[0] == DependencyProperty.UnsetValue || etb == null)
2119 return etb.ErrorMessage;
2122 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
2124 throw FxTrace.Exception.AsError(new NotImplementedException());
2130 internal sealed class TypeToPromptTextConverter : IValueConverter
2132 #region IValueConverter Members
2134 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
2136 return TypeToPromptTextConverter.GetPromptText(value);
2139 internal static string GetPromptText(object value)
2141 Type expressionType = value as Type;
2142 if (value == DependencyProperty.UnsetValue || expressionType == null || !expressionType.IsValueType)
2148 return Activator.CreateInstance(expressionType).ToString();
2152 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
2154 throw FxTrace.Exception.AsError(new NotSupportedException());
2161 public enum EditingState