public static BindingProxy GetValueProxy(IResourceContext context, string propertyKey) { var key = new BindingProxyKey(propertyKey); if (context.TryFindResource(key) is BindingProxy proxy) { return(proxy); } proxy = new BindingProxy(); context.AddResource(key, proxy); return(proxy); }
private static IValidatorProvider CreateValidator(Type modelType, string propertyKey, ValueAttribute attribute, Func <string, object> deserializer) { Func <IResourceContext, IProxy> argumentProvider; var argument = attribute.Argument; if (argument is string expression) { var boundExpression = BoundExpression.Parse(expression); if (boundExpression.IsPlainString) { var literal = new PlainObject(deserializer != null ? deserializer(boundExpression.StringFormat) : boundExpression.StringFormat); argumentProvider = context => literal; } else { var getString = boundExpression.StringFormat != null; var action = attribute.ArgumentUpdatedAction; var notify = action == ValidationAction.ClearErrors || action == ValidationAction.ValidateField; argumentProvider = context => { var value = getString ? (IProxy)boundExpression.GetStringValue(context) : boundExpression.GetValue(context); if (notify) { value.ValueChanged = () => { object model; try { model = context.GetModelInstance(); } catch { // Something went wrong so it's best to // disable the feature entirely. value.ValueChanged = null; return; } if (model is ExpandoObject && modelType == null) { // Do nothing. } else if (model == null || model.GetType() != modelType) { // Self dispose when form indicates model change. value.ValueChanged = null; return; } if (action == ValidationAction.ValidateField) { ModelState.Validate(model, propertyKey); } else { ModelState.ClearValidationErrors(model, propertyKey); } }; } return(value); }; } } else { var literal = new PlainObject(argument); argumentProvider = context => literal; } BindingProxy ValueProvider(IResourceContext context) { var key = new BindingProxyKey(propertyKey); if (context.TryFindResource(key) is BindingProxy proxy) { return(proxy); } proxy = new BindingProxy(); context.AddResource(key, proxy); return(proxy); } Func <IResourceContext, IBoolProxy> isEnforcedProvider; switch (attribute.When) { case null: isEnforcedProvider = context => new PlainBool(true); break; case string expr: var boundExpression = BoundExpression.Parse(expr); if (!boundExpression.IsSingleResource) { throw new ArgumentException( "The provided value must be a bound resource or a literal bool value.", nameof(attribute)); } isEnforcedProvider = context => boundExpression.GetBoolValue(context); break; case bool b: isEnforcedProvider = context => new PlainBool(b); break; default: throw new ArgumentException( "The provided value must be a bound resource or a literal bool value.", nameof(attribute)); } Func <IResourceContext, IErrorStringProvider> errorProvider; var message = attribute.Message; if (message == null) { switch (attribute.Condition) { case Must.BeGreaterThan: message = "Value must be greater than {Argument}."; break; case Must.BeGreaterThanOrEqualTo: message = "Value must be greater than or equal to {Argument}."; break; case Must.BeLessThan: message = "Value must be less than {Argument}."; break; case Must.BeLessThanOrEqualTo: message = "Value must be less than or equal to {Argument}."; break; case Must.BeEmpty: message = "@Field must be empty."; break; case Must.NotBeEmpty: message = "@Field cannot be empty."; break; default: message = "@Invalid value."; break; } } { var func = new Func <IResourceContext, IProxy>(ValueProvider); var boundExpression = BoundExpression.Parse(message, new Dictionary <string, object> { ["Value"] = func, ["Argument"] = argumentProvider }); if (boundExpression.IsPlainString) { var errorMessage = boundExpression.StringFormat; errorProvider = context => new PlainErrorStringProvider(errorMessage); } else { if (boundExpression.Resources.Any( res => res is DeferredProxyResource resource && resource.ProxyProvider == func)) { errorProvider = context => new ValueErrorStringProvider(boundExpression.GetStringValue(context), ValueProvider(context)); }