internal static void UpdateInvalidArgumentsIfNecessary(object sender, ValidationService.ErrorsMarkedEventArgs args) { if (args.Reason != ValidationReason.ModelChange) { return; } if (args.Errors.Count == 0) { return; } using (EditingScope editingScope = args.ModelTreeManager.CreateEditingScope(string.Empty)) { // Prevent the validation -> fix arguments -> validation loop. editingScope.SuppressUndo = true; // Suppress validation. We will do it ourselves (see below) editingScope.SuppressValidationOnComplete = true; // Re-compile erroreous expressions to see if update is necessary ValidationService validationService = args.Context.Services.GetRequiredService<ValidationService>(); ArgumentAccessorWrapperCache argumentAccessorWrapperCache = new ArgumentAccessorWrapperCache(); List<ExpressionReplacement> expressionReplacements = ComputeExpressionReplacements(args.Errors.Select(error => error.Source).OfType<ActivityWithResult>(), args.Context, argumentAccessorWrapperCache); bool argumentReplacementOccurred = false; if (expressionReplacements.Count > 0) { try { foreach (ExpressionReplacement expressionReplacement in expressionReplacements) { if (expressionReplacement.TryReplaceArgument(args.ModelTreeManager, validationService)) { argumentReplacementOccurred = true; } } if (argumentReplacementOccurred) { args.Handled = true; editingScope.Complete(); } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } // We handle exception here instead of letting WF Designer handle it, so that the validation below will run even // if any of the ArgumentAccessor.Setter methods throw exceptions. ErrorReporting.ShowErrorMessage(e); } // Since any pending validation will be canceled if argument replacement occured (=has model change), we need to re-validate the workflow. // We suppressed validation upon EditingScope completion and do it ourselves, because // the argument replacement could have been done directly to the underlying activity instance, rather than thru ModelItem. if (argumentReplacementOccurred) { validationService.ValidateWorkflow(); } } } }
public bool TryReplaceArgument(ModelTreeManager modelTreeManager, ValidationService validationService) { Fx.Assert(modelTreeManager != null, "modelTreeManager cannot be null."); Fx.Assert(validationService != null, "validationService cannot be null."); ModelItem expressionModelItem = modelTreeManager.GetModelItem(this.ExpressionToReplace); if (expressionModelItem != null) { ModelItem argumentModelItem = expressionModelItem.Parent; ModelItem parentObject = argumentModelItem.Parent; if (argumentModelItem.Source != null) { ModelProperty argumentProperty = parentObject.Properties[argumentModelItem.Source.Name]; Type argumentPropertyType = argumentProperty.PropertyType; if (argumentPropertyType == typeof(InArgument) || argumentPropertyType == typeof(OutArgument) || argumentPropertyType == typeof(InOutArgument)) { ModelItem oldArgumentModel = argumentProperty.Value; ModelItem newArgumentModel = argumentProperty.SetValue(this.NewArgument); // Make sure argument.Expression is wrapped in ModelItem as well ModelItem newExpressionModel = newArgumentModel.Properties["Expression"].Value; return true; } } } else { Activity parentActivity = ValidationService.GetParent(this.ExpressionToReplace); if (this.ArgumentAccessor.Setter != null) { try { validationService.DeactivateValidation(); this.ArgumentAccessor.Setter(parentActivity, this.NewArgument); return true; } finally { validationService.ActivateValidation(); } } } return false; }