Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / Tools / System.Activities.Presentation / System / Activities / Presentation / View / ExpressionTextBox.xaml.cs / 1590394 / ExpressionTextBox.xaml.cs
//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------- namespace System.Activities.Presentation.View { using System.Activities.Presentation.Internal.PropertyEditing; using System.Activities.Presentation.Model; using System.Activities.Presentation.Services; using System.Activities.Presentation.Validation; using System.Activities.Presentation.Hosting; using System.Activities.Presentation.Xaml; using System.Activities.ExpressionParser; using System.Activities.Expressions; using System.Activities.XamlIntegration; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Runtime; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using Microsoft.VisualBasic.Activities; using System.Windows.Threading; using System.Threading; //This control is used for expression editing in activity designers. //It uses the ActivityTypeConverter to convert between a Activity and its string representation. //It defines 3 dependency properties - OwnerActivity, ExpressionModelItem and ExpressionType. //ActivityModelItem is used to create the parser context required by the TypeConverter. //ExpressionType is the type of the expression associated with this text box. This is required by the TypeConverter. public sealed partial class ExpressionTextBox : ContentControl { internal static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnTextChanged), new CoerceValueCallback(OnTextCoerceValue))); public static readonly DependencyProperty HintTextProperty = DependencyProperty.Register("HintText", typeof(string), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(SR.ExpressionDefaultText, new PropertyChangedCallback(OnHintTextChanged))); internal static readonly DependencyProperty ValidationStateProperty = DependencyProperty.Register("ValidationState", typeof(ValidationState), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(ValidationState.Valid)); public static readonly DependencyProperty ExpressionProperty = DependencyProperty.Register("Expression", typeof(ModelItem), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnExpressionChanged))); public static readonly DependencyProperty ExpressionTypeProperty = DependencyProperty.Register("ExpressionType", typeof(Type), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnExpressionTypeChanged))); public static readonly DependencyProperty OwnerActivityProperty = DependencyProperty.Register("OwnerActivity", typeof(ModelItem), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnOwnerModelItemChanged))); public static readonly DependencyProperty UseLocationExpressionProperty = DependencyProperty.Register("UseLocationExpression", typeof(bool), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(false, OnUseLocationExpressionChanged)); public static readonly DependencyProperty PathToArgumentProperty = DependencyProperty.Register("PathToArgument", typeof(string), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, OnPathToArgumentChanged)); public static readonly DependencyProperty IsSupportedExpressionProperty = DependencyProperty.Register("IsSupportedExpression", typeof(bool), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(true)); public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = DependencyProperty.Register("VerticalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(ExpressionTextBox), new PropertyMetadata(ScrollBarVisibility.Hidden)); public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = DependencyProperty.Register("HorizontalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(ExpressionTextBox), new PropertyMetadata(ScrollBarVisibility.Hidden)); public static readonly DependencyProperty MaxLinesProperty = DependencyProperty.Register("MaxLines", typeof(int), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(Int32.MaxValue, new PropertyChangedCallback(OnLinesChanged))); public static readonly DependencyProperty MinLinesProperty = DependencyProperty.Register("MinLines", typeof(int), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(1, new PropertyChangedCallback(OnLinesChanged))); public static readonly DependencyProperty ExplicitCommitProperty = DependencyProperty.Register("ExplicitCommit", typeof(bool), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(false)); public static readonly DependencyProperty DefaultValueProperty = DependencyProperty.Register("DefaultValue", typeof(string), typeof(ExpressionTextBox), new PropertyMetadata(null)); public static readonly DependencyProperty AcceptsReturnProperty = DependencyProperty.Register("AcceptsReturn", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(true, OnAcceptsReturnChanged)); public static readonly DependencyProperty AcceptsTabProperty = DependencyProperty.Register("AcceptsTab", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(false, OnAcceptsTabChanged)); public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(false)); internal static readonly DependencyProperty EditingStateProperty = DependencyProperty.Register("EditingState", typeof(EditingState), typeof(ExpressionTextBox), new PropertyMetadata(EditingState.Idle)); internal static readonly DependencyProperty HasValidationErrorProperty = DependencyProperty.Register("HasValidationError", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(false)); internal static readonly DependencyProperty ValidationErrorMessageProperty = DependencyProperty.Register("ValidationErrorMessage", typeof(string), typeof(ExpressionTextBox), new PropertyMetadata(null)); internal static readonly DependencyProperty ExpressionTextProperty = DependencyProperty.Register("ExpressionText", typeof(string), typeof(ExpressionTextBox), new PropertyMetadata(null)); internal static readonly DependencyProperty IsIndependentExpressionProperty = DependencyProperty.Register("IsIndependentExpression", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(new PropertyChangedCallback(OnIsIndependentExpressionChanged))); public static readonly RoutedEvent EditorLostLogicalFocusEvent = EventManager.RegisterRoutedEvent("EditorLostLogicalFocus", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand CompleteWordCommand = new RoutedCommand("CompleteWordCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand GlobalIntellisenseCommand = new RoutedCommand("GlobalIntellisenseCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand ParameterInfoCommand = new RoutedCommand("ParameterInfoCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand QuickInfoCommand = new RoutedCommand("QuickInfoCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand IncreaseFilterLevelCommand = new RoutedCommand("IncreaseFilterLevelCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand DecreaseFilterLevelCommand = new RoutedCommand("DecreaseFilterLevelCommand", typeof(ExpressionTextBox)); bool internalModelItemChange = false; string previousText = null; EditingContext context; ModelProperty expressionModelProperty; TypeConverter expressionConverter; bool initialized = false; IExpressionEditorService expressionEditorService; IExpressionEditorInstance expressionEditorInstance; Control hostControl; string editorName; double blockHeight = double.NaN; double blockWidth = double.NaN; bool isExpressionLoaded = false; bool isBeginEditPending = false; DesignerPerfEventProvider perfProvider; ModelItem boundedExpression = null; BackgroundWorker validator = null; const int ValidationWaitTime = 800; PropertyChangedEventHandler onExpressionModelItemChangedHandler; public ExpressionTextBox() { InitializeComponent(); this.MinHeight = this.FontSize + 4; /* 4 pixels for border*/ this.context = null; this.editorName = null; this.ContentTemplate = (DataTemplate)FindResource("textblock"); this.Loaded += this.OnExpressionTextBoxLoaded; this.Unloaded += this.OnExpressionTextBoxUnloaded; CommandBinding CommitBinding = new CommandBinding(DesignerView.CommitCommand, OnCommitCommandExecute, OnCommitCommandCanExecute); this.CommandBindings.Add(CommitBinding); } public event RoutedEventHandler EditorLostLogicalFocus { add { this.AddHandler(ExpressionTextBox.EditorLostLogicalFocusEvent, value); } remove { this.RemoveHandler(ExpressionTextBox.EditorLostLogicalFocusEvent, value); } } PropertyChangedEventHandler OnExpressionModelItemChanged { get { if (this.onExpressionModelItemChangedHandler == null) { this.onExpressionModelItemChangedHandler = new PropertyChangedEventHandler(this.expressionModelItem_PropertyChanged); } return this.onExpressionModelItemChangedHandler; } } protected override void OnContextMenuOpening(ContextMenuEventArgs e) { e.Handled = true; } protected override void OnPreviewMouseRightButtonDown(MouseButtonEventArgs e) { base.OnPreviewMouseRightButtonDown(e); e.Handled = true; } static object OnTextCoerceValue(DependencyObject dp, object value) { string tempText = value as string; ExpressionTextBox etb = dp as ExpressionTextBox; if (etb != null) { if (tempText != null) { tempText = tempText.Trim(); } } return tempText; } protected override AutomationPeer OnCreateAutomationPeer() { return new ExpressionTextBoxAutomationPeer(this); } protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { base.OnLostKeyboardFocus(e); if (this.expressionEditorInstance != null && (this.expressionEditorInstance.HasAggregateFocus || (this.hostControl != null && this.hostControl.IsFocused))) { return; } DoLostFocus(); } private void DoLostFocus() { KillValidator(); ValidateExpression(this); if (this.Context != null) { // Unselect if this is the currently selected one. ExpressionSelection current = this.Context.Items.GetValue (); if (current != null && current.ModelItem == this.Expression) { ExpressionSelection emptySelection = new ExpressionSelection(null); this.Context.Items.SetValue(emptySelection); } } // Generate and validate the expression. // Get the text from the snapshot and set it to the Text property if (this.expressionEditorInstance != null) { this.expressionEditorInstance.ClearSelection(); } bool committed = false; if (!this.ExplicitCommit) { //commit change and let the commit change code do the revert committed = Commit(false); //reset the error icon if we didn't get to set it in the commit if (!committed || this.IsIndependentExpression) { this.EditingState = EditingState.Idle; // Switch the control back to a textbox - // but give it the text from the editor (textbox should be bound to the Text property, so should // automatically be filled with the correct text, from when we set the Text property earlier) if (!this.ContentTemplate.Equals((DataTemplate)FindResource("textblock"))) { this.ContentTemplate = (DataTemplate)FindResource("textblock"); } } } //raise EditorLostLogical focus - in case when some clients need to do explicit commit this.RaiseEvent(new RoutedEventArgs(ExpressionTextBox.EditorLostLogicalFocusEvent, this)); } private void KillValidator() { if (validator != null) { this.validator.CancelAsync(); this.validator.Dispose(); this.validator = null; } } internal static bool ShouldGenerateExpression(string oldText, string newText) { return newText != null && !string.Equals(newText, oldText) && !(oldText == null && newText.Equals(string.Empty)); } private bool Commit(bool isExplicitCommit) { bool committed = false; //only generate and validate the expression when when we don't require explicit commit change //or when the commit is explicit if (!this.ExplicitCommit || isExplicitCommit) { // Generate and validate the expression. // Get the text from the snapshot and set it to the Text property this.previousText = null; // In VS if (this.expressionEditorInstance != null) { this.previousText = this.Text; this.Text = this.expressionEditorInstance.GetCommittedText(); } // In rehost else { if (this.Expression != null) { Activity expression = this.Expression.GetCurrentValue() as Activity; // if expression is null, GetExpressionString will return null this.previousText = ExpressionHelper.GetExpressionString(expression, this.OwnerActivity); } else { this.previousText = null; } } // If the Text is null, or equal to the previous value, or changed from null to empty, don't bother generating the expression // We still need to generate the expression when it is changed from other value to EMPTY however - otherwise // the case where you had an expression (valid or invalid), then deleted the whole thing will not be evaluated. if (ShouldGenerateExpression(this.previousText, this.Text)) { GenerateExpression(); committed = true; } } if (!this.ContentTemplate.Equals((DataTemplate)FindResource("textblock"))) { this.ContentTemplate = (DataTemplate)FindResource("textblock"); } return committed; } protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnPreviewMouseLeftButtonDown(e); if (this.Context != null) { ExpressionSelection expressionSelection = new ExpressionSelection(this.Expression); this.Context.Items.SetValue(expressionSelection); } } public void BeginEdit() { //am i loaded? is current template a textblock? if (this.isExpressionLoaded || null == this.Expression) { this.isBeginEditPending = false; this.IsReadOnly = false; if (this.IsLoaded && this.ContentTemplate.Equals(this.FindResource("textblock"))) { //get control's content presenter ContentPresenter presenter = VisualTreeUtils.GetTemplateChild (this); if (null != presenter) { //and look for the loaded textblock TextBlock tb = (TextBlock)this.ContentTemplate.FindName("expresionTextBlock", presenter); if (null != tb) { //now - give focus to the textblock - it will trigger OnGotTextBlockFocus event, which eventually //swithc ETB into expression editing mode. tb.Focus(); } } } } else { this.isBeginEditPending = true; } } internal bool HasAggregateFocus() { bool result = false; if (this.IsLoaded) { if (this.expressionEditorInstance != null) { result = (this.hostControl != null && this.hostControl.IsFocused) || this.expressionEditorInstance.HasAggregateFocus; } else { result = !this.IsKeyboardFocused && this.IsKeyboardFocusWithin; } } return result; } void OnTextBlockMouseLeftButtonDown(object sender, RoutedEventArgs e) { if (!this.IsReadOnly) { TextBlock textBlock = sender as TextBlock; if (textBlock != null) { Keyboard.Focus(textBlock); } } } void OnGotTextBlockFocus(object sender, RoutedEventArgs e) { if (this.Context == null) { return; } DesignerView designerView = this.Context.Services.GetService (); if (!designerView.IsMultipleSelectionMode) { TextBlock textBlock = sender as TextBlock; bool isInReadOnlyMode = this.IsReadOnly; if (this.Context != null) { ReadOnlyState readOnlyState = this.Context.Items.GetValue (); isInReadOnlyMode |= readOnlyState.IsReadOnly; } if (null != textBlock) { this.blockHeight = textBlock.ActualHeight; this.blockHeight = Math.Max(this.blockHeight, textBlock.MinHeight); this.blockHeight = Math.Min(this.blockHeight, textBlock.MaxHeight); this.blockWidth = textBlock.ActualWidth; this.blockWidth = Math.Max(this.blockWidth, textBlock.MinWidth); this.blockWidth = Math.Min(this.blockWidth, textBlock.MaxWidth); // 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) // Also don't create editor when we are in read only mode if (this.ContentTemplate.Equals((DataTemplate)FindResource("textblock")) && !isInReadOnlyMode) { if (this.Context != null) { // Get the ExpressionEditorService this.expressionEditorService = this.Context.Services.GetService (); } // If the service exists, use the editor template - else switch to the textbox template if (this.expressionEditorService != null) { this.PerfProvider.WorkflowDesignerExpressionEditorLoadStart(); this.ContentTemplate = (DataTemplate)FindResource("editor"); } else { this.ContentTemplate = (DataTemplate)FindResource("textbox"); } } } if (!isInReadOnlyMode) { //disable the error icon this.StartValidator(); this.EditingState = EditingState.Editing; e.Handled = true; } } } void OnGotEditingFocus(object sender, RoutedEventArgs e) { //disable the error icon this.EditingState = EditingState.Editing; this.StartValidator(); } // This method is called when the editor data template is loaded - when the editor data template // is loaded, create the editor session and the expression editor [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "CreateExpressionEditor is part of a public API. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "CreateExpressionEditor is part of a public API. Propagating exceptions might lead to VS crash.")] void OnEditorLoaded(object sender, RoutedEventArgs e) { // If the service exists, create an expression editor and add it to the grid - else switch to the textbox data template if (this.expressionEditorService != null) { Border border = (Border)sender; // Get the references and variables in scope AssemblyContextControlItem assemblies = (AssemblyContextControlItem)this.Context.Items.GetValue(typeof(AssemblyContextControlItem)); List declaredVariables = VariableHelper.FindUniqueVariablesInScope(this.OwnerActivity); declaredVariables.AddRange(VariableHelper.FindActionHandlerVariables(this.OwnerActivity)); //if expression editor is loaded in the WF which is hosted within ActivityBuilderHelper, there is a need to pickup defined arguments //and feed them as variables, so intelisense can include them if (null != this.OwnerActivity && ActivityBuilderHelper.IsActivityBuilderType(this.OwnerActivity.Root)) { ModelTreeManager treeManager = ((IModelTreeItem)this.OwnerActivity).ModelTreeManager; //call ActivityBuilderHelper.GetVariables - it will create a collection of Variable - each variable corresponds to a specific argument declaredVariables.AddRange( ActivityBuilderHelper.GetVariables(this.OwnerActivity.Root). //create a fake model item implementation - there is no need to store that model item anywhere in the model tree, it is requirement //of the expression editor interface to pass instances of model items wrapping variables, rather than actual variables Select (entry => new FakeModelItemImpl(treeManager, typeof(Variable), entry, null))); } ImportedNamespaceContextItem importedNamespaces = this.Context.Items.GetValue (); importedNamespaces.EnsureInitialized(this.Context); //if the expression text is empty and the expression type is set, then we initialize the text to prompt text if (String.Equals(this.ExpressionText, string.Empty, StringComparison.OrdinalIgnoreCase) && this.ExpressionType != null) { this.Text = TypeToPromptTextConverter.GetPromptText(this.ExpressionType); } //this is a hack this.blockWidth = Math.Max(this.ActualWidth - 8, 0); //8 is the margin if (this.HasErrors) { this.blockWidth = Math.Max(this.blockWidth - 16, 0); //give 16 for error icon } try { if (this.ExpressionType != null) { this.expressionEditorInstance = this.expressionEditorService.CreateExpressionEditor(assemblies, importedNamespaces, declaredVariables, this.Text, this.ExpressionType, new Size(this.blockWidth, this.blockHeight)); } else { this.expressionEditorInstance = this.expressionEditorService.CreateExpressionEditor(assemblies, importedNamespaces, declaredVariables, this.Text, new Size(this.blockWidth, this.blockHeight)); } } catch (Exception ex) { Trace.WriteLine(ex.Message); } if (this.expressionEditorInstance != null) { try { this.expressionEditorInstance.VerticalScrollBarVisibility = this.VerticalScrollBarVisibility; this.expressionEditorInstance.HorizontalScrollBarVisibility = this.HorizontalScrollBarVisibility; this.expressionEditorInstance.AcceptsReturn = this.AcceptsReturn; this.expressionEditorInstance.AcceptsTab = this.AcceptsTab; // Add the expression editor to the text panel, at column 1 this.hostControl = this.expressionEditorInstance.HostControl; // Subscribe to this event to change scrollbar visibility on the fly for auto, and to resize the hostable editor // as necessary this.expressionEditorInstance.LostAggregateFocus += new EventHandler(OnEditorLostAggregateFocus); this.expressionEditorInstance.Closing += new EventHandler(OnEditorClosing); // Set up Hostable Editor properties this.expressionEditorInstance.MinLines = this.MinLines; this.expressionEditorInstance.MaxLines = this.MaxLines; this.expressionEditorInstance.HostControl.Style = (Style)FindResource("editorStyle"); border.Child = this.hostControl; this.expressionEditorInstance.Focus(); } catch (KeyNotFoundException ex) { Debug.Fail("Unable to find editor with the following editor name: " + this.editorName, ex.Message); } } } if (this.expressionEditorInstance == null) { this.ContentTemplate = (DataTemplate)FindResource("textbox"); } this.PerfProvider.WorkflowDesignerExpressionEditorLoaded(); } void OnEditorClosing(object sender, EventArgs e) { if (this.expressionEditorInstance != null) { //these events are expected to be unregistered during lost focus event, but //we are unregistering them during unload just in case. Ideally we want to //do this in the CloseExpressionEditor method this.expressionEditorInstance.LostAggregateFocus -= new EventHandler(OnEditorLostAggregateFocus); this.expressionEditorInstance.Closing -= new EventHandler(OnEditorClosing); this.expressionEditorInstance = null; } Border boarder = this.hostControl.Parent as Border; if (boarder != null) { boarder.Child = null; } this.hostControl = null; this.editorName = null; } void OnEditorLostAggregateFocus(object sender, EventArgs e) { this.DoLostFocus(); } //void BindEditorProperties() //{ // this.hostControl.SetBinding(Control.ContextMenuProperty, "ContextMenu"); // this.hostControl.SetBinding(Control.FlowDirectionProperty, "FlowDirection"); // this.hostControl.SetBinding(Control.FontFamilyProperty, "FontFamily"); // this.hostControl.SetBinding(Control.FontSizeProperty, "FontSize"); // this.hostControl.SetBinding(Control.FontStretchProperty, "FontStretch"); // this.hostControl.SetBinding(Control.FontStyleProperty, "FontStyle"); // this.hostControl.SetBinding(Control.FontWeightProperty, "FontWeight"); // this.hostControl.SetBinding(Control.HeightProperty, "Height"); // this.hostControl.SetBinding(Control.LanguageProperty, "Language"); // this.hostControl.SetBinding(Control.SnapsToDevicePixelsProperty, "SnapsToDevicePixels"); //} // This method is called when the editor data template is unloaded - when the editor data template // is unloaded, close the editor session and set the expression editor and editor session to null void OnEditorUnloaded(object sender, RoutedEventArgs e) { // Blank the editorSession and the expressionEditor so as not to use up memory // Destroy both as you can only ever spawn one editor per session if (this.expressionEditorInstance != null) { //if we are unloaded during editing, this means we got here by someone clicking breadcrumb, we should try to commit if (this.EditingState == EditingState.Editing) { this.Commit(false); } this.expressionEditorInstance.Close(); } } // This method is to give focus and set the caret position when the TextBox DataTemplate is loaded void OnTextBoxLoaded(object sender, RoutedEventArgs e) { TextBox textbox = (TextBox)sender; textbox.ContextMenu = null; //to workaround a but in the TextBox layout code Binding binding = new Binding(); binding.Source = this; binding.Path = new PropertyPath(ExpressionTextBox.TextProperty); binding.Mode = BindingMode.TwoWay; binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; textbox.SetBinding(TextBox.TextProperty, binding); // Set the cursor to correct text position int index = GetCharacterIndexFromPoint(textbox); textbox.SelectionStart = index; textbox.SelectionLength = 0; textbox.Focus(); } // This method is to workaround the fact that textbox.GetCharacterIndexFromPoint returns the caret // to the left of the character... Thus you can never get the caret after the last character in the // expression string. int GetCharacterIndexFromPoint(TextBox textbox) { Point position = Mouse.GetPosition(textbox); int index = textbox.GetCharacterIndexFromPoint(position, false); if (index < 0) { // May have clicked outside the text area, get the index of nearest char index = textbox.GetCharacterIndexFromPoint(position, true); if (index < 0) { index = 0; } // Adjust the cursor position if we clicked to the right of returned character Rect charRect = textbox.GetRectFromCharacterIndex(index, true); if (position.X > charRect.Left + charRect.Width / 2) { index++; } } return index; } static void ValidateExpression(ExpressionTextBox etb) { string errorMessage; if (etb.DoValidation(new ExpressionValidationContext(etb), out errorMessage)) { etb.UpdateValidationError(errorMessage); } } static void OnLinesChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ExpressionTextBox textBox = (ExpressionTextBox)dependencyObject; if (textBox.MinLines > textBox.MaxLines) { textBox.MaxLines = textBox.MinLines; } } static void OnTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ExpressionTextBox textBox = (ExpressionTextBox)dependencyObject; if (textBox.ExpressionEditorService != null && textBox.expressionEditorInstance != null) { textBox.expressionEditorInstance.Text = textBox.Text; } } static void OnOwnerModelItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ExpressionTextBox textBox = (ExpressionTextBox)dependencyObject; textBox.InitializeContext(); textBox.OnPathToArgumentChanged(textBox.PathToArgument); } public IExpressionEditorService ExpressionEditorService { get { return this.expressionEditorService; } } internal string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } internal string ExpressionText { get { return (string)GetValue(ExpressionTextProperty); } set { SetValue(ExpressionTextProperty, value); } } public string HintText { get { return (string)GetValue(HintTextProperty); } set { SetValue(HintTextProperty, value); } } [Fx.Tag.KnownXamlExternal] public ModelItem Expression { get { return (ModelItem)GetValue(ExpressionTextBox.ExpressionProperty); } set { SetValue(ExpressionTextBox.ExpressionProperty, value); } } public Type ExpressionType { get { return (Type)GetValue(ExpressionTextBox.ExpressionTypeProperty); } set { SetValue(ExpressionTextBox.ExpressionTypeProperty, value); } } [Fx.Tag.KnownXamlExternal] public ModelItem OwnerActivity { get { return (ModelItem)GetValue(ExpressionTextBox.OwnerActivityProperty); } set { SetValue(ExpressionTextBox.OwnerActivityProperty, value); } } internal ValidationState ValidationState { get { return (ValidationState)GetValue(ExpressionTextBox.ValidationStateProperty); } set { SetValue(ExpressionTextBox.ValidationStateProperty, value); } } public bool UseLocationExpression { get { return (bool)GetValue(ExpressionTextBox.UseLocationExpressionProperty); } set { SetValue(ExpressionTextBox.UseLocationExpressionProperty, value); } } public ScrollBarVisibility VerticalScrollBarVisibility { get { return (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty); } set { SetValue(VerticalScrollBarVisibilityProperty, value); } } public ScrollBarVisibility HorizontalScrollBarVisibility { get { return (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty); } set { SetValue(HorizontalScrollBarVisibilityProperty, value); } } public int MaxLines { get { return (int)GetValue(MaxLinesProperty); } set { SetValue(MaxLinesProperty, value); } } public int MinLines { get { return (int)GetValue(MinLinesProperty); } set { SetValue(MinLinesProperty, value); } } public bool ExplicitCommit { get { return (bool)GetValue(ExplicitCommitProperty); } set { SetValue(ExplicitCommitProperty, value); } } internal bool HasErrors { get { bool hasErrors = false; if (this.EditingState == EditingState.Idle && !this.IsIndependentExpression) { if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null) { ValidationState state = this.ValidationService.ValidationStateProperty.Getter(this.Expression); hasErrors = state == ValidationState.Error; } } else { hasErrors = this.HasValidationError; } return hasErrors; } } internal string ErrorMessage { get { string errorMessage = string.Empty; if (this.EditingState == EditingState.Idle && !this.IsIndependentExpression) { if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null) { errorMessage = this.ValidationService.ValidationMessageProperty.Getter(this.Expression); } } else { errorMessage = this.ValidationErrorMessage; } return errorMessage; } } internal EditingState EditingState { get { return (EditingState)GetValue(EditingStateProperty); } set { SetValue(EditingStateProperty, value); } } internal bool HasValidationError { get { return (bool)GetValue(HasValidationErrorProperty); } set { SetValue(HasValidationErrorProperty, value); } } internal string ValidationErrorMessage { get { return (string)GetValue(ValidationErrorMessageProperty); } set { SetValue(ValidationErrorMessageProperty, value); } } public string PathToArgument { get { return (string)GetValue(PathToArgumentProperty); } set { SetValue(PathToArgumentProperty, value); } } public bool IsSupportedExpression { get { return (bool)GetValue(IsSupportedExpressionProperty); } set { SetValue(IsSupportedExpressionProperty, value); } } public string DefaultValue { get { return (string)GetValue(DefaultValueProperty); } set { SetValue(DefaultValueProperty, value); } } public bool AcceptsReturn { get { return (bool)GetValue(AcceptsReturnProperty); } set { SetValue(AcceptsReturnProperty, value); } } public bool AcceptsTab { get { return (bool)GetValue(AcceptsTabProperty); } set { SetValue(AcceptsTabProperty, value); } } public bool IsReadOnly { get { return (bool)GetValue(IsReadOnlyProperty); } set { SetValue(IsReadOnlyProperty, value); } } internal bool IsIndependentExpression { get { return (bool)GetValue(IsIndependentExpressionProperty); } set { SetValue(IsIndependentExpressionProperty, value); } } EditingContext Context { get { if (this.context == null) { if (this.OwnerActivity != null) { InitializeContext(); } } return this.context; } } DesignerPerfEventProvider PerfProvider { get { if (this.perfProvider == null && this.Context != null) { perfProvider = this.Context.Services.GetService (); } return this.perfProvider; } } ValidationService ValidationService { get { if (this.Context != null) { return this.Context.Services.GetService (); } else { return null; } } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] internal void GenerateExpression() { Activity valueExpression = null; // If the text is null we don't need to bother generating the expression (this would be the case the // first time you enter an ETB. We still need to generate the expression when it is EMPTY however - otherwise // the case where you had an expression (valid or invalid), then deleted the whole thing will not be evaluated. if (this.Text != null) { using (ModelEditingScope scope = this.OwnerActivity.BeginEdit(SR.PropertyChangeEditingScopeDescription)) { this.EditingState = EditingState.Validating; // we set the expression to null // a) when the expressionText is empty AND it's a reference expression or // b) when the expressionText is empty AND the DefaultValue property is null if (this.Text.Length == 0 && (this.UseLocationExpression || this.DefaultValue == null)) { valueExpression = null; } else { if (this.Text.Length == 0) { this.Text = this.DefaultValue; } valueExpression = CreateVBExpression(); } CreateExpressionModelItem(valueExpression); scope.Complete(); } } } void OnValidationCompleted(object sender, EventArgs e) { if (this.EditingState != EditingState.Editing) { if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null) { this.ValidationState = this.ValidationService.ValidationStateProperty.Getter(this.Expression); } this.EditingState = EditingState.Idle; } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] private void CreateExpressionModelItem(object valueExpression) { // try to wrap the droppedObject in a ModelItem. ModelItem expressionModelItem = null; if (valueExpression != null) { ModelServiceImpl modelService = (ModelServiceImpl)this.Context.Services.GetService (); expressionModelItem = modelService.WrapAsModelItem(valueExpression); expressionModelItem.PropertyChanged += this.OnExpressionModelItemChanged; this.boundedExpression = expressionModelItem; } try { this.internalModelItemChange = true; this.EditingState = EditingState.Validating; SetValue(ExpressionProperty, expressionModelItem); } catch (Exception err) { Trace.WriteLine(string.Format(CultureInfo.CurrentUICulture, "{0}\r\n{1}", err.Message, err.StackTrace)); this.internalModelItemChange = false; } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] private Activity CreateVBExpression() { Activity valueExpression = null; if (this.OwnerActivity != null) { ExpressionValidationContext validationContext = new ExpressionValidationContext(this); Type expReturnType = null; string newExpressionText = null; SourceExpressionException compileErrorMessages; try { VisualBasicSettings settings = null; valueExpression = CreateVBExpression(validationContext, out newExpressionText, out expReturnType, out compileErrorMessages, out settings); if (settings != null) { Fx.Assert(this.Context != null, "editing context cannot be null"); //merge with import designer foreach (VisualBasicImportReference reference in settings.ImportReferences) { ImportDesigner.AddImport(reference.Import, this.Context); } } if (!string.IsNullOrEmpty(newExpressionText)) { this.previousText = this.Text; this.Text = newExpressionText; } } catch (Exception err) { //if the VisualBasicDesignerHelper were able to resolve the type we use that if (expReturnType == null) { //if not we try to use the expression type expReturnType = this.ExpressionType; } //if expression type is also null, the we use object if (expReturnType == null) { expReturnType = typeof(object); } valueExpression = ExpressionHelper.CreateExpression(expReturnType, this.Text, UseLocationExpression, validationContext.ParserContext); Trace.WriteLine(string.Format(CultureInfo.CurrentUICulture, "{0}\r\n{1}", err.Message, err.StackTrace)); } this.ExpressionText = this.Text; } else { // If the OwnerActivity is null, do not try to compile the expression otherwise VS will crash // Inform the user that OwnerActivity is null (i.e. there is a error in their code) Trace.WriteLine("ExpressionTextBox OwnerActivity is null."); } return valueExpression; } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] private static Activity CreateVBExpression(ExpressionValidationContext context, out string newExpressionText, out Type expReturnType, out SourceExpressionException compileErrorMessages, out VisualBasicSettings vbSettings) { expReturnType = null; newExpressionText = null; compileErrorMessages = null; vbSettings = null; //try easy way first - look if there is a type converer which supports convertsion between expression type and string ActivityWithResult literal = null; try { if (!context.UseLocationExpression) { literal = ExpressionHelper.TryCreateLiteral(context.ExpressionType, context.ExpressionText, context.ParserContext); } if (literal != null) { //need to get new expression text - converter might have changed its format, and we want it to be up to date IValueSerializableExpression serializableExpression = literal as IValueSerializableExpression; Fx.Assert(serializableExpression != null, "the expression has to be a Literal<>, which should be IValueSerializableExpression"); if (serializableExpression.CanConvertToString(context.ParserContext)) { bool shouldBeQuoted = typeof(string) == context.ExpressionType || typeof(Uri) == context.ExpressionType; //whether string begins and ends with quotes '"'. also, if there are //more quotes within than those begining and ending ones, do not bother with literal - assume this is an expression. bool isQuotedString = shouldBeQuoted && context.ExpressionText.StartsWith("\"", StringComparison.CurrentCulture) && context.ExpressionText.EndsWith("\"", StringComparison.CurrentCulture) && context.ExpressionText.IndexOf("\"", 1, StringComparison.CurrentCulture) == context.ExpressionText.Length - 1; var formatString = isQuotedString ? "\"{0}\"" : "{0}"; newExpressionText = string.Format(CultureInfo.InvariantCulture, formatString, serializableExpression.ConvertToString(context.ParserContext)); } } } //conversion failed - do nothing, let VB compiler take care of the expression catch { } Activity valueExpression = literal; if (null == valueExpression) { if (!context.UseLocationExpression) { //Compile for validation. valueExpression = VisualBasicDesignerHelper.CreatePrecompiledVisualBasicValue(context.ExpressionType, context.ExpressionText, context.ParserContext.Namespaces, context.ReferencedAssemblies, context.ParserContext, out expReturnType, out compileErrorMessages, out vbSettings); } else { //Compile for validation. valueExpression = VisualBasicDesignerHelper.CreatePrecompiledVisualBasicReference(context.ExpressionType, context.ExpressionText, context.ParserContext.Namespaces, context.ReferencedAssemblies, context.ParserContext, out expReturnType, out compileErrorMessages, out vbSettings); } } return valueExpression; } static void OnExpressionTypeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = obj as ExpressionTextBox; Fx.Assert (etb != null, "Sender must be an ETB"); //for independent expressions, when the type changes, we need to validate the expressions if (etb.initialized && etb.IsIndependentExpression && etb.EditingState == EditingState.Idle) { ValidateExpression(etb); } } static void OnUseLocationExpressionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = obj as ExpressionTextBox; Fx.Assert(etb != null, "Sender must be an ETB"); //for independent expressions, when the type changes, we need to validate the expressions if (etb.initialized && etb.IsIndependentExpression && etb.EditingState == EditingState.Idle && etb.OwnerActivity != null) { ValidateExpression(etb); } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")] static void OnExpressionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { ExpressionTextBox ctrl = (ExpressionTextBox)obj; ModelItem oldExpression = e.OldValue as ModelItem; if (oldExpression != null) { oldExpression.PropertyChanged -= ctrl.OnExpressionModelItemChanged; } ModelItem expression = e.NewValue as ModelItem; if (expression != null && expression != ctrl.boundedExpression) { expression.PropertyChanged += ctrl.OnExpressionModelItemChanged; } try { ctrl.boundedExpression = expression; ctrl.OnExpressionChanged(); } catch (Exception err) { //if context is set - use error reporting if (null != ctrl.Context) { ctrl.Context.Items.SetValue(new ErrorItem() { Message = err.Message, Details = err.ToString() }); } //otherwise - fallback to message box else { MessageBox.Show(err.ToString(), err.Message); } } } void expressionModelItem_PropertyChanged(object sender, PropertyChangedEventArgs e) { ModelItem item = sender as ModelItem; if (item != null) { if (e.PropertyName.Equals("ExpressionText", StringComparison.Ordinal)) { this.OnExpressionChanged(); } } } static void OnPathToArgumentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { ExpressionTextBox ctrl = (ExpressionTextBox)obj; ctrl.OnPathToArgumentChanged((string)e.NewValue); } static void OnIsIndependentExpressionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = dependencyObject as ExpressionTextBox; Fx.Assert(etb != null, "Sender must be an ETB"); //if this is an independent expression, we need to initialize the validation error because validation service will not validate it if (etb.initialized && etb.IsIndependentExpression && etb.EditingState == EditingState.Idle) { ValidateExpression(etb); } } void UpdateValidationState() { if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null) { this.ValidationState = this.ValidationService.ValidationStateProperty.Getter(this.Expression); } else { this.ValidationState = ValidationState.Valid; } } //We need to react to OnExpressionChanged, since there might be multiple ExpressionTextBoxes(ETB) associated to a single Expression. //All the ETBs should be updated if the value in any of the ETBs is changed. void OnExpressionChanged() { if (!this.internalModelItemChange) { if (this.Expression == null) { //new expression is null - there is no text, no previous text, erros should be clear as well as error message this.Text = string.Empty; this.previousText = this.Text; this.ExpressionText = null; this.ValidationState = ValidationState.Valid; } else { this.UpdateValidationState(); object expressionObject = this.Expression.GetCurrentValue(); Activity expression = null; String expressionString = null; //create parser context - do not pass ownerActivity - it might be null at this time ParserContext context = new ParserContext(); expression = expressionObject as Activity; //get the expression text out of expression - in case of non-VB expression, context may be populated with //extra namespace information expressionString = ExpressionHelper.GetExpressionString(expression, context); if (expressionString != null) { Fx.Assert(expression != null, "if expression is null, expressionString should also be null"); Type expressionType = expression.GetType(); Type expressionArgumentType = expressionType.IsGenericType ? expressionType.GetGenericArguments()[0] : typeof(object); bool isValid = false; bool isLiteral = false; //try to figure out what kind of expression are we dealing with while (null != expressionType) { //check if expression is literal expression if (expressionType.IsGenericType && typeof(Literal<>) == expressionType.GetGenericTypeDefinition()) { isLiteral = true; } //expression must be of type Activity if (expressionType.IsGenericType && typeof(Activity<>) == expressionType.GetGenericTypeDefinition()) { isValid = true; break; } expressionType = expressionType.BaseType; } if (!isValid) { throw FxTrace.Exception.AsError(new NotSupportedException()); } this.Text = expressionString; this.ExpressionText = expressionString; this.previousText = this.Text; //if expression is a literal, which is not supported, delegate a call to regenerate it as expression if (isLiteral && !ExpressionHelper.CanTypeBeSerializedAsLiteral( expressionArgumentType )) { //call should be postponed until whole ux is loaded. this.Dispatcher.BeginInvoke(new Action(() => { this.GenerateExpression(); }), DispatcherPriority.ApplicationIdle); } } else { //We won't handle non-VB expressions this.IsSupportedExpression = false; this.HintText = SR.UnsupportedExpressionHintText; } this.isExpressionLoaded = true; if (this.isBeginEditPending) { this.BeginEdit(); } } } internalModelItemChange = false; } void OnPathToArgumentChanged(string pathAsString) { this.expressionModelProperty = null; this.expressionConverter = null; if (!string.IsNullOrEmpty(pathAsString) && null != this.OwnerActivity) { string[] path = pathAsString.Split('.'); if (path.Length > 0) { this.expressionModelProperty = this.OwnerActivity.Properties[path[0]]; for (int i = 1; i < path.Length; ++i) { if (null != this.expressionModelProperty && null != this.expressionModelProperty.Value) { this.expressionModelProperty = this.expressionModelProperty.Value.Properties[path[i]]; } else { this.expressionModelProperty = null; break; } } } } if (null != this.expressionModelProperty) { this.expressionConverter = ((ModelPropertyImpl)this.expressionModelProperty).PropertyDescriptor.Converter; } this.InitializeHintText(); } void InitializeHintText() { DescriptionAttribute customHint = null; if (this.expressionModelProperty != null && this.expressionModelProperty.Attributes.Count != 0) { customHint = this.expressionModelProperty.Attributes. OfType (). FirstOrDefault(); } this.HintText = (null == customHint || string.IsNullOrEmpty(customHint.Description) ? (string.Equals(this.HintText, SR.ExpressionDefaultText) ? SR.ExpressionDefaultText : this.HintText) : customHint.Description); string hint = this.HintText.Trim(new char[] { '<', '>' }); this.SetValue(AutomationProperties.HelpTextProperty, hint); } void InitializeContext() { if (this.context == null) { ModelItem modelItem = this.OwnerActivity; Fx.Assert(modelItem != null, "OwnerActivity is not set before calling InitializeContext()"); if (null != modelItem) { this.context = modelItem.GetEditingContext(); if (this.ValidationService != null) { this.ValidationService.ValidationCompleted += this.OnValidationCompleted; this.UpdateValidationState(); } } } } #region Command Handlers void OnCommitCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { string currentText = this.Text; if (this.expressionEditorInstance != null) { currentText = this.expressionEditorInstance.Text; if (!string.IsNullOrEmpty(currentText)) { currentText = currentText.Trim(); } } //we dont need to commit change if currentText and previousText is the same //null and empty should be considered equal in this context e.CanExecute = !string.Equals(currentText, this.previousText) && !(string.IsNullOrEmpty(currentText) && string.IsNullOrEmpty(this.previousText)); e.Handled = true; } void OnCommitCommandExecute(object sender, ExecutedRoutedEventArgs e) { this.Commit(true); e.Handled = true; } void OnExpressionTextBoxLoaded(object sender, RoutedEventArgs e) { this.InitializeHintText(); //if this is an independent expression, we need to initialize the validation error because validation service will not validate it if (this.IsIndependentExpression) { ValidateExpression(this); } this.initialized = true; } void OnExpressionTextBoxUnloaded(object sender, RoutedEventArgs e) { if (this.ValidationService != null) { this.ValidationService.ValidationCompleted -= this.OnValidationCompleted; } KillValidator(); if (this.boundedExpression != null) { this.boundedExpression.PropertyChanged -= this.OnExpressionModelItemChanged; } } void OnCompleteWordCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanCompleteWord(); e.Handled = true; } else { e.Handled = false; } } void OnGlobalIntellisenseCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanGlobalIntellisense(); e.Handled = true; } else { e.Handled = false; } } void OnParameterInfoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanParameterInfo(); e.Handled = true; } else { e.Handled = false; } } void OnQuickInfoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanQuickInfo(); e.Handled = true; } else { e.Handled = false; } } void OnIncreaseFilterLevelCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanIncreaseFilterLevel(); e.Handled = true; } else { e.Handled = false; } } void OnDecreaseFilterLevelCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanDecreaseFilterLevel(); e.Handled = true; } else { e.Handled = false; } } void OnCutCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanCut(); e.Handled = true; } else { e.Handled = false; } } void OnCompleteWordCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.CompleteWord(); } else { e.Handled = false; } } void OnGlobalIntellisenseCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.GlobalIntellisense(); } else { e.Handled = false; } } void OnParameterInfoCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.ParameterInfo(); } else { e.Handled = false; } } void OnQuickInfoCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.QuickInfo(); } else { e.Handled = false; } } void OnDecreaseFilterLevelCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.DecreaseFilterLevel(); } else { e.Handled = false; } } void OnIncreaseFilterLevelCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.IncreaseFilterLevel(); } else { e.Handled = false; } } void OnCutCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.Cut(); } else { e.Handled = false; } } void OnCopyCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanCopy(); e.Handled = true; } else { e.Handled = false; } } void OnCopyCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.Copy(); } else { e.Handled = false; } } void OnPasteCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanPaste(); e.Handled = true; } else { e.Handled = false; } } void OnPasteCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.Paste(); } else { e.Handled = false; } } void OnUndoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanUndo(); e.Handled = true; } else { e.Handled = false; } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Catch all execeptions to prevent crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "Catch all execeptions to prevent crash.")] void OnUndoCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { try { e.Handled = this.expressionEditorInstance.Undo(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } else { e.Handled = false; } } void OnRedoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanRedo(); e.Handled = true; } else { e.Handled = false; } } void OnRedoCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.Redo(); } else { e.Handled = false; } } void OnHelpExecute(object sender, ExecutedRoutedEventArgs e) { IIntegratedHelpService help = this.Context.Services.GetService (); if (help != null) { help.ShowHelpFromKeyword(HelpKeywords.ExpressionEditorPage); } else { System.Diagnostics.Process.Start(SR.DefaultHelpUrl); } } #endregion static void OnHintTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if (!string.Equals(SR.ExpressionDefaultText, e.NewValue) && !string.Equals(e.OldValue, e.NewValue)) { ((ExpressionTextBox)sender).InitializeHintText(); } } static void OnAcceptsReturnChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = sender as ExpressionTextBox; if (etb != null && etb.expressionEditorInstance != null) { etb.expressionEditorInstance.AcceptsReturn = (bool)e.NewValue; } } static void OnAcceptsTabChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = sender as ExpressionTextBox; if (etb != null && etb.expressionEditorInstance != null) { etb.expressionEditorInstance.AcceptsTab = (bool)e.NewValue; } } void StartValidator() { if (this.validator == null) { this.validator = new BackgroundWorker(); this.validator.WorkerReportsProgress = true; this.validator.WorkerSupportsCancellation = true; this.validator.DoWork += delegate(object obj, DoWorkEventArgs args) { BackgroundWorker worker = obj as BackgroundWorker; if (worker.CancellationPending) { args.Cancel = true; return; } ExpressionValidationContext validationContext = args.Argument as ExpressionValidationContext; if (validationContext != null) { string errorMessage; if (DoValidation(validationContext, out errorMessage)) { worker.ReportProgress(0, errorMessage); } //sleep if (worker.CancellationPending) { args.Cancel = true; return; } Thread.Sleep(ValidationWaitTime); args.Result = validationContext; } }; this.validator.RunWorkerCompleted += delegate(object obj, RunWorkerCompletedEventArgs args) { if (!args.Cancelled) { ExpressionValidationContext validationContext = args.Result as ExpressionValidationContext; if (validationContext != null) { Dispatcher.BeginInvoke(new Action ((target)=>{ //validator could be null by the time we try to validate again or //if it's already busy if (this.validator != null && !this.validator.IsBusy) { target.Update(this); this.validator.RunWorkerAsync(target); } }), validationContext); } } }; this.validator.ProgressChanged += delegate(object obj, ProgressChangedEventArgs args) { string error = args.UserState as string; Dispatcher.BeginInvoke(new Action (UpdateValidationError), error); }; this.validator.RunWorkerAsync(new ExpressionValidationContext(this)); } } //perform one validation synchronously //return value indicates whether errorMessage is updated. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")] bool DoValidation(ExpressionValidationContext validationContext, out string errorMessage) { Fx.Assert(validationContext != null, "only work when context is not null"); errorMessage = null; //validate //if the text is empty we clear the error message if (string.IsNullOrEmpty(validationContext.ExpressionText)) { errorMessage = null; return true; } // if the expression text is different from the last time we run the validation we run the validation else if (!string.Equals(validationContext.ExpressionText, validationContext.ValidatedExpressionText)) { Type expReturnType = null; string newExpressionText = null; SourceExpressionException compileErrorMessages = null; VisualBasicSettings settings = null; try { CreateVBExpression(validationContext, out newExpressionText, out expReturnType, out compileErrorMessages, out settings); if (compileErrorMessages != null) { errorMessage = compileErrorMessages.Message; } } catch (Exception err) { errorMessage = err.Message; } return true; } return false; } void UpdateValidationError(string errorMessage) { if (!string.IsNullOrEmpty(errorMessage)) { //report error this.HasValidationError = true; this.ValidationErrorMessage = errorMessage; } else { this.HasValidationError = false; this.ValidationErrorMessage = null; } } private class ExpressionValidationContext { internal ParserContext ParserContext { get; set; } internal Type ExpressionType { get; set; } internal String ExpressionText { get; set; } internal EditingContext EditingContext { get; set; } internal String ValidatedExpressionText { get; set; } internal bool UseLocationExpression { get; set; } internal ExpressionValidationContext(ExpressionTextBox etb) { Update(etb); } internal void Update(ExpressionTextBox etb) { Fx.Assert(etb.OwnerActivity != null, "Owner Activity is null"); this.EditingContext = etb.OwnerActivity.GetEditingContext(); //setup ParserContext this.ParserContext = new ParserContext(etb.OwnerActivity) { //callee is a ExpressionTextBox Instance = etb, //pass property descriptor belonging to epression's model property (if one exists) PropertyDescriptor = (null != etb.expressionModelProperty ? ((ModelPropertyImpl)etb.expressionModelProperty).PropertyDescriptor : null), }; this.ExpressionType = etb.ExpressionType; this.ValidatedExpressionText = this.ExpressionText; if (etb.expressionEditorInstance != null) { this.ExpressionText = etb.expressionEditorInstance.Text; } else { this.ExpressionText = etb.Text; } this.UseLocationExpression = etb.UseLocationExpression; } internal IEnumerable ReferencedAssemblies { get { Fx.Assert(this.EditingContext != null, "ModelItem.Context = null"); AssemblyContextControlItem assemblyContext = this.EditingContext.Items.GetValue (); if (assemblyContext != null) { return assemblyContext.AllAssemblyNamesInContext; } return null; } } } } internal sealed class LineToHeightConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { double convertedValue = Double.NaN; bool isDefault = true; // Calculate the height for the textblock as ExpressionTextBox exposes lines properties, // and TextBlock doesn't have lines properties. FontFamily fontFamily = values.OfType ().FirstOrDefault(); int lines = values.OfType ().FirstOrDefault(); double[] doubleArray = values.OfType ().ToArray (); if (doubleArray.Length == 2) { double height = doubleArray[0]; // The first element of the array is going to be the height double fontSize = doubleArray[1]; // The seconed element of the array is going to be the fontSize // 0.0 is default for MinHeight, PositiveInfinity is default for MaxHeight if (string.Equals(parameter as string, "MinHeight")) { isDefault = (height == 0.0); } else if (string.Equals(parameter as string, "MaxHeight")) { isDefault = (double.IsPositiveInfinity(height)); } // If the height value we are evaluating is default, use Lines for sizing... // If no heights (height or lines) have been explicitly specified, we would rather default the height // as if the Line was 1 - so use the line heights, rather than 0.0 and/or PositiveInfinity. if (isDefault) { double lineHeight = fontSize * fontFamily.LineSpacing; if (fontFamily != null) { convertedValue = lineHeight * (double)lines + 4; } } else { convertedValue = height; } } return convertedValue; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw FxTrace.Exception.AsError(new NotSupportedException()); } } internal sealed class ValidationStateToErrorConverter : IMultiValueConverter { #region IMultiValueConverter Members public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { ExpressionTextBox etb = values[0] as ExpressionTextBox; if (values[0] == DependencyProperty.UnsetValue || etb == null) { return false; } return etb.HasErrors; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw FxTrace.Exception.AsError(new NotImplementedException()); } #endregion } internal sealed class ValidationErrorMessageConverter : IMultiValueConverter { #region IMultiValueConverter Members public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { ExpressionTextBox etb = values[0] as ExpressionTextBox; if (values[0] == DependencyProperty.UnsetValue || etb == null) { return false; } return etb.ErrorMessage; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw FxTrace.Exception.AsError(new NotImplementedException()); } #endregion } internal sealed class TypeToPromptTextConverter : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return TypeToPromptTextConverter.GetPromptText(value); } internal static string GetPromptText(object value) { Type expressionType = value as Type; if (value == DependencyProperty.UnsetValue || expressionType == null || !expressionType.IsValueType) { return "Nothing"; } else { return Activator.CreateInstance(expressionType).ToString(); } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw FxTrace.Exception.AsError(new NotSupportedException()); } #endregion } public enum EditingState { Editing, Validating, Idle } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------- namespace System.Activities.Presentation.View { using System.Activities.Presentation.Internal.PropertyEditing; using System.Activities.Presentation.Model; using System.Activities.Presentation.Services; using System.Activities.Presentation.Validation; using System.Activities.Presentation.Hosting; using System.Activities.Presentation.Xaml; using System.Activities.ExpressionParser; using System.Activities.Expressions; using System.Activities.XamlIntegration; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Runtime; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using Microsoft.VisualBasic.Activities; using System.Windows.Threading; using System.Threading; //This control is used for expression editing in activity designers. //It uses the Activity TypeConverter to convert between a Activity and its string representation. //It defines 3 dependency properties - OwnerActivity, ExpressionModelItem and ExpressionType. //ActivityModelItem is used to create the parser context required by the TypeConverter. //ExpressionType is the type of the expression associated with this text box. This is required by the TypeConverter. public sealed partial class ExpressionTextBox : ContentControl { internal static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnTextChanged), new CoerceValueCallback(OnTextCoerceValue))); public static readonly DependencyProperty HintTextProperty = DependencyProperty.Register("HintText", typeof(string), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(SR.ExpressionDefaultText, new PropertyChangedCallback(OnHintTextChanged))); internal static readonly DependencyProperty ValidationStateProperty = DependencyProperty.Register("ValidationState", typeof(ValidationState), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(ValidationState.Valid)); public static readonly DependencyProperty ExpressionProperty = DependencyProperty.Register("Expression", typeof(ModelItem), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnExpressionChanged))); public static readonly DependencyProperty ExpressionTypeProperty = DependencyProperty.Register("ExpressionType", typeof(Type), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnExpressionTypeChanged))); public static readonly DependencyProperty OwnerActivityProperty = DependencyProperty.Register("OwnerActivity", typeof(ModelItem), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnOwnerModelItemChanged))); public static readonly DependencyProperty UseLocationExpressionProperty = DependencyProperty.Register("UseLocationExpression", typeof(bool), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(false, OnUseLocationExpressionChanged)); public static readonly DependencyProperty PathToArgumentProperty = DependencyProperty.Register("PathToArgument", typeof(string), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(null, OnPathToArgumentChanged)); public static readonly DependencyProperty IsSupportedExpressionProperty = DependencyProperty.Register("IsSupportedExpression", typeof(bool), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(true)); public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = DependencyProperty.Register("VerticalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(ExpressionTextBox), new PropertyMetadata(ScrollBarVisibility.Hidden)); public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = DependencyProperty.Register("HorizontalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(ExpressionTextBox), new PropertyMetadata(ScrollBarVisibility.Hidden)); public static readonly DependencyProperty MaxLinesProperty = DependencyProperty.Register("MaxLines", typeof(int), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(Int32.MaxValue, new PropertyChangedCallback(OnLinesChanged))); public static readonly DependencyProperty MinLinesProperty = DependencyProperty.Register("MinLines", typeof(int), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(1, new PropertyChangedCallback(OnLinesChanged))); public static readonly DependencyProperty ExplicitCommitProperty = DependencyProperty.Register("ExplicitCommit", typeof(bool), typeof(ExpressionTextBox), new FrameworkPropertyMetadata(false)); public static readonly DependencyProperty DefaultValueProperty = DependencyProperty.Register("DefaultValue", typeof(string), typeof(ExpressionTextBox), new PropertyMetadata(null)); public static readonly DependencyProperty AcceptsReturnProperty = DependencyProperty.Register("AcceptsReturn", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(true, OnAcceptsReturnChanged)); public static readonly DependencyProperty AcceptsTabProperty = DependencyProperty.Register("AcceptsTab", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(false, OnAcceptsTabChanged)); public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(false)); internal static readonly DependencyProperty EditingStateProperty = DependencyProperty.Register("EditingState", typeof(EditingState), typeof(ExpressionTextBox), new PropertyMetadata(EditingState.Idle)); internal static readonly DependencyProperty HasValidationErrorProperty = DependencyProperty.Register("HasValidationError", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(false)); internal static readonly DependencyProperty ValidationErrorMessageProperty = DependencyProperty.Register("ValidationErrorMessage", typeof(string), typeof(ExpressionTextBox), new PropertyMetadata(null)); internal static readonly DependencyProperty ExpressionTextProperty = DependencyProperty.Register("ExpressionText", typeof(string), typeof(ExpressionTextBox), new PropertyMetadata(null)); internal static readonly DependencyProperty IsIndependentExpressionProperty = DependencyProperty.Register("IsIndependentExpression", typeof(bool), typeof(ExpressionTextBox), new PropertyMetadata(new PropertyChangedCallback(OnIsIndependentExpressionChanged))); public static readonly RoutedEvent EditorLostLogicalFocusEvent = EventManager.RegisterRoutedEvent("EditorLostLogicalFocus", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand CompleteWordCommand = new RoutedCommand("CompleteWordCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand GlobalIntellisenseCommand = new RoutedCommand("GlobalIntellisenseCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand ParameterInfoCommand = new RoutedCommand("ParameterInfoCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand QuickInfoCommand = new RoutedCommand("QuickInfoCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand IncreaseFilterLevelCommand = new RoutedCommand("IncreaseFilterLevelCommand", typeof(ExpressionTextBox)); [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotDeclareReadOnlyMutableReferenceTypes)] public static readonly ICommand DecreaseFilterLevelCommand = new RoutedCommand("DecreaseFilterLevelCommand", typeof(ExpressionTextBox)); bool internalModelItemChange = false; string previousText = null; EditingContext context; ModelProperty expressionModelProperty; TypeConverter expressionConverter; bool initialized = false; IExpressionEditorService expressionEditorService; IExpressionEditorInstance expressionEditorInstance; Control hostControl; string editorName; double blockHeight = double.NaN; double blockWidth = double.NaN; bool isExpressionLoaded = false; bool isBeginEditPending = false; DesignerPerfEventProvider perfProvider; ModelItem boundedExpression = null; BackgroundWorker validator = null; const int ValidationWaitTime = 800; PropertyChangedEventHandler onExpressionModelItemChangedHandler; public ExpressionTextBox() { InitializeComponent(); this.MinHeight = this.FontSize + 4; /* 4 pixels for border*/ this.context = null; this.editorName = null; this.ContentTemplate = (DataTemplate)FindResource("textblock"); this.Loaded += this.OnExpressionTextBoxLoaded; this.Unloaded += this.OnExpressionTextBoxUnloaded; CommandBinding CommitBinding = new CommandBinding(DesignerView.CommitCommand, OnCommitCommandExecute, OnCommitCommandCanExecute); this.CommandBindings.Add(CommitBinding); } public event RoutedEventHandler EditorLostLogicalFocus { add { this.AddHandler(ExpressionTextBox.EditorLostLogicalFocusEvent, value); } remove { this.RemoveHandler(ExpressionTextBox.EditorLostLogicalFocusEvent, value); } } PropertyChangedEventHandler OnExpressionModelItemChanged { get { if (this.onExpressionModelItemChangedHandler == null) { this.onExpressionModelItemChangedHandler = new PropertyChangedEventHandler(this.expressionModelItem_PropertyChanged); } return this.onExpressionModelItemChangedHandler; } } protected override void OnContextMenuOpening(ContextMenuEventArgs e) { e.Handled = true; } protected override void OnPreviewMouseRightButtonDown(MouseButtonEventArgs e) { base.OnPreviewMouseRightButtonDown(e); e.Handled = true; } static object OnTextCoerceValue(DependencyObject dp, object value) { string tempText = value as string; ExpressionTextBox etb = dp as ExpressionTextBox; if (etb != null) { if (tempText != null) { tempText = tempText.Trim(); } } return tempText; } protected override AutomationPeer OnCreateAutomationPeer() { return new ExpressionTextBoxAutomationPeer(this); } protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) { base.OnLostKeyboardFocus(e); if (this.expressionEditorInstance != null && (this.expressionEditorInstance.HasAggregateFocus || (this.hostControl != null && this.hostControl.IsFocused))) { return; } DoLostFocus(); } private void DoLostFocus() { KillValidator(); ValidateExpression(this); if (this.Context != null) { // Unselect if this is the currently selected one. ExpressionSelection current = this.Context.Items.GetValue (); if (current != null && current.ModelItem == this.Expression) { ExpressionSelection emptySelection = new ExpressionSelection(null); this.Context.Items.SetValue(emptySelection); } } // Generate and validate the expression. // Get the text from the snapshot and set it to the Text property if (this.expressionEditorInstance != null) { this.expressionEditorInstance.ClearSelection(); } bool committed = false; if (!this.ExplicitCommit) { //commit change and let the commit change code do the revert committed = Commit(false); //reset the error icon if we didn't get to set it in the commit if (!committed || this.IsIndependentExpression) { this.EditingState = EditingState.Idle; // Switch the control back to a textbox - // but give it the text from the editor (textbox should be bound to the Text property, so should // automatically be filled with the correct text, from when we set the Text property earlier) if (!this.ContentTemplate.Equals((DataTemplate)FindResource("textblock"))) { this.ContentTemplate = (DataTemplate)FindResource("textblock"); } } } //raise EditorLostLogical focus - in case when some clients need to do explicit commit this.RaiseEvent(new RoutedEventArgs(ExpressionTextBox.EditorLostLogicalFocusEvent, this)); } private void KillValidator() { if (validator != null) { this.validator.CancelAsync(); this.validator.Dispose(); this.validator = null; } } internal static bool ShouldGenerateExpression(string oldText, string newText) { return newText != null && !string.Equals(newText, oldText) && !(oldText == null && newText.Equals(string.Empty)); } private bool Commit(bool isExplicitCommit) { bool committed = false; //only generate and validate the expression when when we don't require explicit commit change //or when the commit is explicit if (!this.ExplicitCommit || isExplicitCommit) { // Generate and validate the expression. // Get the text from the snapshot and set it to the Text property this.previousText = null; // In VS if (this.expressionEditorInstance != null) { this.previousText = this.Text; this.Text = this.expressionEditorInstance.GetCommittedText(); } // In rehost else { if (this.Expression != null) { Activity expression = this.Expression.GetCurrentValue() as Activity; // if expression is null, GetExpressionString will return null this.previousText = ExpressionHelper.GetExpressionString(expression, this.OwnerActivity); } else { this.previousText = null; } } // If the Text is null, or equal to the previous value, or changed from null to empty, don't bother generating the expression // We still need to generate the expression when it is changed from other value to EMPTY however - otherwise // the case where you had an expression (valid or invalid), then deleted the whole thing will not be evaluated. if (ShouldGenerateExpression(this.previousText, this.Text)) { GenerateExpression(); committed = true; } } if (!this.ContentTemplate.Equals((DataTemplate)FindResource("textblock"))) { this.ContentTemplate = (DataTemplate)FindResource("textblock"); } return committed; } protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnPreviewMouseLeftButtonDown(e); if (this.Context != null) { ExpressionSelection expressionSelection = new ExpressionSelection(this.Expression); this.Context.Items.SetValue(expressionSelection); } } public void BeginEdit() { //am i loaded? is current template a textblock? if (this.isExpressionLoaded || null == this.Expression) { this.isBeginEditPending = false; this.IsReadOnly = false; if (this.IsLoaded && this.ContentTemplate.Equals(this.FindResource("textblock"))) { //get control's content presenter ContentPresenter presenter = VisualTreeUtils.GetTemplateChild (this); if (null != presenter) { //and look for the loaded textblock TextBlock tb = (TextBlock)this.ContentTemplate.FindName("expresionTextBlock", presenter); if (null != tb) { //now - give focus to the textblock - it will trigger OnGotTextBlockFocus event, which eventually //swithc ETB into expression editing mode. tb.Focus(); } } } } else { this.isBeginEditPending = true; } } internal bool HasAggregateFocus() { bool result = false; if (this.IsLoaded) { if (this.expressionEditorInstance != null) { result = (this.hostControl != null && this.hostControl.IsFocused) || this.expressionEditorInstance.HasAggregateFocus; } else { result = !this.IsKeyboardFocused && this.IsKeyboardFocusWithin; } } return result; } void OnTextBlockMouseLeftButtonDown(object sender, RoutedEventArgs e) { if (!this.IsReadOnly) { TextBlock textBlock = sender as TextBlock; if (textBlock != null) { Keyboard.Focus(textBlock); } } } void OnGotTextBlockFocus(object sender, RoutedEventArgs e) { if (this.Context == null) { return; } DesignerView designerView = this.Context.Services.GetService (); if (!designerView.IsMultipleSelectionMode) { TextBlock textBlock = sender as TextBlock; bool isInReadOnlyMode = this.IsReadOnly; if (this.Context != null) { ReadOnlyState readOnlyState = this.Context.Items.GetValue (); isInReadOnlyMode |= readOnlyState.IsReadOnly; } if (null != textBlock) { this.blockHeight = textBlock.ActualHeight; this.blockHeight = Math.Max(this.blockHeight, textBlock.MinHeight); this.blockHeight = Math.Min(this.blockHeight, textBlock.MaxHeight); this.blockWidth = textBlock.ActualWidth; this.blockWidth = Math.Max(this.blockWidth, textBlock.MinWidth); this.blockWidth = Math.Min(this.blockWidth, textBlock.MaxWidth); // 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) // Also don't create editor when we are in read only mode if (this.ContentTemplate.Equals((DataTemplate)FindResource("textblock")) && !isInReadOnlyMode) { if (this.Context != null) { // Get the ExpressionEditorService this.expressionEditorService = this.Context.Services.GetService (); } // If the service exists, use the editor template - else switch to the textbox template if (this.expressionEditorService != null) { this.PerfProvider.WorkflowDesignerExpressionEditorLoadStart(); this.ContentTemplate = (DataTemplate)FindResource("editor"); } else { this.ContentTemplate = (DataTemplate)FindResource("textbox"); } } } if (!isInReadOnlyMode) { //disable the error icon this.StartValidator(); this.EditingState = EditingState.Editing; e.Handled = true; } } } void OnGotEditingFocus(object sender, RoutedEventArgs e) { //disable the error icon this.EditingState = EditingState.Editing; this.StartValidator(); } // This method is called when the editor data template is loaded - when the editor data template // is loaded, create the editor session and the expression editor [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "CreateExpressionEditor is part of a public API. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "CreateExpressionEditor is part of a public API. Propagating exceptions might lead to VS crash.")] void OnEditorLoaded(object sender, RoutedEventArgs e) { // If the service exists, create an expression editor and add it to the grid - else switch to the textbox data template if (this.expressionEditorService != null) { Border border = (Border)sender; // Get the references and variables in scope AssemblyContextControlItem assemblies = (AssemblyContextControlItem)this.Context.Items.GetValue(typeof(AssemblyContextControlItem)); List declaredVariables = VariableHelper.FindUniqueVariablesInScope(this.OwnerActivity); declaredVariables.AddRange(VariableHelper.FindActionHandlerVariables(this.OwnerActivity)); //if expression editor is loaded in the WF which is hosted within ActivityBuilderHelper, there is a need to pickup defined arguments //and feed them as variables, so intelisense can include them if (null != this.OwnerActivity && ActivityBuilderHelper.IsActivityBuilderType(this.OwnerActivity.Root)) { ModelTreeManager treeManager = ((IModelTreeItem)this.OwnerActivity).ModelTreeManager; //call ActivityBuilderHelper.GetVariables - it will create a collection of Variable - each variable corresponds to a specific argument declaredVariables.AddRange( ActivityBuilderHelper.GetVariables(this.OwnerActivity.Root). //create a fake model item implementation - there is no need to store that model item anywhere in the model tree, it is requirement //of the expression editor interface to pass instances of model items wrapping variables, rather than actual variables Select (entry => new FakeModelItemImpl(treeManager, typeof(Variable), entry, null))); } ImportedNamespaceContextItem importedNamespaces = this.Context.Items.GetValue (); importedNamespaces.EnsureInitialized(this.Context); //if the expression text is empty and the expression type is set, then we initialize the text to prompt text if (String.Equals(this.ExpressionText, string.Empty, StringComparison.OrdinalIgnoreCase) && this.ExpressionType != null) { this.Text = TypeToPromptTextConverter.GetPromptText(this.ExpressionType); } //this is a hack this.blockWidth = Math.Max(this.ActualWidth - 8, 0); //8 is the margin if (this.HasErrors) { this.blockWidth = Math.Max(this.blockWidth - 16, 0); //give 16 for error icon } try { if (this.ExpressionType != null) { this.expressionEditorInstance = this.expressionEditorService.CreateExpressionEditor(assemblies, importedNamespaces, declaredVariables, this.Text, this.ExpressionType, new Size(this.blockWidth, this.blockHeight)); } else { this.expressionEditorInstance = this.expressionEditorService.CreateExpressionEditor(assemblies, importedNamespaces, declaredVariables, this.Text, new Size(this.blockWidth, this.blockHeight)); } } catch (Exception ex) { Trace.WriteLine(ex.Message); } if (this.expressionEditorInstance != null) { try { this.expressionEditorInstance.VerticalScrollBarVisibility = this.VerticalScrollBarVisibility; this.expressionEditorInstance.HorizontalScrollBarVisibility = this.HorizontalScrollBarVisibility; this.expressionEditorInstance.AcceptsReturn = this.AcceptsReturn; this.expressionEditorInstance.AcceptsTab = this.AcceptsTab; // Add the expression editor to the text panel, at column 1 this.hostControl = this.expressionEditorInstance.HostControl; // Subscribe to this event to change scrollbar visibility on the fly for auto, and to resize the hostable editor // as necessary this.expressionEditorInstance.LostAggregateFocus += new EventHandler(OnEditorLostAggregateFocus); this.expressionEditorInstance.Closing += new EventHandler(OnEditorClosing); // Set up Hostable Editor properties this.expressionEditorInstance.MinLines = this.MinLines; this.expressionEditorInstance.MaxLines = this.MaxLines; this.expressionEditorInstance.HostControl.Style = (Style)FindResource("editorStyle"); border.Child = this.hostControl; this.expressionEditorInstance.Focus(); } catch (KeyNotFoundException ex) { Debug.Fail("Unable to find editor with the following editor name: " + this.editorName, ex.Message); } } } if (this.expressionEditorInstance == null) { this.ContentTemplate = (DataTemplate)FindResource("textbox"); } this.PerfProvider.WorkflowDesignerExpressionEditorLoaded(); } void OnEditorClosing(object sender, EventArgs e) { if (this.expressionEditorInstance != null) { //these events are expected to be unregistered during lost focus event, but //we are unregistering them during unload just in case. Ideally we want to //do this in the CloseExpressionEditor method this.expressionEditorInstance.LostAggregateFocus -= new EventHandler(OnEditorLostAggregateFocus); this.expressionEditorInstance.Closing -= new EventHandler(OnEditorClosing); this.expressionEditorInstance = null; } Border boarder = this.hostControl.Parent as Border; if (boarder != null) { boarder.Child = null; } this.hostControl = null; this.editorName = null; } void OnEditorLostAggregateFocus(object sender, EventArgs e) { this.DoLostFocus(); } //void BindEditorProperties() //{ // this.hostControl.SetBinding(Control.ContextMenuProperty, "ContextMenu"); // this.hostControl.SetBinding(Control.FlowDirectionProperty, "FlowDirection"); // this.hostControl.SetBinding(Control.FontFamilyProperty, "FontFamily"); // this.hostControl.SetBinding(Control.FontSizeProperty, "FontSize"); // this.hostControl.SetBinding(Control.FontStretchProperty, "FontStretch"); // this.hostControl.SetBinding(Control.FontStyleProperty, "FontStyle"); // this.hostControl.SetBinding(Control.FontWeightProperty, "FontWeight"); // this.hostControl.SetBinding(Control.HeightProperty, "Height"); // this.hostControl.SetBinding(Control.LanguageProperty, "Language"); // this.hostControl.SetBinding(Control.SnapsToDevicePixelsProperty, "SnapsToDevicePixels"); //} // This method is called when the editor data template is unloaded - when the editor data template // is unloaded, close the editor session and set the expression editor and editor session to null void OnEditorUnloaded(object sender, RoutedEventArgs e) { // Blank the editorSession and the expressionEditor so as not to use up memory // Destroy both as you can only ever spawn one editor per session if (this.expressionEditorInstance != null) { //if we are unloaded during editing, this means we got here by someone clicking breadcrumb, we should try to commit if (this.EditingState == EditingState.Editing) { this.Commit(false); } this.expressionEditorInstance.Close(); } } // This method is to give focus and set the caret position when the TextBox DataTemplate is loaded void OnTextBoxLoaded(object sender, RoutedEventArgs e) { TextBox textbox = (TextBox)sender; textbox.ContextMenu = null; //to workaround a but in the TextBox layout code Binding binding = new Binding(); binding.Source = this; binding.Path = new PropertyPath(ExpressionTextBox.TextProperty); binding.Mode = BindingMode.TwoWay; binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; textbox.SetBinding(TextBox.TextProperty, binding); // Set the cursor to correct text position int index = GetCharacterIndexFromPoint(textbox); textbox.SelectionStart = index; textbox.SelectionLength = 0; textbox.Focus(); } // This method is to workaround the fact that textbox.GetCharacterIndexFromPoint returns the caret // to the left of the character... Thus you can never get the caret after the last character in the // expression string. int GetCharacterIndexFromPoint(TextBox textbox) { Point position = Mouse.GetPosition(textbox); int index = textbox.GetCharacterIndexFromPoint(position, false); if (index < 0) { // May have clicked outside the text area, get the index of nearest char index = textbox.GetCharacterIndexFromPoint(position, true); if (index < 0) { index = 0; } // Adjust the cursor position if we clicked to the right of returned character Rect charRect = textbox.GetRectFromCharacterIndex(index, true); if (position.X > charRect.Left + charRect.Width / 2) { index++; } } return index; } static void ValidateExpression(ExpressionTextBox etb) { string errorMessage; if (etb.DoValidation(new ExpressionValidationContext(etb), out errorMessage)) { etb.UpdateValidationError(errorMessage); } } static void OnLinesChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ExpressionTextBox textBox = (ExpressionTextBox)dependencyObject; if (textBox.MinLines > textBox.MaxLines) { textBox.MaxLines = textBox.MinLines; } } static void OnTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ExpressionTextBox textBox = (ExpressionTextBox)dependencyObject; if (textBox.ExpressionEditorService != null && textBox.expressionEditorInstance != null) { textBox.expressionEditorInstance.Text = textBox.Text; } } static void OnOwnerModelItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ExpressionTextBox textBox = (ExpressionTextBox)dependencyObject; textBox.InitializeContext(); textBox.OnPathToArgumentChanged(textBox.PathToArgument); } public IExpressionEditorService ExpressionEditorService { get { return this.expressionEditorService; } } internal string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } internal string ExpressionText { get { return (string)GetValue(ExpressionTextProperty); } set { SetValue(ExpressionTextProperty, value); } } public string HintText { get { return (string)GetValue(HintTextProperty); } set { SetValue(HintTextProperty, value); } } [Fx.Tag.KnownXamlExternal] public ModelItem Expression { get { return (ModelItem)GetValue(ExpressionTextBox.ExpressionProperty); } set { SetValue(ExpressionTextBox.ExpressionProperty, value); } } public Type ExpressionType { get { return (Type)GetValue(ExpressionTextBox.ExpressionTypeProperty); } set { SetValue(ExpressionTextBox.ExpressionTypeProperty, value); } } [Fx.Tag.KnownXamlExternal] public ModelItem OwnerActivity { get { return (ModelItem)GetValue(ExpressionTextBox.OwnerActivityProperty); } set { SetValue(ExpressionTextBox.OwnerActivityProperty, value); } } internal ValidationState ValidationState { get { return (ValidationState)GetValue(ExpressionTextBox.ValidationStateProperty); } set { SetValue(ExpressionTextBox.ValidationStateProperty, value); } } public bool UseLocationExpression { get { return (bool)GetValue(ExpressionTextBox.UseLocationExpressionProperty); } set { SetValue(ExpressionTextBox.UseLocationExpressionProperty, value); } } public ScrollBarVisibility VerticalScrollBarVisibility { get { return (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty); } set { SetValue(VerticalScrollBarVisibilityProperty, value); } } public ScrollBarVisibility HorizontalScrollBarVisibility { get { return (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty); } set { SetValue(HorizontalScrollBarVisibilityProperty, value); } } public int MaxLines { get { return (int)GetValue(MaxLinesProperty); } set { SetValue(MaxLinesProperty, value); } } public int MinLines { get { return (int)GetValue(MinLinesProperty); } set { SetValue(MinLinesProperty, value); } } public bool ExplicitCommit { get { return (bool)GetValue(ExplicitCommitProperty); } set { SetValue(ExplicitCommitProperty, value); } } internal bool HasErrors { get { bool hasErrors = false; if (this.EditingState == EditingState.Idle && !this.IsIndependentExpression) { if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null) { ValidationState state = this.ValidationService.ValidationStateProperty.Getter(this.Expression); hasErrors = state == ValidationState.Error; } } else { hasErrors = this.HasValidationError; } return hasErrors; } } internal string ErrorMessage { get { string errorMessage = string.Empty; if (this.EditingState == EditingState.Idle && !this.IsIndependentExpression) { if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null) { errorMessage = this.ValidationService.ValidationMessageProperty.Getter(this.Expression); } } else { errorMessage = this.ValidationErrorMessage; } return errorMessage; } } internal EditingState EditingState { get { return (EditingState)GetValue(EditingStateProperty); } set { SetValue(EditingStateProperty, value); } } internal bool HasValidationError { get { return (bool)GetValue(HasValidationErrorProperty); } set { SetValue(HasValidationErrorProperty, value); } } internal string ValidationErrorMessage { get { return (string)GetValue(ValidationErrorMessageProperty); } set { SetValue(ValidationErrorMessageProperty, value); } } public string PathToArgument { get { return (string)GetValue(PathToArgumentProperty); } set { SetValue(PathToArgumentProperty, value); } } public bool IsSupportedExpression { get { return (bool)GetValue(IsSupportedExpressionProperty); } set { SetValue(IsSupportedExpressionProperty, value); } } public string DefaultValue { get { return (string)GetValue(DefaultValueProperty); } set { SetValue(DefaultValueProperty, value); } } public bool AcceptsReturn { get { return (bool)GetValue(AcceptsReturnProperty); } set { SetValue(AcceptsReturnProperty, value); } } public bool AcceptsTab { get { return (bool)GetValue(AcceptsTabProperty); } set { SetValue(AcceptsTabProperty, value); } } public bool IsReadOnly { get { return (bool)GetValue(IsReadOnlyProperty); } set { SetValue(IsReadOnlyProperty, value); } } internal bool IsIndependentExpression { get { return (bool)GetValue(IsIndependentExpressionProperty); } set { SetValue(IsIndependentExpressionProperty, value); } } EditingContext Context { get { if (this.context == null) { if (this.OwnerActivity != null) { InitializeContext(); } } return this.context; } } DesignerPerfEventProvider PerfProvider { get { if (this.perfProvider == null && this.Context != null) { perfProvider = this.Context.Services.GetService (); } return this.perfProvider; } } ValidationService ValidationService { get { if (this.Context != null) { return this.Context.Services.GetService (); } else { return null; } } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] internal void GenerateExpression() { Activity valueExpression = null; // If the text is null we don't need to bother generating the expression (this would be the case the // first time you enter an ETB. We still need to generate the expression when it is EMPTY however - otherwise // the case where you had an expression (valid or invalid), then deleted the whole thing will not be evaluated. if (this.Text != null) { using (ModelEditingScope scope = this.OwnerActivity.BeginEdit(SR.PropertyChangeEditingScopeDescription)) { this.EditingState = EditingState.Validating; // we set the expression to null // a) when the expressionText is empty AND it's a reference expression or // b) when the expressionText is empty AND the DefaultValue property is null if (this.Text.Length == 0 && (this.UseLocationExpression || this.DefaultValue == null)) { valueExpression = null; } else { if (this.Text.Length == 0) { this.Text = this.DefaultValue; } valueExpression = CreateVBExpression(); } CreateExpressionModelItem(valueExpression); scope.Complete(); } } } void OnValidationCompleted(object sender, EventArgs e) { if (this.EditingState != EditingState.Editing) { if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null) { this.ValidationState = this.ValidationService.ValidationStateProperty.Getter(this.Expression); } this.EditingState = EditingState.Idle; } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] private void CreateExpressionModelItem(object valueExpression) { // try to wrap the droppedObject in a ModelItem. ModelItem expressionModelItem = null; if (valueExpression != null) { ModelServiceImpl modelService = (ModelServiceImpl)this.Context.Services.GetService (); expressionModelItem = modelService.WrapAsModelItem(valueExpression); expressionModelItem.PropertyChanged += this.OnExpressionModelItemChanged; this.boundedExpression = expressionModelItem; } try { this.internalModelItemChange = true; this.EditingState = EditingState.Validating; SetValue(ExpressionProperty, expressionModelItem); } catch (Exception err) { Trace.WriteLine(string.Format(CultureInfo.CurrentUICulture, "{0}\r\n{1}", err.Message, err.StackTrace)); this.internalModelItemChange = false; } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] private Activity CreateVBExpression() { Activity valueExpression = null; if (this.OwnerActivity != null) { ExpressionValidationContext validationContext = new ExpressionValidationContext(this); Type expReturnType = null; string newExpressionText = null; SourceExpressionException compileErrorMessages; try { VisualBasicSettings settings = null; valueExpression = CreateVBExpression(validationContext, out newExpressionText, out expReturnType, out compileErrorMessages, out settings); if (settings != null) { Fx.Assert(this.Context != null, "editing context cannot be null"); //merge with import designer foreach (VisualBasicImportReference reference in settings.ImportReferences) { ImportDesigner.AddImport(reference.Import, this.Context); } } if (!string.IsNullOrEmpty(newExpressionText)) { this.previousText = this.Text; this.Text = newExpressionText; } } catch (Exception err) { //if the VisualBasicDesignerHelper were able to resolve the type we use that if (expReturnType == null) { //if not we try to use the expression type expReturnType = this.ExpressionType; } //if expression type is also null, the we use object if (expReturnType == null) { expReturnType = typeof(object); } valueExpression = ExpressionHelper.CreateExpression(expReturnType, this.Text, UseLocationExpression, validationContext.ParserContext); Trace.WriteLine(string.Format(CultureInfo.CurrentUICulture, "{0}\r\n{1}", err.Message, err.StackTrace)); } this.ExpressionText = this.Text; } else { // If the OwnerActivity is null, do not try to compile the expression otherwise VS will crash // Inform the user that OwnerActivity is null (i.e. there is a error in their code) Trace.WriteLine("ExpressionTextBox OwnerActivity is null."); } return valueExpression; } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The conversion to an expression might fail due to invalid user input. Propagating exceptions might lead to VS crash.")] private static Activity CreateVBExpression(ExpressionValidationContext context, out string newExpressionText, out Type expReturnType, out SourceExpressionException compileErrorMessages, out VisualBasicSettings vbSettings) { expReturnType = null; newExpressionText = null; compileErrorMessages = null; vbSettings = null; //try easy way first - look if there is a type converer which supports convertsion between expression type and string ActivityWithResult literal = null; try { if (!context.UseLocationExpression) { literal = ExpressionHelper.TryCreateLiteral(context.ExpressionType, context.ExpressionText, context.ParserContext); } if (literal != null) { //need to get new expression text - converter might have changed its format, and we want it to be up to date IValueSerializableExpression serializableExpression = literal as IValueSerializableExpression; Fx.Assert(serializableExpression != null, "the expression has to be a Literal<>, which should be IValueSerializableExpression"); if (serializableExpression.CanConvertToString(context.ParserContext)) { bool shouldBeQuoted = typeof(string) == context.ExpressionType || typeof(Uri) == context.ExpressionType; //whether string begins and ends with quotes '"'. also, if there are //more quotes within than those begining and ending ones, do not bother with literal - assume this is an expression. bool isQuotedString = shouldBeQuoted && context.ExpressionText.StartsWith("\"", StringComparison.CurrentCulture) && context.ExpressionText.EndsWith("\"", StringComparison.CurrentCulture) && context.ExpressionText.IndexOf("\"", 1, StringComparison.CurrentCulture) == context.ExpressionText.Length - 1; var formatString = isQuotedString ? "\"{0}\"" : "{0}"; newExpressionText = string.Format(CultureInfo.InvariantCulture, formatString, serializableExpression.ConvertToString(context.ParserContext)); } } } //conversion failed - do nothing, let VB compiler take care of the expression catch { } Activity valueExpression = literal; if (null == valueExpression) { if (!context.UseLocationExpression) { //Compile for validation. valueExpression = VisualBasicDesignerHelper.CreatePrecompiledVisualBasicValue(context.ExpressionType, context.ExpressionText, context.ParserContext.Namespaces, context.ReferencedAssemblies, context.ParserContext, out expReturnType, out compileErrorMessages, out vbSettings); } else { //Compile for validation. valueExpression = VisualBasicDesignerHelper.CreatePrecompiledVisualBasicReference(context.ExpressionType, context.ExpressionText, context.ParserContext.Namespaces, context.ReferencedAssemblies, context.ParserContext, out expReturnType, out compileErrorMessages, out vbSettings); } } return valueExpression; } static void OnExpressionTypeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = obj as ExpressionTextBox; Fx.Assert (etb != null, "Sender must be an ETB"); //for independent expressions, when the type changes, we need to validate the expressions if (etb.initialized && etb.IsIndependentExpression && etb.EditingState == EditingState.Idle) { ValidateExpression(etb); } } static void OnUseLocationExpressionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = obj as ExpressionTextBox; Fx.Assert(etb != null, "Sender must be an ETB"); //for independent expressions, when the type changes, we need to validate the expressions if (etb.initialized && etb.IsIndependentExpression && etb.EditingState == EditingState.Idle && etb.OwnerActivity != null) { ValidateExpression(etb); } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")] static void OnExpressionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { ExpressionTextBox ctrl = (ExpressionTextBox)obj; ModelItem oldExpression = e.OldValue as ModelItem; if (oldExpression != null) { oldExpression.PropertyChanged -= ctrl.OnExpressionModelItemChanged; } ModelItem expression = e.NewValue as ModelItem; if (expression != null && expression != ctrl.boundedExpression) { expression.PropertyChanged += ctrl.OnExpressionModelItemChanged; } try { ctrl.boundedExpression = expression; ctrl.OnExpressionChanged(); } catch (Exception err) { //if context is set - use error reporting if (null != ctrl.Context) { ctrl.Context.Items.SetValue(new ErrorItem() { Message = err.Message, Details = err.ToString() }); } //otherwise - fallback to message box else { MessageBox.Show(err.ToString(), err.Message); } } } void expressionModelItem_PropertyChanged(object sender, PropertyChangedEventArgs e) { ModelItem item = sender as ModelItem; if (item != null) { if (e.PropertyName.Equals("ExpressionText", StringComparison.Ordinal)) { this.OnExpressionChanged(); } } } static void OnPathToArgumentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { ExpressionTextBox ctrl = (ExpressionTextBox)obj; ctrl.OnPathToArgumentChanged((string)e.NewValue); } static void OnIsIndependentExpressionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = dependencyObject as ExpressionTextBox; Fx.Assert(etb != null, "Sender must be an ETB"); //if this is an independent expression, we need to initialize the validation error because validation service will not validate it if (etb.initialized && etb.IsIndependentExpression && etb.EditingState == EditingState.Idle) { ValidateExpression(etb); } } void UpdateValidationState() { if (this.Expression != null && this.ValidationService != null && this.ValidationService.ValidationStateProperty != null) { this.ValidationState = this.ValidationService.ValidationStateProperty.Getter(this.Expression); } else { this.ValidationState = ValidationState.Valid; } } //We need to react to OnExpressionChanged, since there might be multiple ExpressionTextBoxes(ETB) associated to a single Expression. //All the ETBs should be updated if the value in any of the ETBs is changed. void OnExpressionChanged() { if (!this.internalModelItemChange) { if (this.Expression == null) { //new expression is null - there is no text, no previous text, erros should be clear as well as error message this.Text = string.Empty; this.previousText = this.Text; this.ExpressionText = null; this.ValidationState = ValidationState.Valid; } else { this.UpdateValidationState(); object expressionObject = this.Expression.GetCurrentValue(); Activity expression = null; String expressionString = null; //create parser context - do not pass ownerActivity - it might be null at this time ParserContext context = new ParserContext(); expression = expressionObject as Activity; //get the expression text out of expression - in case of non-VB expression, context may be populated with //extra namespace information expressionString = ExpressionHelper.GetExpressionString(expression, context); if (expressionString != null) { Fx.Assert(expression != null, "if expression is null, expressionString should also be null"); Type expressionType = expression.GetType(); Type expressionArgumentType = expressionType.IsGenericType ? expressionType.GetGenericArguments()[0] : typeof(object); bool isValid = false; bool isLiteral = false; //try to figure out what kind of expression are we dealing with while (null != expressionType) { //check if expression is literal expression if (expressionType.IsGenericType && typeof(Literal<>) == expressionType.GetGenericTypeDefinition()) { isLiteral = true; } //expression must be of type Activity if (expressionType.IsGenericType && typeof(Activity<>) == expressionType.GetGenericTypeDefinition()) { isValid = true; break; } expressionType = expressionType.BaseType; } if (!isValid) { throw FxTrace.Exception.AsError(new NotSupportedException()); } this.Text = expressionString; this.ExpressionText = expressionString; this.previousText = this.Text; //if expression is a literal, which is not supported, delegate a call to regenerate it as expression if (isLiteral && !ExpressionHelper.CanTypeBeSerializedAsLiteral( expressionArgumentType )) { //call should be postponed until whole ux is loaded. this.Dispatcher.BeginInvoke(new Action(() => { this.GenerateExpression(); }), DispatcherPriority.ApplicationIdle); } } else { //We won't handle non-VB expressions this.IsSupportedExpression = false; this.HintText = SR.UnsupportedExpressionHintText; } this.isExpressionLoaded = true; if (this.isBeginEditPending) { this.BeginEdit(); } } } internalModelItemChange = false; } void OnPathToArgumentChanged(string pathAsString) { this.expressionModelProperty = null; this.expressionConverter = null; if (!string.IsNullOrEmpty(pathAsString) && null != this.OwnerActivity) { string[] path = pathAsString.Split('.'); if (path.Length > 0) { this.expressionModelProperty = this.OwnerActivity.Properties[path[0]]; for (int i = 1; i < path.Length; ++i) { if (null != this.expressionModelProperty && null != this.expressionModelProperty.Value) { this.expressionModelProperty = this.expressionModelProperty.Value.Properties[path[i]]; } else { this.expressionModelProperty = null; break; } } } } if (null != this.expressionModelProperty) { this.expressionConverter = ((ModelPropertyImpl)this.expressionModelProperty).PropertyDescriptor.Converter; } this.InitializeHintText(); } void InitializeHintText() { DescriptionAttribute customHint = null; if (this.expressionModelProperty != null && this.expressionModelProperty.Attributes.Count != 0) { customHint = this.expressionModelProperty.Attributes. OfType (). FirstOrDefault(); } this.HintText = (null == customHint || string.IsNullOrEmpty(customHint.Description) ? (string.Equals(this.HintText, SR.ExpressionDefaultText) ? SR.ExpressionDefaultText : this.HintText) : customHint.Description); string hint = this.HintText.Trim(new char[] { '<', '>' }); this.SetValue(AutomationProperties.HelpTextProperty, hint); } void InitializeContext() { if (this.context == null) { ModelItem modelItem = this.OwnerActivity; Fx.Assert(modelItem != null, "OwnerActivity is not set before calling InitializeContext()"); if (null != modelItem) { this.context = modelItem.GetEditingContext(); if (this.ValidationService != null) { this.ValidationService.ValidationCompleted += this.OnValidationCompleted; this.UpdateValidationState(); } } } } #region Command Handlers void OnCommitCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { string currentText = this.Text; if (this.expressionEditorInstance != null) { currentText = this.expressionEditorInstance.Text; if (!string.IsNullOrEmpty(currentText)) { currentText = currentText.Trim(); } } //we dont need to commit change if currentText and previousText is the same //null and empty should be considered equal in this context e.CanExecute = !string.Equals(currentText, this.previousText) && !(string.IsNullOrEmpty(currentText) && string.IsNullOrEmpty(this.previousText)); e.Handled = true; } void OnCommitCommandExecute(object sender, ExecutedRoutedEventArgs e) { this.Commit(true); e.Handled = true; } void OnExpressionTextBoxLoaded(object sender, RoutedEventArgs e) { this.InitializeHintText(); //if this is an independent expression, we need to initialize the validation error because validation service will not validate it if (this.IsIndependentExpression) { ValidateExpression(this); } this.initialized = true; } void OnExpressionTextBoxUnloaded(object sender, RoutedEventArgs e) { if (this.ValidationService != null) { this.ValidationService.ValidationCompleted -= this.OnValidationCompleted; } KillValidator(); if (this.boundedExpression != null) { this.boundedExpression.PropertyChanged -= this.OnExpressionModelItemChanged; } } void OnCompleteWordCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanCompleteWord(); e.Handled = true; } else { e.Handled = false; } } void OnGlobalIntellisenseCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanGlobalIntellisense(); e.Handled = true; } else { e.Handled = false; } } void OnParameterInfoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanParameterInfo(); e.Handled = true; } else { e.Handled = false; } } void OnQuickInfoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanQuickInfo(); e.Handled = true; } else { e.Handled = false; } } void OnIncreaseFilterLevelCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanIncreaseFilterLevel(); e.Handled = true; } else { e.Handled = false; } } void OnDecreaseFilterLevelCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanDecreaseFilterLevel(); e.Handled = true; } else { e.Handled = false; } } void OnCutCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanCut(); e.Handled = true; } else { e.Handled = false; } } void OnCompleteWordCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.CompleteWord(); } else { e.Handled = false; } } void OnGlobalIntellisenseCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.GlobalIntellisense(); } else { e.Handled = false; } } void OnParameterInfoCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.ParameterInfo(); } else { e.Handled = false; } } void OnQuickInfoCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.QuickInfo(); } else { e.Handled = false; } } void OnDecreaseFilterLevelCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.DecreaseFilterLevel(); } else { e.Handled = false; } } void OnIncreaseFilterLevelCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.IncreaseFilterLevel(); } else { e.Handled = false; } } void OnCutCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.Cut(); } else { e.Handled = false; } } void OnCopyCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanCopy(); e.Handled = true; } else { e.Handled = false; } } void OnCopyCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.Copy(); } else { e.Handled = false; } } void OnPasteCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanPaste(); e.Handled = true; } else { e.Handled = false; } } void OnPasteCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.Paste(); } else { e.Handled = false; } } void OnUndoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanUndo(); e.Handled = true; } else { e.Handled = false; } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Catch all execeptions to prevent crash.")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "Catch all execeptions to prevent crash.")] void OnUndoCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { try { e.Handled = this.expressionEditorInstance.Undo(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } else { e.Handled = false; } } void OnRedoCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.CanExecute = this.expressionEditorInstance.CanRedo(); e.Handled = true; } else { e.Handled = false; } } void OnRedoCommandExecute(object sender, ExecutedRoutedEventArgs e) { if (this.expressionEditorInstance != null) { e.Handled = this.expressionEditorInstance.Redo(); } else { e.Handled = false; } } void OnHelpExecute(object sender, ExecutedRoutedEventArgs e) { IIntegratedHelpService help = this.Context.Services.GetService (); if (help != null) { help.ShowHelpFromKeyword(HelpKeywords.ExpressionEditorPage); } else { System.Diagnostics.Process.Start(SR.DefaultHelpUrl); } } #endregion static void OnHintTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if (!string.Equals(SR.ExpressionDefaultText, e.NewValue) && !string.Equals(e.OldValue, e.NewValue)) { ((ExpressionTextBox)sender).InitializeHintText(); } } static void OnAcceptsReturnChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = sender as ExpressionTextBox; if (etb != null && etb.expressionEditorInstance != null) { etb.expressionEditorInstance.AcceptsReturn = (bool)e.NewValue; } } static void OnAcceptsTabChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { ExpressionTextBox etb = sender as ExpressionTextBox; if (etb != null && etb.expressionEditorInstance != null) { etb.expressionEditorInstance.AcceptsTab = (bool)e.NewValue; } } void StartValidator() { if (this.validator == null) { this.validator = new BackgroundWorker(); this.validator.WorkerReportsProgress = true; this.validator.WorkerSupportsCancellation = true; this.validator.DoWork += delegate(object obj, DoWorkEventArgs args) { BackgroundWorker worker = obj as BackgroundWorker; if (worker.CancellationPending) { args.Cancel = true; return; } ExpressionValidationContext validationContext = args.Argument as ExpressionValidationContext; if (validationContext != null) { string errorMessage; if (DoValidation(validationContext, out errorMessage)) { worker.ReportProgress(0, errorMessage); } //sleep if (worker.CancellationPending) { args.Cancel = true; return; } Thread.Sleep(ValidationWaitTime); args.Result = validationContext; } }; this.validator.RunWorkerCompleted += delegate(object obj, RunWorkerCompletedEventArgs args) { if (!args.Cancelled) { ExpressionValidationContext validationContext = args.Result as ExpressionValidationContext; if (validationContext != null) { Dispatcher.BeginInvoke(new Action ((target)=>{ //validator could be null by the time we try to validate again or //if it's already busy if (this.validator != null && !this.validator.IsBusy) { target.Update(this); this.validator.RunWorkerAsync(target); } }), validationContext); } } }; this.validator.ProgressChanged += delegate(object obj, ProgressChangedEventArgs args) { string error = args.UserState as string; Dispatcher.BeginInvoke(new Action (UpdateValidationError), error); }; this.validator.RunWorkerAsync(new ExpressionValidationContext(this)); } } //perform one validation synchronously //return value indicates whether errorMessage is updated. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")] [SuppressMessage("Reliability", "Reliability108:IsFatalRule", Justification = "The entered expression might be invalid and may throw on deserialization. Propagating exception might lead to VS crash")] bool DoValidation(ExpressionValidationContext validationContext, out string errorMessage) { Fx.Assert(validationContext != null, "only work when context is not null"); errorMessage = null; //validate //if the text is empty we clear the error message if (string.IsNullOrEmpty(validationContext.ExpressionText)) { errorMessage = null; return true; } // if the expression text is different from the last time we run the validation we run the validation else if (!string.Equals(validationContext.ExpressionText, validationContext.ValidatedExpressionText)) { Type expReturnType = null; string newExpressionText = null; SourceExpressionException compileErrorMessages = null; VisualBasicSettings settings = null; try { CreateVBExpression(validationContext, out newExpressionText, out expReturnType, out compileErrorMessages, out settings); if (compileErrorMessages != null) { errorMessage = compileErrorMessages.Message; } } catch (Exception err) { errorMessage = err.Message; } return true; } return false; } void UpdateValidationError(string errorMessage) { if (!string.IsNullOrEmpty(errorMessage)) { //report error this.HasValidationError = true; this.ValidationErrorMessage = errorMessage; } else { this.HasValidationError = false; this.ValidationErrorMessage = null; } } private class ExpressionValidationContext { internal ParserContext ParserContext { get; set; } internal Type ExpressionType { get; set; } internal String ExpressionText { get; set; } internal EditingContext EditingContext { get; set; } internal String ValidatedExpressionText { get; set; } internal bool UseLocationExpression { get; set; } internal ExpressionValidationContext(ExpressionTextBox etb) { Update(etb); } internal void Update(ExpressionTextBox etb) { Fx.Assert(etb.OwnerActivity != null, "Owner Activity is null"); this.EditingContext = etb.OwnerActivity.GetEditingContext(); //setup ParserContext this.ParserContext = new ParserContext(etb.OwnerActivity) { //callee is a ExpressionTextBox Instance = etb, //pass property descriptor belonging to epression's model property (if one exists) PropertyDescriptor = (null != etb.expressionModelProperty ? ((ModelPropertyImpl)etb.expressionModelProperty).PropertyDescriptor : null), }; this.ExpressionType = etb.ExpressionType; this.ValidatedExpressionText = this.ExpressionText; if (etb.expressionEditorInstance != null) { this.ExpressionText = etb.expressionEditorInstance.Text; } else { this.ExpressionText = etb.Text; } this.UseLocationExpression = etb.UseLocationExpression; } internal IEnumerable ReferencedAssemblies { get { Fx.Assert(this.EditingContext != null, "ModelItem.Context = null"); AssemblyContextControlItem assemblyContext = this.EditingContext.Items.GetValue (); if (assemblyContext != null) { return assemblyContext.AllAssemblyNamesInContext; } return null; } } } } internal sealed class LineToHeightConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { double convertedValue = Double.NaN; bool isDefault = true; // Calculate the height for the textblock as ExpressionTextBox exposes lines properties, // and TextBlock doesn't have lines properties. FontFamily fontFamily = values.OfType ().FirstOrDefault(); int lines = values.OfType ().FirstOrDefault(); double[] doubleArray = values.OfType ().ToArray (); if (doubleArray.Length == 2) { double height = doubleArray[0]; // The first element of the array is going to be the height double fontSize = doubleArray[1]; // The seconed element of the array is going to be the fontSize // 0.0 is default for MinHeight, PositiveInfinity is default for MaxHeight if (string.Equals(parameter as string, "MinHeight")) { isDefault = (height == 0.0); } else if (string.Equals(parameter as string, "MaxHeight")) { isDefault = (double.IsPositiveInfinity(height)); } // If the height value we are evaluating is default, use Lines for sizing... // If no heights (height or lines) have been explicitly specified, we would rather default the height // as if the Line was 1 - so use the line heights, rather than 0.0 and/or PositiveInfinity. if (isDefault) { double lineHeight = fontSize * fontFamily.LineSpacing; if (fontFamily != null) { convertedValue = lineHeight * (double)lines + 4; } } else { convertedValue = height; } } return convertedValue; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw FxTrace.Exception.AsError(new NotSupportedException()); } } internal sealed class ValidationStateToErrorConverter : IMultiValueConverter { #region IMultiValueConverter Members public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { ExpressionTextBox etb = values[0] as ExpressionTextBox; if (values[0] == DependencyProperty.UnsetValue || etb == null) { return false; } return etb.HasErrors; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw FxTrace.Exception.AsError(new NotImplementedException()); } #endregion } internal sealed class ValidationErrorMessageConverter : IMultiValueConverter { #region IMultiValueConverter Members public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { ExpressionTextBox etb = values[0] as ExpressionTextBox; if (values[0] == DependencyProperty.UnsetValue || etb == null) { return false; } return etb.ErrorMessage; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw FxTrace.Exception.AsError(new NotImplementedException()); } #endregion } internal sealed class TypeToPromptTextConverter : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return TypeToPromptTextConverter.GetPromptText(value); } internal static string GetPromptText(object value) { Type expressionType = value as Type; if (value == DependencyProperty.UnsetValue || expressionType == null || !expressionType.IsValueType) { return "Nothing"; } else { return Activator.CreateInstance(expressionType).ToString(); } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw FxTrace.Exception.AsError(new NotSupportedException()); } #endregion } public enum EditingState { Editing, Validating, Idle } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SearchForVirtualItemEventArgs.cs
- X509UI.cs
- GCHandleCookieTable.cs
- ResourceSetExpression.cs
- ManagementEventArgs.cs
- FixedSOMTableCell.cs
- GridViewDeleteEventArgs.cs
- SiteMapHierarchicalDataSourceView.cs
- StatusStrip.cs
- ToolStripTextBox.cs
- NotCondition.cs
- SafeEventLogWriteHandle.cs
- OdbcError.cs
- User.cs
- ConvertersCollection.cs
- IndexedString.cs
- StatusBarItemAutomationPeer.cs
- elementinformation.cs
- InkCanvas.cs
- MemoryMappedFile.cs
- RequestQueryProcessor.cs
- SerialErrors.cs
- CompositionTarget.cs
- DetailsViewRowCollection.cs
- InputScope.cs
- UnsafeNativeMethods.cs
- SystemIPGlobalStatistics.cs
- AncillaryOps.cs
- AuthenticationManager.cs
- DynamicQueryStringParameter.cs
- DesignerDataColumn.cs
- DefaultParameterValueAttribute.cs
- RenderDataDrawingContext.cs
- BulletedListEventArgs.cs
- CommonGetThemePartSize.cs
- DescendantOverDescendantQuery.cs
- DataGridViewRowCancelEventArgs.cs
- DynamicControlParameter.cs
- SqlInternalConnectionSmi.cs
- DrawingBrush.cs
- Invariant.cs
- BasicExpandProvider.cs
- FlowDocumentView.cs
- CollectionChangeEventArgs.cs
- ConnectionPointCookie.cs
- IntegerValidatorAttribute.cs
- MatrixKeyFrameCollection.cs
- PickBranch.cs
- DtdParser.cs
- Int16Animation.cs
- GrammarBuilderDictation.cs
- StringBuilder.cs
- Gdiplus.cs
- DataControlCommands.cs
- ContentElementAutomationPeer.cs
- ImportRequest.cs
- UInt16Storage.cs
- XmlReflectionMember.cs
- EventLogPermissionAttribute.cs
- Quaternion.cs
- Guid.cs
- Matrix.cs
- LoadWorkflowByInstanceKeyCommand.cs
- RegexCaptureCollection.cs
- VariableQuery.cs
- unitconverter.cs
- FixedLineResult.cs
- AtlasWeb.Designer.cs
- CapacityStreamGeometryContext.cs
- PropertyDescriptor.cs
- OperationContractGenerationContext.cs
- designeractionlistschangedeventargs.cs
- TransformPattern.cs
- SqlTopReducer.cs
- TripleDESCryptoServiceProvider.cs
- GPStream.cs
- BinaryEditor.cs
- GuidTagList.cs
- PrePrepareMethodAttribute.cs
- TypeDescriptor.cs
- ChangeToolStripParentVerb.cs
- odbcmetadatafactory.cs
- PagerStyle.cs
- XmlParserContext.cs
- ApplicationTrust.cs
- SelectionItemProviderWrapper.cs
- GradientStop.cs
- Peer.cs
- RepeatButton.cs
- LostFocusEventManager.cs
- ActivityPropertyReference.cs
- ControlIdConverter.cs
- EndpointAddressProcessor.cs
- ListBase.cs
- InputProcessorProfilesLoader.cs
- FixedSOMPageElement.cs
- ThicknessKeyFrameCollection.cs
- SizeFConverter.cs
- RuleCache.cs
- Stacktrace.cs