internal object EvalPropertyValue(DotvvmProperty property, object value) { if (property.IsBindingProperty) { return(value); } if (value is IBinding) { DotvvmBindableObject control = this; // DataContext is always bound to it's parent, setting it right here is a bit faster if (property == DataContextProperty) { control = control.Parent; } // handle binding if (value is IStaticValueBinding binding) { value = binding.Evaluate(control); } else if (value is ICommandBinding command) { value = command.GetCommandDelegate(control); } else { throw new NotSupportedException($"Cannot evaluate binding {value} of type {value.GetType().Name}."); } } return(value); }
/// <summary> /// Writes text iff the property contains hard-coded value OR /// writes knockout text binding iff the property contains binding /// </summary> /// <param name="writer">HTML output writer</param> /// <param name="obj">Dotvvm control which contains the <see cref="DotvvmProperty"/> with value to be written</param> /// <param name="property">Value of this property will be written</param> /// <param name="wrapperTag">Name of wrapper tag, null => knockout binding comment</param> public static void WriteTextOrBinding(this IHtmlWriter writer, DotvvmBindableObject obj, DotvvmProperty property, string wrapperTag = null) { var valueBinding = obj.GetValueBinding(property); if (valueBinding != null) { if (wrapperTag == null) { writer.WriteKnockoutDataBindComment("text", valueBinding.GetKnockoutBindingExpression(obj)); writer.WriteKnockoutDataBindEndComment(); } else { writer.AddKnockoutDataBind("text", valueBinding.GetKnockoutBindingExpression(obj)); writer.RenderBeginTag(wrapperTag); writer.RenderEndTag(); } } else { if (wrapperTag != null) { writer.RenderBeginTag(wrapperTag); } writer.WriteText(obj.GetValue(property).ToString()); if (wrapperTag != null) { writer.RenderEndTag(); } } }
static string GenerateConcurrencyModeHandler(string propertyName, DotvvmBindableObject obj) { var mode = (obj.GetValue(PostBack.ConcurrencyProperty) as PostbackConcurrencyMode?) ?? PostbackConcurrencyMode.Default; // determine concurrency queue string queueName = null; var queueSettings = obj.GetValueRaw(PostBack.ConcurrencyQueueSettingsProperty) as ConcurrencyQueueSettingsCollection; if (queueSettings != null) { queueName = queueSettings.FirstOrDefault(q => string.Equals(q.EventName, propertyName, StringComparison.OrdinalIgnoreCase))?.ConcurrencyQueue; } if (queueName == null) { queueName = obj.GetValue(PostBack.ConcurrencyQueueProperty) as string ?? "default"; } // return the handler script if (mode == PostbackConcurrencyMode.Default && "default".Equals(queueName)) { return(null); } var handlerName = $"concurrency-{mode.ToString().ToLower()}"; if ("default".Equals(queueName)) { return(JsonConvert.ToString(handlerName)); } else { return($"[{JsonConvert.ToString(handlerName)},{GenerateHandlerOptions(obj, new Dictionary<string, object> { ["q"] = queueName })}]"); } }
/// <summary> /// Gets the value of a specified property. /// </summary> public virtual object GetValue(DotvvmProperty property, bool inherit = true) { var value = GetValueRaw(property, inherit); if (property.IsBindingProperty) { return(value); } while (value is IBinding) { DotvvmBindableObject control = this; if (inherit && !properties.ContainsKey(property)) { int n; control = GetClosestWithPropertyValue(out n, d => d.properties != null && d.properties.ContainsKey(property)); } if (value is IStaticValueBinding) { // handle binding var binding = (IStaticValueBinding)value; value = binding.Evaluate(control, property); } else if (value is CommandBindingExpression) { var binding = (CommandBindingExpression)value; value = binding.GetCommandDelegate(control, property); } } return(value); }
/// <summary> /// Gets the value of a specified property. /// </summary> public virtual object GetValue(DotvvmProperty property, bool inherit = true) { var value = GetValueRaw(property, inherit); if (property.IsBindingProperty) { return(value); } while (value is IBinding) { DotvvmBindableObject control = this; // DataContext is always bound to it's parent, setting it right here is a bit faster if (property == DataContextProperty) { control = control.Parent; } // handle binding if (value is IStaticValueBinding binding) { value = binding.Evaluate(control); } else if (value is ICommandBinding command) { value = command.GetCommandDelegate(control); } } return(value); }
public static string DebugString(this DotvvmBindableObject control, DotvvmConfiguration?config = null, bool multiline = true) { if (control == null) { return("null"); } config = config ?? (control.TryGeyValue(Internal.RequestContextProperty) as IDotvvmRequestContext)?.Configuration; var type = control.GetType(); var properties = (from kvp in control.Properties let p = kvp.Key let rawValue = kvp.Value where p.DeclaringType != typeof(Internal) let isAttached = !p.DeclaringType.IsAssignableFrom(type) orderby !isAttached, p.Name let name = isAttached ? p.DeclaringType.Name + "." + p.Name : p.Name let value = rawValue == null ? "<null>" : rawValue is ITemplate ? "<a template>" : rawValue is DotvvmBindableObject ? $"<control {rawValue.GetType()}>" : rawValue is IEnumerable <DotvvmBindableObject> controlCollection ? $"<{controlCollection.Count()} controls>" : rawValue is IEnumerable <object> collection ? string.Join(", ", collection) : rawValue.ToString() let croppedValue = value.Length > 41 ? value.Substring(0, 40) + "…" : value select new { p, name, croppedValue, value, isAttached } ).ToArray(); var location = (file : control.TryGeyValue(Internal.MarkupFileNameProperty) as string, line : control.TryGeyValue(Internal.MarkupLineNumberProperty) as int? ?? -1); var reg = config?.Markup.Controls.FirstOrDefault(c => c.Namespace == type.Namespace && Type.GetType(c.Namespace + "." + type.Name + ", " + c.Assembly) == type) ?? config?.Markup.Controls.FirstOrDefault(c => c.Namespace == type.Namespace) ?? config?.Markup.Controls.FirstOrDefault(c => c.Assembly == type.Assembly.GetName().Name); var ns = reg?.TagPrefix ?? "?"; var tagName = type == typeof(HtmlGenericControl) ? ((HtmlGenericControl)control).TagName : ns + ":" + type.Name; var dothtmlString = $"<{tagName} "; var prefixLength = dothtmlString.Length; foreach (var p in properties) { if (multiline && p != properties[0]) { dothtmlString += "\n" + new string(' ', prefixLength); } dothtmlString += $"{p.name}={p.croppedValue}"; } dothtmlString += "/>"; var from = (location.file) + (location.line >= 0 ? ":" + location.line : ""); if (!String.IsNullOrWhiteSpace(from)) { from = (multiline ? "\n" : " ") + "from " + from.Trim(); } return(dothtmlString + from); }
/// <summary> /// Returns Javascript expression that represents the property value (even if the property contains hardcoded value) /// </summary> public static string GetKnockoutBindingExpression(this DotvvmBindableObject obj, DotvvmProperty property) { var binding = obj.GetValueBinding(property); if (binding != null) { return(binding.GetKnockoutBindingExpression()); } return(JsonConvert.SerializeObject(obj.GetValue(property))); }
/// <summary> /// Gets the validation target expression. /// </summary> public static string GetValidationTargetExpression(DotvvmBindableObject control) { if (!(bool)control.GetValue(Validation.EnabledProperty)) { return(null); } return(control.GetValueBinding(Validation.TargetProperty)?.GetKnockoutBindingExpression(control) ?? RootValidationTargetExpression); }
/// <summary> /// Returns Javascript expression that represents the property value (even if the property contains hardcoded value) /// </summary> public static string GetKnockoutBindingExpression(this DotvvmBindableObject obj, DotvvmProperty property) { var binding = obj.GetValueBinding(property); if (binding != null) { return(binding.GetKnockoutBindingExpression(obj)); } return(JsonConvert.SerializeObject(obj.GetValue(property), DefaultViewModelSerializer.CreateDefaultSettings())); }
internal static object?TryGeyValue(this DotvvmBindableObject control, DotvvmProperty property) { try { return(control.GetValue(property)); } catch { return(property.DefaultValue); } }
public DotvvmControlException(DotvvmBindableObject control, string message, Exception innerException = null) : base(message, innerException) { ControlType = control.GetType(); LineNumber = (int?)Internal.MarkupLineNumberProperty.GetValue(control); if (control.Parent != null) { control = control.Parent; } FileName = (string)Internal.MarkupFileNameProperty.GetValue(control); }
public static IEnumerable <string> GetContextPath(DotvvmBindableObject control) { while (control != null) { var pathFragment = control.GetDataContextPathFragment(); if (pathFragment != null) { yield return(pathFragment); } control = control.Parent; } }
private static string TranslateRouteParameter(DotvvmBindableObject control, KeyValuePair <string, object> param, bool caseSensitive = false) { string expression = ""; if (param.Value is IBinding) { EnsureValidBindingType(param.Value as IBinding); expression = (param.Value as IValueBinding)?.GetKnockoutBindingExpression(control) ?? JsonConvert.SerializeObject((param.Value as IStaticValueBinding)?.Evaluate(control), DefaultViewModelSerializer.CreateDefaultSettings()); } else { expression = JsonConvert.SerializeObject(param.Value, DefaultViewModelSerializer.CreateDefaultSettings()); } return(JsonConvert.SerializeObject(caseSensitive ? param.Key : param.Key.ToLower()) + ": " + expression); }
static string GenerateConcurrencyModeHandler(DotvvmBindableObject obj) { var mode = (obj.GetValue(PostBack.ConcurrencyProperty) as PostbackConcurrencyMode?) ?? PostbackConcurrencyMode.Default; var queueName = obj.GetValueRaw(PostBack.ConcurrencyQueueProperty) ?? "default"; if (mode == PostbackConcurrencyMode.Default && "default".Equals(queueName)) { return(null); } var handlerName = $"concurrency-{mode.ToString().ToLower()}"; if ("default".Equals(queueName)) { return(JsonConvert.ToString(handlerName)); } return($"[{JsonConvert.ToString(handlerName)},{GenerateHandlerOptions(obj, new Dictionary<string, object> { ["q"] = queueName })}]"); }
/// <summary> /// Gets the validation target expression. /// </summary> public static string GetValidationTargetExpression(DotvvmBindableObject control) { if (!(bool)control.GetValue(Validation.EnabledProperty)) { return(null); } // find the closest control var validationTargetControl = control.GetClosestControlValidationTarget(out var _); if (validationTargetControl == null) { return("$root"); } // reparent the expression to work in current DataContext return(validationTargetControl.GetValueBinding(Validation.TargetProperty).GetKnockoutBindingExpression(control)); }
internal IEnumerable <IValueBinding> GetDataContextHierarchy() { var bindings = new List <IValueBinding>(); DotvvmBindableObject current = this; do { var binding = current.GetValueBinding(DataContextProperty, false); if (binding != null) { bindings.Add(binding); } current = current.Parent; }while (current != null); bindings.Reverse(); return(bindings); }
private static string GenerateHandlerOptions(DotvvmBindableObject handler, Dictionary <string, object> options) { JsExpression optionsExpr = new JsObjectExpression( options.Where(o => o.Value != null).Select(o => new JsObjectProperty(o.Key, TransformOptionValueToExpression(handler, o.Value))) ); if (options.Any(o => o.Value is IValueBinding)) { optionsExpr = new JsFunctionExpression( new[] { new JsIdentifier("c"), new JsIdentifier("d") }, new JsBlockStatement(new JsReturnStatement(optionsExpr)) ); } optionsExpr.FixParenthesis(); var script = new JsFormattingVisitor().ApplyAction(optionsExpr.AcceptVisitor).GetParameterlessResult(); return(script); }
/// <summary> /// Generates a list of postback update handlers. /// </summary> private static string GetPostBackHandlersScript(DotvvmBindableObject control, string eventName) { var handlers = (List <PostBackHandler>)control.GetValue(PostBack.HandlersProperty); if (handlers == null) { return("null"); } var effectiveHandlers = handlers.Where(h => string.IsNullOrEmpty(h.EventName) || h.EventName == eventName); var sb = new StringBuilder(); sb.Append("["); foreach (var handler in effectiveHandlers) { if (sb.Length > 1) { sb.Append(","); } sb.Append("{name:'"); sb.Append(handler.ClientHandlerName); sb.Append("',options:function(){return {"); var isFirst = true; var options = handler.GetHandlerOptionClientExpressions(); options.Add("enabled", handler.TranslateValueOrBinding(PostBackHandler.EnabledProperty)); foreach (var option in options) { if (!isFirst) { sb.Append(','); } isFirst = false; sb.Append(option.Key); sb.Append(":"); sb.Append(option.Value); } sb.Append("};}}"); } sb.Append("]"); return(sb.ToString()); }
/// <summary> /// Copies the value of a property from this <see cref="DotvvmBindableObject"/> (source) to a property of another <see cref="DotvvmBindableObject"/> (target). /// </summary> /// <exception cref="DotvvmControlException">Gets thrown if copying fails and <paramref name="throwOnFailure"/> is set to true</exception> /// <param name="sourceProperty">The <see cref="DotvvmProperty"/> whose value will be copied</param> /// <param name="target">The <see cref="DotvvmBindableObject"/> that holds the value of the <paramref name="targetProperty"/></param> /// <param name="targetProperty">The <see cref="DotvvmProperty"/> to which <paramref name="sourceProperty"/> will be copied</param> /// <param name="throwOnFailure">Determines whether to throw an exception if copying fails</param> protected void CopyProperty(DotvvmProperty sourceProperty, DotvvmBindableObject target, DotvvmProperty targetProperty, bool throwOnFailure = false) { if (throwOnFailure && !targetProperty.MarkupOptions.AllowBinding && !targetProperty.MarkupOptions.AllowHardCodedValue) { throw new DotvvmControlException(this, $"TargetProperty: {targetProperty.FullName} doesn't allow bindings nor hard coded values"); } if (targetProperty.MarkupOptions.AllowBinding && HasBinding(sourceProperty)) { target.SetBinding(targetProperty, GetBinding(sourceProperty)); } else if (targetProperty.MarkupOptions.AllowHardCodedValue && IsPropertySet(sourceProperty)) { target.SetValue(targetProperty, GetValue(sourceProperty)); } else if (throwOnFailure) { throw new DotvvmControlException(this, $"Value of {sourceProperty.FullName} couldn't be copied to targetProperty: {targetProperty.FullName}, because {targetProperty.FullName} is not set."); } }
public static void AddKnockoutDataBind(this IHtmlWriter writer, string name, DotvvmBindableObject control, DotvvmProperty property, Action?nullBindingAction = null, string?valueUpdate = null, bool renderEvenInServerRenderingMode = false, bool setValueBack = false) { var expression = control.GetValueBinding(property); if (expression != null && (!control.RenderOnServer || renderEvenInServerRenderingMode)) { writer.AddKnockoutDataBind(name, expression.GetKnockoutBindingExpression(control)); if (valueUpdate != null) { writer.AddKnockoutDataBind("valueUpdate", $"'{valueUpdate}'"); } } else { nullBindingAction?.Invoke(); if (setValueBack && expression != null) { control.SetValue(property, expression.Evaluate(control)); } } }
private static string GenerateHandlerOptions(DotvvmBindableObject handler, Dictionary <string, object> options) { JsExpression optionsExpr = new JsObjectExpression( options.Where(o => o.Value != null).Select(o => new JsObjectProperty(o.Key, o.Value is IValueBinding b ? (JsExpression) new JsIdentifierExpression( JavascriptTranslator.FormatKnockoutScript(b.GetParametrizedKnockoutExpression(handler, unwrapped: true), new ParametrizedCode("c"), new ParametrizedCode("d"))) : new JsLiteral(o.Value))) ); if (options.Any(o => o.Value is IValueBinding)) { optionsExpr = new JsFunctionExpression( new[] { new JsIdentifier("c"), new JsIdentifier("d") }, new JsBlockStatement(new JsReturnStatement(optionsExpr)) ); } optionsExpr.FixParenthesis(); var script = new JsFormattingVisitor().ApplyAction(optionsExpr.AcceptVisitor).GetParameterlessResult(); return(script); }
private static JsExpression TransformOptionValueToExpression(DotvvmBindableObject handler, object optionValue) { switch (optionValue) { case IValueBinding binding: return(new JsIdentifierExpression( JavascriptTranslator.FormatKnockoutScript(binding.GetParametrizedKnockoutExpression(handler, unwrapped: true), new ParametrizedCode("c"), new ParametrizedCode("d")))); case IStaticValueBinding staticValueBinding: return(new JsLiteral(staticValueBinding.Evaluate(handler))); case JsExpression expression: return(expression.Clone()); case IBinding _: throw new ArgumentException("Option value can contains only IValueBinding or IStaticValueBinding. Other bindings are not supported."); default: return(new JsLiteral(optionValue)); } }
/// <summary> /// Gets the validation target expression. /// </summary> public static string GetValidationTargetExpression(DotvvmBindableObject control) { if (!(bool)control.GetValue(Validation.EnabledProperty)) { return(null); } // find the closest control int dataSourceChanges; var validationTargetControl = control.GetClosestWithPropertyValue(out dataSourceChanges, c => c.IsPropertySet(Validation.TargetProperty, false)); if (validationTargetControl == null) { return("$root"); } // reparent the expression to work in current DataContext var validationBindingExpression = validationTargetControl.GetValueBinding(Validation.TargetProperty); string validationExpression = validationBindingExpression.GetKnockoutBindingExpression(); validationExpression = string.Join("", Enumerable.Range(0, dataSourceChanges).Select(i => "$parentContext.")) + validationExpression; return(validationExpression); }
public static void WriteKnockoutDataBindComment(this IHtmlWriter writer, string name, DotvvmBindableObject control, DotvvmProperty property) { var binding = control.GetValueBinding(property); if (binding is null) { throw new DotvvmControlException(control, $"A value binding expression was expected in property {property}."); } writer.WriteKnockoutDataBindComment(name, binding.GetKnockoutBindingExpression(control)); }
public static void AddKnockoutDataBind(this IHtmlWriter writer, string name, IEnumerable <KeyValuePair <string, IValueBinding> > expressions, DotvvmBindableObject control, DotvvmProperty?property = null) { writer.AddKnockoutDataBind(name, $"{{{String.Join(",", expressions.Select(e => "'" + e.Key + "': " + e.Value.GetKnockoutBindingExpression(control)))}}}"); }
public static void AddKnockoutDataBind(this IHtmlWriter writer, string name, IValueBinding valueBinding, DotvvmBindableObject control) { writer.AddKnockoutDataBind(name, valueBinding.GetKnockoutBindingExpression(control)); }
public static string GenerateClientPostBackExpression(string propertyName, ICommandBinding expression, DotvvmBindableObject control, PostbackScriptOptions options) { var target = (DotvvmControl?)control.GetClosestControlBindingTarget(); var uniqueControlId = target?.GetDotvvmUniqueId(); string getContextPath(DotvvmBindableObject?current) { var result = new List <string>(); while (current != null) { var pathFragment = current.GetDataContextPathFragment(); if (pathFragment != null) { result.Add(JsonConvert.ToString(pathFragment)); } current = current.Parent; } result.Reverse(); return("[" + string.Join(",", result) + "]"); } string getHandlerScript() { if (!options.AllowPostbackHandlers) { return("[]"); } // turn validation off for static commands var validationPath = expression is StaticCommandBindingExpression ? null : GetValidationTargetExpression(control); return(GetPostBackHandlersScript(control, propertyName, // validation handler validationPath == null ? null : validationPath == RootValidationTargetExpression ? "\"validate-root\"" : validationPath == "$data" ? "\"validate-this\"" : $"[\"validate\", {{path:{JsonConvert.ToString(validationPath)}}}]", // use window.setTimeout options.UseWindowSetTimeout ? "\"timeout\"" : null, options.IsOnChange ? "\"suppressOnUpdating\"" : null, GenerateConcurrencyModeHandler(propertyName, control) )); } string?generatedPostbackHandlers = null; var adjustedExpression = expression.GetParametrizedCommandJavascript(control); // when the expression changes the dataContext, we need to override the default knockout context fo the command binding. var knockoutContext = options.KoContext ?? ( // adjustedExpression != expression.CommandJavascript ? new CodeParameterAssignment(new ParametrizedCode.Builder { "ko.contextFor(", options.ElementAccessor.Code !, ")" }.Build(OperatorPrecedence.Max))
public static string GenerateClientPostBackScript(string propertyName, ICommandBinding expression, DotvvmBindableObject control, PostbackScriptOptions options) { var expr = GenerateClientPostBackExpression(propertyName, expression, control, options); if (options.ReturnValue == false) { return(expr + ";event.stopPropagation();return false;"); } else { return(expr); } }
public static string GenerateClientPostBackScript(string propertyName, ICommandBinding expression, DotvvmBindableObject control, bool useWindowSetTimeout = false, bool?returnValue = false, bool isOnChange = false, string elementAccessor = "this") { return(GenerateClientPostBackScript(propertyName, expression, control, new PostbackScriptOptions(useWindowSetTimeout, returnValue, isOnChange, elementAccessor))); }
/// Sets an expected data context type public static void SetDataContextType(this DotvvmBindableObject obj, DataContextStack stack) => obj.SetValue(Internal.DataContextTypeProperty, stack);