public SingleControlTests() { context = new TestDotvvmRequestContext() { Configuration = configuration }; writer = new HtmlWriter(new StringWriter(output), context); Internal.MarkupFileNameProperty.SetValue(rootView, "some_fake_path"); Internal.RequestContextProperty.SetValue(rootView, "some_fake_path"); var bcs = context.Services.GetService <BindingCompilationService>(); var dataContext = DataContextStack.Create(typeof(TestViewModel)); Internal.DataContextTypeProperty.SetValue(rootView, dataContext); DotvvmBindableObject.DataContextProperty.SetValue(rootView, viewModel); testValueBinding = ValueBindingExpression.CreateBinding(bcs, h => ((TestViewModel)h[0]).Property, dataContext); boolValueBinding = ValueBindingExpression.CreateBinding(bcs, h => ((TestViewModel)h[0]).Property == 0, dataContext); basicHtmlElement = new HtmlGenericControl("div"); richHtmlElement = new HtmlGenericControl("div"); HtmlGenericControl.IncludeInPageProperty.SetValue(richHtmlElement, boolValueBinding); HtmlGenericControl.CssClassesGroupDescriptor.GetDotvvmProperty("my-class").SetValue(richHtmlElement, boolValueBinding); HtmlGenericControl.CssStylesGroupDescriptor.GetDotvvmProperty("width").SetValue(richHtmlElement, testValueBinding); richHtmlElement.Attributes.Add("data-my-attr", "HELLO"); richHtmlElement.Attributes.Add("title", new(testValueBinding)); Internal.UniqueIDProperty.SetValue(basicHtmlElement, "c1"); Internal.UniqueIDProperty.SetValue(richHtmlElement, "c1"); }
private object JoinValuesOrBindings(IList <object> fragments) { if (fragments.All(f => f is string)) { return(string.Join("_", fragments)); } else { BindingCompilationService service = null; var result = new ParametrizedCode.Builder(); var first = true; foreach (var f in fragments) { if (!first | (first = false)) { result.Add("+'_'+"); } if (f is IValueBinding binding) { service = service ?? binding.GetProperty <BindingCompilationService>(ErrorHandlingMode.ReturnNull); result.Add(binding.GetParametrizedKnockoutExpression(this, unwraped: true), 14); } else { result.Add(JavascriptCompilationHelper.CompileConstant(f)); } } if (service == null) { throw new NotSupportedException(); } return(ValueBindingExpression.CreateBinding <string>(service.WithoutInitialization(), h => null, result.Build(new OperatorPrecedence()))); } }
private GridViewTextColumn GenerateTextColumn(Column column) { var bindingService = Context.Configuration.ServiceProvider.GetRequiredService <BindingCompilationService>(); //gets element with ID attribute set to GridView var gridView = (GridView)Context.View.FindControlByClientId("GridView"); //get DataContext from other column to ensure that the new column will have the same dataContext //we could also use DataContextStack.Create(typeof(GridViewData)); if we cannot copy DataContext var dataContextStack = gridView.Columns[0].GetDataContextType(); // ((GridViewData)objects[0]).AdditionalProp translates into _parent0.AdditionalProp which is equal to _this.AdditionalProp Expression <Func <object[], int> > expression = objects => ((GridViewData)objects[0]).DefaultProp; //this is quite expensive call //but soon there should be available cached version //usage will be bindingService.Cache.CreateCachedBinding("ID", new [] { dataContext }, () => CreateBinding<T>(service, o => (T)o[0], dataContext).CreateBinding(bindingService,expression,dataContextStack)); //see https://github.com/riganti/dotvvm/pull/672 for more info var valueBindingExpression = ValueBindingExpression.CreateBinding(bindingService, expression, dataContextStack); var gridViewTextColumn = new GridViewTextColumn() { HeaderText = column.Name, ValueBinding = valueBindingExpression }; return(gridViewTextColumn); }
public void RepeaterEmptyData_RenderedHtmlTest_ServerRendering() { Repeater createRepeater(RenderMode renderMode) { var repeater = new Repeater() { ItemTemplate = new DelegateTemplate((f, s, c) => c.Children.Add(new HtmlGenericControl("ITEM_TAG"))), EmptyDataTemplate = new DelegateTemplate((f, s, c) => c.Children.Add(new HtmlGenericControl("EMPTY_DATA"))), DataSource = ValueBindingExpression.CreateThisBinding <string[]>(Configuration.ServiceProvider.GetRequiredService <BindingCompilationService>(), null), RenderWrapperTag = false }; repeater.SetValue(RenderSettings.ModeProperty, renderMode); return(repeater); } var viewModel = new string[] { }; var clientHtml = InvokeLifecycleAndRender(createRepeater(RenderMode.Client), CreateContext(viewModel)); Assert.IsTrue(clientHtml.Contains("<ITEM_TAG")); Assert.IsTrue(clientHtml.Contains("<EMPTY_DATA")); Assert.IsTrue(!clientHtml.Contains("<div")); Assert.IsTrue(clientHtml.Contains("<!-- ko ")); var serverHtml = InvokeLifecycleAndRender(createRepeater(RenderMode.Server), CreateContext(viewModel)); Assert.IsTrue(serverHtml.Contains("<EMPTY_DATA")); Assert.IsTrue(!serverHtml.Contains("<div")); }
private void AddDynamicColumn <TType>(string propertyName) { var column = SelectedCategory.AllColumns.Single(c => c.PropertyName == propertyName); // get GridView var gridView = (DotVVM.BusinessPack.Controls.GridView)Context.View.FindControlByClientId("GridView"); // create the binding var dataContextStack = gridView.Columns[0].GetDataContextType(); var param = Expression.Parameter(typeof(object[])); var expression = Expression.Lambda <Func <object[], TType> >(Expression.Property(Expression.Convert(Expression.ArrayIndex(param, Expression.Constant(0)), typeof(DataEntry)), propertyName), param); var binding = ValueBindingExpression.CreateBinding(bindingCompilationService, expression, dataContextStack); // create the column var gridViewTextColumn = new DotVVM.BusinessPack.Controls.GridViewTextColumn(bindingCompilationService) { HeaderText = column.Name, ColumnName = column.PropertyName, SortExpression = column.PropertyName }; gridViewTextColumn.SetBinding(DotVVM.BusinessPack.Controls.GridViewTextColumn.ValueProperty, binding); gridView.Columns.Add(gridViewTextColumn); // add user settings gridView.UserSettings.ColumnsSettings.Add(new GridViewColumnSettings { ColumnName = column.PropertyName, DisplayOrder = gridView.Columns.Count - 1, DisplayText = column.Name, Visible = true }); }
private ValueBindingExpression GetNearIndexesBinding(IDotvvmRequestContext context, int i, DataContextStack dataContext = null) { return (_nearIndexesBindingCache.GetOrCreateValue(context.Configuration) .GetOrAdd(i, _ => ValueBindingExpression.CreateBinding( bindingService.WithoutInitialization(), h => ((IPageableGridViewDataSet)h[0]).PagingOptions.NearPageIndexes[i], dataContext))); }
public void BindingGroup_MultipleBindings_InProperOrder() { var textbox = new OrderedDataBindTextBox(); textbox.SetBinding(TextBox.TextProperty, ValueBindingExpression.CreateThisBinding <string>(Configuration.ServiceProvider.GetRequiredService <BindingCompilationService>(), null)); var html = InvokeLifecycleAndRender(textbox, CreateContext(string.Empty)); StringAssert.Contains(html.Replace(" ", ""), "data-bind=\"first:true,value:$rawData,second:true\""); }
protected virtual HtmlGenericControl CreatePageNumberButton(IHtmlWriter writer, IDotvvmRequestContext context) { HtmlGenericControl li; var currentPageTextContext = DataContextStack.Create(typeof(int), NumberButtonsPlaceHolder.GetDataContextType()); var currentPageTextBinding = ValueBindingExpression.CreateBinding(bindingService.WithoutInitialization(), vm => ((int)vm[0] + 1).ToString(), currentPageTextJs, currentPageTextContext); if (!RenderLinkForCurrentPage) { writer.AddKnockoutDataBind("visible", "$data == $parent.PagingOptions().PageIndex()"); AddItemCssClass(writer, context); AddKnockoutActiveCssDataBind(writer, context, "$data == $parent.PagingOptions().PageIndex()"); li = new HtmlGenericControl("li"); var literal = new Literal(); literal.DataContext = 0; literal.SetDataContextType(currentPageTextContext); literal.SetBinding(Literal.TextProperty, currentPageTextBinding); li.Children.Add(literal); NumberButtonsPlaceHolder.Children.Add(li); li.Render(writer, context); writer.AddKnockoutDataBind("visible", "$data != $parent.PagingOptions().PageIndex()"); } AddItemCssClass(writer, context); AddKnockoutActiveCssDataBind(writer, context, "$data == $parent.PagingOptions().PageIndex()"); li = new HtmlGenericControl("li"); li.SetValue(Internal.PathFragmentProperty, "PagingOptions.NearPageIndexes[$index]"); var link = new LinkButton(); li.Children.Add(link); link.SetDataContextType(currentPageTextContext); link.SetBinding(ButtonBase.TextProperty, currentPageTextBinding); link.SetBinding(ButtonBase.ClickProperty, commonBindings.GoToThisPageCommand); object enabledValue = HasValueBinding(EnabledProperty) ? (object)ValueBindingExpression.CreateBinding(bindingService.WithoutInitialization(), h => GetValueBinding(EnabledProperty).Evaluate(this), new JsSymbolicParameter(JavascriptTranslator.KnockoutContextParameter).Member("$pagerEnabled")) : Enabled; if (!true.Equals(enabledValue)) { link.SetValue(LinkButton.EnabledProperty, enabledValue); } NumberButtonsPlaceHolder.Children.Add(li); return(li); }
private void SetUpServerItem(IDotvvmRequestContext context, object item, int index, IValueBinding itemBinding, DataItemContainer container) { container.DataItemIndex = index; var bindingService = context.Configuration.ServiceLocator.GetService <BindingCompilationService>(); container.SetBinding(DataContextProperty, ValueBindingExpression.CreateBinding( bindingService.WithoutInitialization(), j => item, itemBinding.KnockoutExpression.AssignParameters(p => p == JavascriptTranslator.CurrentIndexParameter ? new CodeParameterAssignment(index.ToString(), OperatorPrecedence.Max) : default(CodeParameterAssignment)))); container.SetValue(Internal.PathFragmentProperty, GetPathFragmentExpression() + "/[" + index + "]"); container.ID = index.ToString(); }
public void CommandResolver_Valid_SimpleTest() { var path = new[] { ValueBindingExpression.CreateBinding <object>(bindingService, vm => ((Test1)vm[0]).A[0], (DataContextStack)null) }; var commandId = "someCommand"; var command = new CommandBindingExpression(bindingService, vm => { ((TestA)vm[0]).Test(((TestA)vm[0]).StringToPass, ((dynamic)vm[1]).NumberToPass); }, commandId); var testObject = new Test1 { A = new[] { new TestA() { StringToPass = "******" } }, NumberToPass = 16 }; var viewRoot = new DotvvmView() { DataContext = testObject }; viewRoot.SetBinding(Controls.Validation.TargetProperty, ValueBindingExpression.CreateBinding(bindingService, vm => vm.Last(), new ParametrizedCode("$root"))); var placeholder = new HtmlGenericControl("div"); placeholder.SetBinding(DotvvmBindableObject.DataContextProperty, path[0]); viewRoot.Children.Add(placeholder); var button = new Button(); button.SetBinding(ButtonBase.ClickProperty, command); placeholder.Children.Add(button); var resolver = new CommandResolver(); var context = new TestDotvvmRequestContext() { ViewModel = testObject, ModelState = new ModelState() }; context.ModelState.ValidationTargetPath = KnockoutHelper.GetValidationTargetExpression(button); resolver.GetFunction(viewRoot, context, path.Select(v => v.GetProperty <SimplePathExpressionBindingProperty>().Code.FormatKnockoutScript(button, v)).ToArray(), commandId, new Func <Type, object> [0]).Action(); Assert.AreEqual(testObject.NumberToPass, testObject.A[0].ResultInt); Assert.AreEqual(testObject.A[0].ResultString, testObject.A[0].ResultString); }
/// <summary> /// Performs the data-binding and builds the controls inside the <see cref="Repeater"/>. /// </summary> private void DataBind(IDotvvmRequestContext context) { Children.Clear(); emptyDataContainer = null; var dataSourceBinding = GetDataSourceBinding(); var index = 0; var dataSource = DataSource; if (dataSource != null) { var itemBinding = GetItemBinding(); var bindingService = context.Configuration.ServiceLocator.GetService <BindingCompilationService>(); foreach (var item in GetIEnumerableFromDataSource()) { var placeholder = new DataItemContainer { DataItemIndex = index }; ItemTemplate.BuildContent(context, placeholder); placeholder.SetBinding(DataContextProperty, ValueBindingExpression.CreateBinding( bindingService, j => item, itemBinding.KnockoutExpression.AssignParameters(p => p == JavascriptTranslator.CurrentIndexParameter ? new CodeParameterAssignment(index.ToString(), OperatorPrecedence.Max) : default(CodeParameterAssignment)))); placeholder.SetValue(Internal.PathFragmentProperty, GetPathFragmentExpression() + "/[" + index + "]"); placeholder.ID = index.ToString(); Children.Add(placeholder); index++; } numberOfRows = index; } else { numberOfRows = 0; } // add empty item if (EmptyDataTemplate != null) { emptyDataContainer = new EmptyData(); emptyDataContainer.SetValue(EmptyData.RenderWrapperTagProperty, GetValueRaw(RenderWrapperTagProperty)); emptyDataContainer.SetValue(EmptyData.WrapperTagNameProperty, GetValueRaw(WrapperTagNameProperty)); emptyDataContainer.SetBinding(DataSourceProperty, dataSourceBinding); EmptyDataTemplate.BuildContent(context, emptyDataContainer); Children.Add(emptyDataContainer); } }
public ValueBindingExpression CompileValueBinding(string expression, Type[] contexts, Type expectedType) { var context = DataContextStack.Create(contexts.FirstOrDefault() ?? typeof(object), extensionParameters: new BindingExtensionParameter[]{ new BindingPageInfoExtensionParameter(), }.Concat(configuration.Markup.DefaultExtensionParameters).ToArray()); for (int i = 1; i < contexts.Length; i++) { context = DataContextStack.Create(contexts[i], context); } var valueBinding = new ValueBindingExpression(bindingService, new object[] { context, new OriginalStringBindingProperty(expression), new BindingParserOptions(typeof(ValueBindingExpression)).AddImports(configuration.Markup.ImportedNamespaces), new ExpectedTypeBindingProperty(expectedType ?? typeof(object)) }); return valueBinding; }
public void GridViewTextColumn_RenderedHtmlTest_ServerRendering() { var gridView = new GridView() { Columns = new List <GridViewColumn> { new GridViewTextColumn() { HeaderCssClass = "lol", HeaderText = "Header Text", ValueBinding = ValueBindingExpression.CreateBinding(BindingService, h => (object)h[0], (DataContextStack)null) } }, DataSource = ValueBindingExpression.CreateBinding(BindingService, h => (IList)h[0], (DataContextStack)null), }; gridView.SetValue(RenderSettings.ModeProperty, RenderMode.Server); var viewModel = new[] { "ROW 1", "ROW 2", "ROW 3" }; var html = InvokeLifecycleAndRender(gridView, CreateContext(viewModel)); Assert.IsTrue(html.Contains("ROW 2")); Assert.IsTrue(html.Contains("class=\"lol\"")); }
private ValueBindingExpression GetNearIndexesBinding(IDotvvmRequestContext context, int i, DataContextStack dataContext = null) { return(ValueBindingExpression.CreateBinding( context.Configuration.ServiceLocator.GetService <BindingCompilationService>(), h => ((IPageableGridViewDataSet)h[0]).PagingOptions.NearPageIndexes[i], dataContext)); }
protected override void RenderContents(IHtmlWriter writer, IDotvvmRequestContext context) { writer.AddKnockoutDataBind("css", "{ 'disabled': PagingOptions().IsFirstPage() }"); firstLi.Render(writer, context); writer.AddKnockoutDataBind("css", "{ 'disabled': PagingOptions().IsFirstPage() }"); previousLi.Render(writer, context); // render template writer.WriteKnockoutForeachComment("PagingOptions().NearPageIndexes"); // render page number numbersPlaceHolder.Children.Clear(); HtmlGenericControl li; var currentPageTextContext = DataContextStack.Create(typeof(int), numbersPlaceHolder.GetDataContextType()); var currentPageTextBinding = ValueBindingExpression.CreateBinding(bindingService.WithoutInitialization(), vm => ((int)vm[0] + 1).ToString(), currentPageTextJs, currentPageTextContext); if (!RenderLinkForCurrentPage) { writer.AddKnockoutDataBind("visible", "$data == $parent.PagingOptions().PageIndex()"); writer.AddKnockoutDataBind("css", "{'active': $data == $parent.PagingOptions().PageIndex()}"); li = new HtmlGenericControl("li"); var literal = new Literal(); literal.DataContext = 0; literal.SetDataContextType(currentPageTextContext); literal.SetBinding(Literal.TextProperty, currentPageTextBinding); li.Children.Add(literal); numbersPlaceHolder.Children.Add(li); li.Render(writer, context); writer.AddKnockoutDataBind("visible", "$data != $parent.PagingOptions().PageIndex()"); } writer.AddKnockoutDataBind("css", "{ 'active': $data == $parent.PagingOptions().PageIndex()}"); li = new HtmlGenericControl("li"); li.SetValue(Internal.PathFragmentProperty, "PagingOptions.NearPageIndexes[$index]"); var link = new LinkButton(); li.Children.Add(link); link.SetDataContextType(currentPageTextContext); link.SetBinding(ButtonBase.TextProperty, currentPageTextBinding); link.SetBinding(ButtonBase.ClickProperty, commonBindings.GoToThisPageCommand); object enabledValue = HasValueBinding(EnabledProperty) ? (object)ValueBindingExpression.CreateBinding(bindingService.WithoutInitialization(), h => GetValueBinding(EnabledProperty).Evaluate(this), new JsSymbolicParameter(JavascriptTranslator.KnockoutContextParameter).Member("$pagerEnabled")) : Enabled; if (!true.Equals(enabledValue)) { link.SetValue(LinkButton.EnabledProperty, enabledValue); } numbersPlaceHolder.Children.Add(li); li.Render(writer, context); writer.WriteKnockoutDataBindEndComment(); writer.AddKnockoutDataBind("css", "{ 'disabled': PagingOptions().IsLastPage() }"); nextLi.Render(writer, context); writer.AddKnockoutDataBind("css", "{ 'disabled': PagingOptions().IsLastPage() }"); lastLi.Render(writer, context); }
private void DataBind(Hosting.IDotvvmRequestContext context) { if (DataSet is IRefreshableGridViewDataSet refreshableDataSet) { CallGridViewDataSetRefreshRequest(refreshableDataSet); } Children.Clear(); content = new HtmlGenericControl("ul"); var dataContextType = DataContextStack.Create(typeof(IPageableGridViewDataSet), this.GetDataContextType()); content.SetDataContextType(dataContextType); content.SetBinding(DataContextProperty, GetDataSetBinding()); Children.Add(content); var bindings = context.Services.GetService <CommonBindings>(); var dataSet = DataSet; if (dataSet != null) { object enabledValue = HasValueBinding(EnabledProperty) ? (object)ValueBindingExpression.CreateBinding <bool>( bindingService.WithoutInitialization(), h => (bool)GetValueBinding(EnabledProperty).Evaluate(this), new JsSymbolicParameter(JavascriptTranslator.KnockoutContextParameter).Member("$pagerEnabled")) : Enabled; // first button firstLi = new HtmlGenericControl("li"); var firstLink = new LinkButton(); SetButtonContent(context, firstLink, "««", FirstPageTemplate); firstLink.SetBinding(ButtonBase.ClickProperty, bindings.GoToFirstPageCommand); if (!true.Equals(enabledValue)) { firstLink.SetValue(LinkButton.EnabledProperty, enabledValue); } firstLi.Children.Add(firstLink); content.Children.Add(firstLi); // previous button previousLi = new HtmlGenericControl("li"); var previousLink = new LinkButton(); SetButtonContent(context, previousLink, "«", PreviousPageTemplate); previousLink.SetBinding(ButtonBase.ClickProperty, bindings.GoToPrevPageCommand); if (!true.Equals(enabledValue)) { previousLink.SetValue(LinkButton.EnabledProperty, enabledValue); } previousLi.Children.Add(previousLink); content.Children.Add(previousLi); // number fields numbersPlaceHolder = new PlaceHolder(); content.Children.Add(numbersPlaceHolder); var i = 0; foreach (var number in dataSet.PagingOptions.NearPageIndexes) { var li = new HtmlGenericControl("li"); li.SetBinding(DataContextProperty, GetNearIndexesBinding(context, i, dataContextType)); if (number == dataSet.PagingOptions.PageIndex) { li.Attributes["class"] = "active"; } var link = new LinkButton() { Text = (number + 1).ToString() }; link.SetBinding(ButtonBase.ClickProperty, bindings.GoToThisPageCommand); if (!true.Equals(enabledValue)) { link.SetValue(LinkButton.EnabledProperty, enabledValue); } li.Children.Add(link); numbersPlaceHolder.Children.Add(li); i++; } // next button nextLi = new HtmlGenericControl("li"); var nextLink = new LinkButton(); SetButtonContent(context, nextLink, "»", NextPageTemplate); nextLink.SetBinding(ButtonBase.ClickProperty, bindings.GoToNextPageCommand); if (!true.Equals(enabledValue)) { nextLink.SetValue(LinkButton.EnabledProperty, enabledValue); } nextLi.Children.Add(nextLink); content.Children.Add(nextLi); // last button lastLi = new HtmlGenericControl("li"); var lastLink = new LinkButton(); SetButtonContent(context, lastLink, "»»", LastPageTemplate); if (!true.Equals(enabledValue)) { lastLink.SetValue(LinkButton.EnabledProperty, enabledValue); } lastLink.SetBinding(ButtonBase.ClickProperty, bindings.GoToLastPageCommand); lastLi.Children.Add(lastLink); content.Children.Add(lastLi); } }
protected virtual void DataBind(Hosting.IDotvvmRequestContext context) { Children.Clear(); var dataContextType = DataContextStack.Create(typeof(IPageableGridViewDataSet), this.GetDataContextType()); ContentWrapper = CreateWrapperList(dataContextType); Children.Add(ContentWrapper); var bindings = context.Services.GetRequiredService <CommonBindings>(); object enabledValue = (HasValueBinding(EnabledProperty) ? (object)ValueBindingExpression.CreateBinding <bool>( bindingService.WithoutInitialization(), h => (bool)GetValueBinding(EnabledProperty).Evaluate(this), new JsSymbolicParameter(JavascriptTranslator.KnockoutContextParameter).Member("$pagerEnabled")) : Enabled); GoToFirstPageButton = CreateNavigationButton("««", FirstPageTemplate, enabledValue, bindings.GoToFirstPageCommand, context); ContentWrapper.Children.Add(GoToFirstPageButton); GoToPreviousPageButton = CreateNavigationButton("«", PreviousPageTemplate, enabledValue, bindings.GoToPrevPageCommand, context); ContentWrapper.Children.Add(GoToPreviousPageButton); // number fields NumberButtonsPlaceHolder = new PlaceHolder(); ContentWrapper.Children.Add(NumberButtonsPlaceHolder); var dataSet = DataSet; if (dataSet != null) { var i = 0; foreach (var number in dataSet.PagingOptions.NearPageIndexes) { var li = new HtmlGenericControl("li"); li.SetBinding(DataContextProperty, GetNearIndexesBinding(context, i, dataContextType)); if (number == dataSet.PagingOptions.PageIndex) { li.Attributes["class"] = "active"; } var link = new LinkButton() { Text = (number + 1).ToString() }; link.SetBinding(ButtonBase.ClickProperty, bindings.GoToThisPageCommand); if (!true.Equals(enabledValue)) { link.SetValue(LinkButton.EnabledProperty, enabledValue); } li.Children.Add(link); NumberButtonsPlaceHolder.Children.Add(li); i++; } } GoToNextPageButton = CreateNavigationButton("»", NextPageTemplate, enabledValue, bindings.GoToNextPageCommand, context); ContentWrapper.Children.Add(GoToNextPageButton); GoToLastPageButton = CreateNavigationButton("»»", LastPageTemplate, enabledValue, bindings.GoToLastPageCommand, context); ContentWrapper.Children.Add(GoToLastPageButton); }
private void DataBind(IDotvvmRequestContext context) { Children.Clear(); emptyDataContainer = null; head = null; var dataSourceBinding = GetDataSourceBinding(); var dataSource = DataSource; if (dataSource is IRefreshableGridViewDataSet refreshableDataSet) { CallGridViewDataSetRefreshRequest(refreshableDataSet); } var sortCommand = dataSource is ISortableGridViewDataSet sortableGridViewDataSet ? sortableGridViewDataSet.SetSortExpression : SortChanged; // WORKAROUND: DataSource is null => don't throw exception if (sortCommand == null && dataSource == null) { sortCommand = s => { throw new DotvvmControlException(this, "Cannot sort when DataSource is null."); }; } CreateHeaderRow(context, sortCommand); var index = 0; if (dataSource != null) { var itemBinding = GetItemBinding(); var bindingService = context.Configuration.ServiceLocator.GetService <BindingCompilationService>(); foreach (var item in GetIEnumerableFromDataSource()) { // create row var placeholder = new DataItemContainer { DataItemIndex = index }; placeholder.SetBinding(DataContextProperty, ValueBindingExpression.CreateBinding( bindingService, j => item, itemBinding.KnockoutExpression.AssignParameters(p => p == JavascriptTranslator.CurrentIndexParameter ? new CodeParameterAssignment(index.ToString(), OperatorPrecedence.Max) : default(CodeParameterAssignment)))); placeholder.SetValue(Internal.PathFragmentProperty, GetPathFragmentExpression() + "/[" + index + "]"); placeholder.ID = index.ToString(); Children.Add(placeholder); CreateRowWithCells(context, placeholder); index++; } numberOfRows = index; } else { numberOfRows = 0; } // add empty item if (EmptyDataTemplate != null) { emptyDataContainer = new EmptyData(); emptyDataContainer.SetBinding(DataSourceProperty, dataSourceBinding); EmptyDataTemplate.BuildContent(context, emptyDataContainer); Children.Add(emptyDataContainer); } }