public override Type GetChildDataContextType(Type dataContext, Type parentDataContext, RedwoodControl control) { var type = control.GetType(); var property = type.GetProperty(PropertyName); if (property == null) throw new Exception($"property { PropertyName } does not exists on { type.FullName }."); return property.GetValue(control).GetType(); }
public static Type GetDataContextType(Type parentDataContext, RedwoodControl control) { var dataContext = (control as RedwoodBindableControl)?.DataContext?.GetType() ?? parentDataContext; var attributes = control.GetType().GetCustomAttributes<DataContextChangeAttribute>(); foreach (var attribute in attributes) { dataContext = attribute.GetChildDataContextType(dataContext, parentDataContext, control); } return dataContext; }
/// <summary> /// Validates the control command. /// </summary> public void ValidateControlCommand(string[] path, string command, RedwoodControl viewRootControl, RedwoodControl targetControl, ref string validationTargetPath) { // find the binding RedwoodProperty targetProperty; var binding = FindControlCommandBinding(path, command, viewRootControl, (RedwoodBindableControl)targetControl, ref validationTargetPath, out targetProperty); if (binding == null) { ThrowEventValidationException(); return; } }
public override Type GetChildDataContextType(Type dataContext, Type parentDataContext, RedwoodControl control) { if(dataContext.IsArray) { return dataContext.GetElementType(); } var ienumerable = dataContext.GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)); if (ienumerable != null) { return ienumerable.GetGenericArguments()[0]; } else throw new NotSupportedException(); }
/// <summary> /// Validates the command. /// </summary> public void ValidateCommand(string[] path, string command, RedwoodControl viewRootControl, ref string validationTargetPath) { // find the binding RedwoodProperty targetProperty; RedwoodBindableControl targetControl; var binding = FindCommandBinding(path, command, viewRootControl, ref validationTargetPath, out targetControl, out targetProperty); if (binding == null) { ThrowEventValidationException(); return; } // validate the command against the control if (targetControl is IEventValidationHandler) { if (!((IEventValidationHandler)targetControl).ValidateCommand(targetProperty)) { ThrowEventValidationException(); return; } } }
/// <summary> /// Finds the binding of the specified type on the specified viewmodel path. /// </summary> private CommandBindingExpression FindCommandBinding(string[] path, string command, RedwoodControl viewRootControl, ref string validationTargetPath, out RedwoodBindableControl targetControl, out RedwoodProperty targetProperty) { // walk the control tree and find the path CommandBindingExpression result = null; RedwoodBindableControl resultControl = null; RedwoodProperty resultProperty = null; string resultValidationTargetPath = validationTargetPath; var walker = new NonEvaluatingControlTreeWalker(viewRootControl); walker.ProcessControlTree((ViewModel, control) => { // compare path if (result == null && control is RedwoodBindableControl && ViewModelPathComparer.AreEqual(path, walker.CurrentPathArray)) { // find bindings of current control var bindableControl = (RedwoodBindableControl)control; var binding = bindableControl.GetAllBindings().Where(p => p.Value is CommandBindingExpression) .FirstOrDefault(b => b.Value.Expression == command); if (binding.Key != null) { // we have found the binding, now get the validation path var currentValidationTargetPath = KnockoutHelper.GetValidationTargetExpression(bindableControl, true); if (currentValidationTargetPath == resultValidationTargetPath) { // the validation path is equal, we have found the binding result = (CommandBindingExpression)binding.Value; resultControl = bindableControl; resultProperty = binding.Key; resultValidationTargetPath = KnockoutHelper.GetValidationTargetExpression(bindableControl, false); } } } }); validationTargetPath = resultValidationTargetPath; targetControl = resultControl; targetProperty = resultProperty; return result; }
public abstract void CreateControls(RedwoodRequestContext context, RedwoodControl container);
/// <summary> /// Evaluates the command arguments. /// </summary> private static object[] EvaluateCommandArguments(object viewModel, RedwoodControl viewRootControl, List<object> hierarchy, InvocationExpressionSyntax node) { var arguments = node.ArgumentList.Arguments.Select(a => { var evaluator = new ExpressionEvaluationVisitor(viewModel, viewRootControl, hierarchy); return evaluator.Visit(a); }).ToArray(); return arguments; }
/// <summary> /// Finds the method on control. /// </summary> private static MethodInfo FindMethodOnControl(RedwoodControl targetControl, InvocationExpressionSyntax node) { MethodInfo method; var methods = targetControl.GetType().GetMethods().Where(m => m.Name == node.Expression.ToString()).ToList(); if (methods.Count == 0) { throw new Exception(string.Format("The control '{0}' does not have a function '{1}'!", targetControl.GetType().FullName, node.Expression)); } else if (methods.Count > 1) { throw new Exception(string.Format("The control '{0}' has more than one function called '{1}'! Overloading in {{controlCommand: ...}} binding is not yet supported!", targetControl.GetType().FullName, node.Expression)); } method = methods[0]; return method; }
public void BuildContent(RedwoodRequestContext context, RedwoodControl container) { var controlBuilderFactory = context.Configuration.ServiceLocator.GetService<IControlBuilderFactory>(); var control = BuildContentBody(controlBuilderFactory); container.Children.Add(control); }
public abstract Type GetChildDataContextType(Type dataContext, Type parentDataContext, RedwoodControl control);
public void OnAddedToParent(RedwoodControl parent) { SetParent(parent); }
public override void CreateControls(RedwoodRequestContext context, RedwoodControl container) { ContentTemplate.BuildContent(context, container); }
/// <summary> /// Invokes the specified method on all controls in the page control tree. /// </summary> private void InvokePageLifeCycleEventRecursive(RedwoodControl control, Action<RedwoodControl> action) { foreach (var child in control.GetThisAndAllDescendants()) { action(child); } }
/// <summary> /// Resolves the command called on the ViewModel. /// </summary> public ActionInfo GetFunction(RedwoodControl viewRootControl, RedwoodRequestContext context, string[] path, string command) { return GetFunction(null, viewRootControl, context, path, command); }
/// <summary> /// Translates the binding expression to the knockout command name. /// </summary> public static string TranslateToKnockoutCommand(RedwoodControl target, RedwoodProperty property, CommandMarkupExpression binding) { return DataContextPathBuilder.Default.BuildPath(target); }
/// <summary> /// Resolves the command called on the RedwoodControl. /// </summary> public ActionInfo GetFunction(RedwoodControl targetControl, RedwoodControl viewRootControl, RedwoodRequestContext context, string[] path, string command) { // event validation var validationTargetPath = context.ModelState.ValidationTargetPath; if (targetControl == null) { eventValidator.ValidateCommand(path, command, viewRootControl, ref validationTargetPath); } else { eventValidator.ValidateControlCommand(path, command, viewRootControl, targetControl, ref validationTargetPath); } // resolve the path in the view model var viewModel = context.ViewModel; List<object> hierarchy = ResolveViewModelPath(viewModel, viewRootControl, path); // resolve validation target if (!string.IsNullOrEmpty(validationTargetPath)) { var hierarchyCopy = new List<object>(hierarchy); context.ModelState.ValidationTarget = EvaluateOnViewModel(viewModel, viewRootControl, hierarchyCopy, validationTargetPath); } // find the function var tree = CSharpSyntaxTree.ParseText(command, new CSharpParseOptions(LanguageVersion.CSharp5, DocumentationMode.Parse, SourceCodeKind.Interactive)); var expr = tree.EnsureSingleExpression(); var node = expr.ChildNodes().First() as InvocationExpressionSyntax; if (node == null) { throw new ParserException("The expression in {command: ...} binding must be a method call!"); } MethodInfo method; object target; if (targetControl != null) { // the function is invoked on the control object method = FindMethodOnControl(targetControl, node); target = targetControl; } else { // the function is invoked on the viewmodel method = FindMethodOnViewModel(viewModel, viewRootControl, hierarchy, node, out target); } // validate that we can safely call the method ValidateMethod(method); // parse arguments var arguments = EvaluateCommandArguments(viewModel, viewRootControl, hierarchy, node); // return the delegate for further invoke return new ActionInfo() { IsControlCommand = target != null, Target = target, MethodInfo = method, Arguments = method.GetParameters().Select((param, index) => new ActionParameterInfo() { ParameterInfo = param, Value = arguments[index] }).ToArray() }; }
/// <summary> /// Resolves the path in the view model and returns the target object. /// </summary> private List<object> ResolveViewModelPath(object viewModel, RedwoodControl viewRootControl, string[] path) { var visitor = new ExpressionEvaluationVisitor(viewModel, viewRootControl); var outputHierarchy = new List<object>(); outputHierarchy.Add(viewModel); foreach (var expression in path) { // evaluate path fragment var pathTree = CSharpSyntaxTree.ParseText(expression, new CSharpParseOptions(LanguageVersion.CSharp5, DocumentationMode.Parse, SourceCodeKind.Interactive)); var pathExpr = pathTree.EnsureSingleExpression(); var result = visitor.Visit(pathExpr); visitor.BackupCurrentPosition(result, expression); outputHierarchy.Add(result); } return outputHierarchy; }
/// <summary> /// Evaluates the expression the on view model with specified hierarchy. /// </summary> private object EvaluateOnViewModel(object viewModel, RedwoodControl viewRootControl, List<object> hierarchy, string expression) { var pathTree = CSharpSyntaxTree.ParseText(expression, new CSharpParseOptions(LanguageVersion.CSharp5, DocumentationMode.Parse, SourceCodeKind.Interactive)); var pathExpr = pathTree.EnsureSingleExpression(); var visitor = new ExpressionEvaluationVisitor(viewModel, viewRootControl, hierarchy); return visitor.Visit(pathExpr); }
/// <summary> /// Finds the method on view model. /// </summary> private static MethodInfo FindMethodOnViewModel(object viewModel, RedwoodControl viewRootControl, List<object> hierarchy, InvocationExpressionSyntax node, out object target) { MethodInfo method; var methodEvaluator = new ExpressionEvaluationVisitor(viewModel, viewRootControl, hierarchy) { AllowMethods = true }; method = methodEvaluator.Visit(node.Expression) as MethodInfo; if (method == null) { throw new Exception("The command path was not found!"); } target = methodEvaluator.MethodInvocationTarget; return method; }