Using DynamicObject as a datasource (or ExpandoObject)

14 posts, 0 answers
  1. Erik
    Erik avatar
    314 posts
    Member since:
    Feb 2008

    Posted 16 Apr 2011 Link to this post

    Hello,

    I did find some information, but that was on other forums. 

    Now we have DynamicObject (or ExpandoObject, that's an implementation of DynamicObject as I understood) we, of course, want it to bind to a RadGrid. There is no thread or blog I can find on this issue. It's also a bit of a Sunday afternoon thing for now.

    I was wondering what Telerik thinks of this usage, what would be your approach..
    And also this tread can grow to be of use to others, that's the main gaol..

    We have a dynamic class:
    Imports System.IO
    Imports System.Dynamic
      
    Public Class DynamicDictionary
        Inherits DynamicObject
      
        ' The inner dictionary.
        Dim mdic_PropertyHolder As New Dictionary(Of String, Object)
      
        ' This property returns the number of elements in the inner dictionary.
        ReadOnly Property Count As Integer
            Get
                Return mdic_PropertyHolder.Count
            End Get
        End Property
      
      
        ReadOnly Property Items As Dictionary(Of String, Object)
            Get
                Return mdic_PropertyHolder
            End Get
        End Property
      
        Public Function GetMembers() As String()
            Return mdic_PropertyHolder.Keys.ToArray
        End Function
      
        Public Property Item(ByVal PropertyName As String) As String
            Get
                Return mdic_PropertyHolder(PropertyName)
            End Get
            Set(ByVal value As String)
                mdic_PropertyHolder(PropertyName) = value
            End Set
        End Property
      
        ' If you try to get a value of a property that is not defined in the class, this method is called.
        Public Overrides Function TryGetMember(ByVal binder As System.Dynamic.GetMemberBinder, ByRef result As Object) As Boolean
            ' Converting the property name to lowercase
            ' so that property names become case-insensitive.
            Dim name As String = binder.Name.ToLower()
      
            ' If the property name is found in a dictionary, set the result parameter to the property value and return true. Otherwise, return false.
            Return mdic_PropertyHolder.TryGetValue(name, result)
        End Function
      
        Public Overrides Function TrySetMember(ByVal binder As System.Dynamic.SetMemberBinder, ByVal value As Object) As Boolean
      
            ' Converting the property name to lowercase so that property names become case-insensitive.
            mdic_PropertyHolder(binder.Name.ToLower()) = value
      
            ' You can always add a value to a dictionary, so this method always returns true.
            Return True
        End Function
      
      
    End Class

    In a asp.net page we have:
    <telerik:RadGrid ID="RadGrid1" runat="server" AllowPaging="True" GridLines="None" Width="100%" ShowGroupPanel="True" AllowSorting="True" ShowStatusBar="true">
        <MasterTableView RetrieveAllDataFields="true" AutoGenerateColumns="false">
            <Columns>
                <telerik:GridTemplateColumn HeaderText="Hopa" >
                    <ItemTemplate>
                        <span style="white-space:nowrap"><%# Container.DataItem.firstname%></span>
                    </ItemTemplate>
                </telerik:GridTemplateColumn>
                <telerik:GridBoundColumn HeaderText="FirstName" DataField="FirstName">
                </telerik:GridBoundColumn>
                <telerik:GridBoundColumn HeaderText="MiddleName" DataField="MiddleName">
                </telerik:GridBoundColumn>
                <telerik:GridBoundColumn DataField="LastName" HeaderText="LastName">
                </telerik:GridBoundColumn>
            </Columns>
        </MasterTableView>
    </telerik:RadGrid>

    In a asp.net page code we have:
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim person As Object
        Dim List As New ArrayList
        ' Adding new dynamic properties.
        ' The TrySetMember method is called.
        person = New DynamicDictionary()
        person.FirstName = "Ellen"
        person.LastName = "Adams"
        List.Add(person)
        person = New DynamicDictionary()
        person.FirstName = "Piet"
        person.LastName = "Pietersen"
        List.Add(person)
        person = New DynamicDictionary()
        person.FirstName = "Erik"
        person.MiddleName = "van der"
        person.LastName = "Heide"
        List.Add(person)
        person = New DynamicDictionary()
        person.FirstName = "Jan"
        person.LastName = "Janssen"
        List.Add(person)
        RadGrid1.DataSource = List
        RadGrid1.DataBind()
    End Sub
    Private Sub RadGrid1_ItemDataBound(sender As Object, e As Telerik.Web.UI.GridItemEventArgs) Handles RadGrid1.ItemDataBound
        If TypeOf e.Item Is GridDataItem Then
            Dim dataBoundItem As GridDataItem = e.Item
            For Each Member As String In dataBoundItem.DataItem.GetMembers
                Try
                    dataBoundItem(Member).Text = dataBoundItem.DataItem.Item(Member)
                Catch ex As Exception
                    ' Oops
                End Try
            Next
        End If
    End Sub


    This is of course not verry fast, but verry flexible it is. Editing is an issue I think, but displaying is not.

    Regards,

    Erik
  2. Vasil
    Admin
    Vasil avatar
    1547 posts

    Posted 21 Apr 2011 Link to this post

    Hi Erik,

    This approach can be very useful. Thank you for sharing it with the whole community.

    It is a nice way of using the dynamic object that is supported in .Net 4.0

    Here is C# version of the code that also supports editing.

    ASPX:
    <telerik:RadGrid ID="RadGrid1" runat="server" OnNeedDataSource="RadGrid1_NeedDataSource"
      AutoGenerateEditColumn="true" AutoGenerateColumns="false" AllowAutomaticUpdates="false"
      OnUpdateCommand="RadGrid1_UpdateCommand">
      <MasterTableView>
        <Columns>
          <telerik:GridTemplateColumn HeaderText="First name">
            <ItemTemplate>
              <%# (Container.DataItem as dynamic).FirstName%>
            </ItemTemplate>
            <EditItemTemplate>
              <asp:TextBox runat="server" ID="FirstNameTextbox" Text=" <%# (Container.DataItem as dynamic).FirstName%> "> </asp:TextBox>
            </EditItemTemplate>
          </telerik:GridTemplateColumn>
        </Columns>
      </MasterTableView>
    </telerik:RadGrid>

    C#:
    using System.Dynamic;
    using Telerik.Web.UI;
     
     
    public partial class RadGridBindingToListOfDynamicDict : System.Web.UI.Page
    {
     
        public class DynamicDictionary : DynamicObject
        {
            Dictionary<string, object> dictionary = new Dictionary<string, object>();
     
            public int Count
            {
                get
                {
                    return dictionary.Count;
                }
            }
     
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                string name = binder.Name.ToLower();
                return dictionary.TryGetValue(name, out result);
            }
     
            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                dictionary[binder.Name.ToLower()] = value;
                return true;
            }
        }
     
        protected List<dynamic> LoadData()
        {
            List<dynamic> data = new List<dynamic>();
     
            dynamic person;
     
            person = new DynamicDictionary();
     
            person.FirstName = "Ellen";
            person.LastName = "Adams";
            data.Add(person);
            person = new DynamicDictionary();
            person.FirstName = "Piet";
            person.LastName = "Pietersen";
            data.Add(person);
            person = new DynamicDictionary();
            person.FirstName = "Erik";
            person.MiddleName = "van der";
            person.LastName = "Heide";
            data.Add(person);
            person = new DynamicDictionary();
            person.FirstName = "Jan";
            person.LastName = "Janssen";
            data.Add(person);
     
            return data;
        }
     
        protected void Page_Load(object sender, EventArgs e)
        {
            if (Session["data"] == null)
            {
                Session["data"] = LoadData();
            }
        }
     
        protected void RadGrid1_NeedDataSource(object sender, Telerik.Web.UI.GridNeedDataSourceEventArgs e)
        {
            (sender as RadGrid).DataSource = (List<dynamic>)Session["data"];
        }
        protected void RadGrid1_UpdateCommand(object source, GridCommandEventArgs e)
        {
            string newFirstName = (e.Item.FindControl("FirstNameTextbox") as TextBox).Text;
            ((List<dynamic>)Session["data"])[e.Item.DataSetIndex].FirstName = newFirstName;
        }
    }

    Kind regards,
    Vasil
    the Telerik team

    Browse the vast support resources we have to jump start your development with RadControls for ASP.NET AJAX. See how to integrate our AJAX controls seamlessly in SharePoint 2007/2010 visiting our common SharePoint portal.

  3. UI for ASP.NET Ajax is Ready for VS 2017
  4. Cristian
    Cristian avatar
    2 posts
    Member since:
    Dec 2010

    Posted 16 Sep 2011 Link to this post

    Hi,

    I used the above example which works fine but I have problems grouping.
    When I try to group by one of the columns I get the following Server exception:
    at Telerik.Web.UI.GridDataSetHelper.CreateGroupByTable(DataTable SourceTable, GridTableView view)
    at Telerik.Web.UI.GridDataSetHelper.CalcGroupByTables(GridTableView gridTableView, DataTable SourceTable, String RowFilter, Int32 FirstIndex, Int32 LastIndex, Boolean applyPaging, Boolean isCustomPaging)
    at Telerik.Web.UI.GridEnumerableFromDataView.PerformTransformation()
    at Telerik.Web.UI.GridEnumerableFromDataView.TransformEnumerable()
    at Telerik.Web.UI.GridTableView.GetEnumerator(Boolean useDataSource, GridEnumerableBase resolvedDataSource, ArrayList dataKeysArray)
    at Telerik.Web.UI.GridTableView.CreateControlHierarchy(Boolean useDataSource)
    at Telerik.Web.UI.GridTableView.CreateChildControls(IEnumerable dataSource, Boolean useDataSource)
    at System.Web.UI.WebControls.CompositeDataBoundControl.PerformDataBinding(IEnumerable data)
    at System.Web.UI.WebControls.DataBoundControl.OnDataSourceViewSelectCallback(IEnumerable data)
    at System.Web.UI.DataSourceView.Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback)
    at System.Web.UI.WebControls.DataBoundControl.PerformSelect()
    at Telerik.Web.UI.GridTableView.PerformSelect()
    at System.Web.UI.WebControls.BaseDataBoundControl.DataBind()
    at Telerik.Web.UI.GridTableView.DataBind()
    at Telerik.Web.UI.GridTableView.BindAllInHierarchyLevel()
    at Telerik.Web.UI.RadGrid.RaisePostBackEvent(String eventArgument)
    at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
    at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)
    at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)


    or on Client side:
    Microsoft JScript runtime error: Sys.WebForms.PageRequestManagerServerErrorException: Field FirstName not found in the source table. Please check the expression syntax.

    <telerik:RadGrid ID="RadGrid2" runat="server" PageSize="10" AllowSorting="True" AllowMultiRowSelection="True"
    		AllowPaging="True" AllowCustomPaging="false" ShowGroupPanel="True" AutoGenerateColumns="false"
    		GridLines="none">
    		<PagerStyle Mode="NextPrevNumericAndAdvanced"></PagerStyle>
    		<ClientSettings ReorderColumnsOnClient="True" AllowDragToGroup="True" AllowColumnsReorder="True">
    			<Selecting AllowRowSelect="True"></Selecting>
    			<Resizing AllowRowResize="false" AllowColumnResize="True" EnableRealTimeResize="True"
    				ResizeGridOnColumnResize="False"></Resizing>
    		</ClientSettings>
    		<GroupingSettings ShowUnGroupButton="true" />
    		<MasterTableView Width="100%">
    			<Columns>
    				<telerik:GridTemplateColumn HeaderText="First name" DataField="LastName" SortExpression="LastName"
    					GroupByExpression="FirstName group by FirstName">
    					<ItemTemplate>
    						<%# (Container.DataItem as dynamic).FirstName%>
    					</ItemTemplate>
    					<EditItemTemplate>
    						<asp:TextBox runat="server" ID="FirstNameTextbox" Text=<%# (Container.DataItem as dynamic).FirstName%> "> </asp:TextBox>
    					</EditItemTemplate>
    				</telerik:GridTemplateColumn>
    				<telerik:GridTemplateColumn HeaderText="Last name" DataField="LastName" SortExpression="LastName"
    					GroupByExpression="LastName group by LastName">
    					<ItemTemplate>
    						<%# (Container.DataItem as dynamic).LastName%>
    					</ItemTemplate>
    					<EditItemTemplate>
    						<asp:TextBox runat="server" ID="LastNameTextbox" Text=<%# (Container.DataItem as dynamic).LastName%> "> </asp:TextBox>
    					</EditItemTemplate>
    				</telerik:GridTemplateColumn>
    			</Columns>
    		</MasterTableView>
    	</telerik:RadGrid>
  5. Radoslav
    Admin
    Radoslav avatar
    1564 posts

    Posted 21 Sep 2011 Link to this post

    Hi Cristian,

    Unfortunately the RadGrid does not support filtering/sorting/paging when it is bound to collection of . DynamicObjects. RadGrid supports binding to generic collection of .NET 4.0 dynamic type objects. For more information please check out the following online example:
    http://demos.telerik.com/aspnet-ajax/grid/examples/programming/dynamictypes/defaultcs.aspx

    Regards,
    Radoslav
    the Telerik team
    If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now
  6. Josh
    Josh avatar
    1 posts
    Member since:
    Oct 2011

    Posted 27 Oct 2011 Link to this post

    How would a dynamic object be used with the Reporting Table?
    [=Fields.FirstName]
    and
    this.textBox.Value = "=(Container.DataItem as dynamic).FirstName";
    don't seem to work.

    thanks
  7. Radoslav
    Admin
    Radoslav avatar
    1564 posts

    Posted 01 Nov 2011 Link to this post

    Hello Josh,

    If you want to bind a control property to the value of object's property boxed into dynamic object by using binding expressions you could try using Eval and bind directly to the property. For example:
    <telerik:RadGrid ID="RadGrid1" AllowFilteringByColumn="true" runat="server" AllowPaging="true"
          PageSize="4" AutoGenerateColumns="true" ShowStatusBar="true" AllowSorting="true"
          OnNeedDataSource="RadGrid1_NeedDataSource">
          <MasterTableView PagerStyle-AlwaysVisible="true">
          <Columns>
              <telerik:GridTemplateColumn DataField="Name">
                  <ItemTemplate>
                      <asp:TextBox runat="server" ID="TextBox1" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>'></asp:TextBox>
                  </ItemTemplate>
              </telerik:GridTemplateColumn>
          </Columns>
          </MasterTableView>
          <ClientSettings EnablePostBackOnRowClick="true">
              <Selecting AllowRowSelect="true" />
          </ClientSettings>
      </telerik:RadGrid>
    protected void RadGrid1_NeedDataSource(object sender, GridNeedDataSourceEventArgs e)
        {
            dynamic data = new[] {
                    new { ID = 1, Name ="Name1"},
                    new { ID = 2, Name = "Name2"},
                    new { ID = 3, Name = "Name3"},
                    new { ID = 4, Name = "Name4"},
                    new { ID = 5, Name = "Name5"},
                    new { ID = 6, Name ="Name6"},
                    new { ID = 7, Name = "Name7"},
                    new { ID = 8, Name = "Name8"},
                    new { ID = 9, Name = "Name9"},
                    new { ID = 10, Name = "Name10"},
                    new { ID = 11, Name ="Name11"},
                    new { ID = 12, Name = "Name12"},
                    new { ID = 13, Name = "Name13"},
                    new { ID = 14, Name = "Name14"},
                    new { ID = 15, Name = "Name15"}
                };
     
            RadGrid1.DataSource = data;
        }

    I hope this helps.

    Kind regards,
    Radoslav
    the Telerik team
    If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now
  8. Jitender
    Jitender avatar
    4 posts
    Member since:
    Feb 2013

    Posted 23 Feb 2013 Link to this post

    Hi Telerik Team,

    I am using Normal Telerik grid control not Rad one. But can this above dynamic list bind with my telerik normal grid control as well?
  9. Radoslav
    Admin
    Radoslav avatar
    1564 posts

    Posted 27 Feb 2013 Link to this post

    Hello Jitender,

    Could you please elaborate a bit more on your scenario? What do you mean by “I am using Normal Telerik grid control not Rad one”?

    Looking forward for your reply.

    Kind regards,
    Radoslav
    the Telerik team
    If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now.
  10. Jitender
    Jitender avatar
    4 posts
    Member since:
    Feb 2013

    Posted 27 Feb 2013 Link to this post

    my apologies for this confusion. I am using Telerik grid @(Html.Telerik.grid) control. So can i use above solution?
  11. Jitender
    Jitender avatar
    4 posts
    Member since:
    Feb 2013

    Posted 02 Mar 2013 Link to this post

    Hi Telerik Team,

    From last 2 weeks i am running into a problem. I am working with MVC applicaiton where i have to implement Telerik grid with dynamic columns. I have done this by Data Table. This table containing column name as DateTime like "2/3/2013 Sunday" or "7:30 AM" and this is the issue. Telerik grid is not binding data with date and time column name. Though if i change the column name to any string value like "Sunday" or any "Text" value then it is working fine.

    Index.cshtml

    @using System.Data;
     
    @model DataTable
     
    @{
        ViewBag.Title = "Home Page";
    }
     
     
    <h2>@ViewBag.Message</h2>
     
     
        @(Html.Telerik().Grid<System.Data.DataRow>(Model.Rows.Cast<System.Data.DataRow>())
                    .Name("Grid")
                              .Columns(columns =>
                              {
                                  foreach (System.Data.DataColumn column in Model.Columns)
                                  {
                                      if(column.ColumnName != "ShopName")
                                          columns.Bound(column.DataType, column.ColumnName).Width(50).Sortable(false).Filterable(false);
                                      else
                                          columns.Bound(column.DataType, column.ColumnName).Width(50).Sortable(true).Filterable(true);
                                  }
                              })
                            .DataBinding(dataBinding => dataBinding.Ajax().Select("_CustomBinding", "Home"))
                            .Pageable()                       
                            .Sortable()
                            .Filterable()
                             
        )

    HomeController.cs

    public class HomeController : Controller
        {
            private static int count;
            ShopLoadGridModel model = new ShopLoadGridModel();
     
            public ActionResult Index()
            {
                ViewBag.Message = "Welcome to ASP.NET MVC!";
                ViewData["total"] = 100;
     
                DataTable table = new DataTable();
                table = model.DataTableColumnSettings;
     
                return View(table);
            }
     
            [GridAction(EnableCustomBinding = true)]
            public ActionResult _CustomBinding(GridCommand command)
            {
                model.ShopLoadGridCommand = command;
                model.Load();
                IEnumerable data = model.ShopLoadGridSummaryIEnumerable;
                count = 3;
                GridModel model1 = new GridModel();
                return View(new GridModel { Data = data, Total=count });
            }       
        }

    Model Class
    Creating column settings with column name
    DataTable table = new DataTable();
                table.Columns.Add("ShopName", typeof(string));
                table.Columns.Add("WIP", typeof(string));
                table.Columns.Add("CurrentRO", typeof(string));
                table.Columns.Add("Total", typeof(string));
     
                bool continueLoop = true;
                int limit = 0;
     
                while (continueLoop)
                {
                    //I want column name like from 7:30 AM to 8:30 PM, if below Date and days column name works fine
                    string columnName = DateTime.Now.AddDays(limit).ToShortDateString() +" " + DateTime.Now.AddDays(limit).DayOfWeek;
                    table.Columns.Add(columnName, typeof(string));
     
                    if (limit == 7)// No of columns (5 static + 56 dynamic)
                        continueLoop = false;
     
                    limit++;
                }
                return table;

    Filling data which will display in grid

    var serializer = new JavaScriptSerializer();
                serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJSONConverter() });
                List<Dictionary<string, string>> data = new List<Dictionary<string, string>>();
     
                bool continueLoop = true;
                int limit = 0;
                for (int i = 1; i <= 3; i++)
                {
                    continueLoop = true;
                    limit = 0;
                    dynamic diarioModel = new ExpandoObject();
                    var dictionary = (IDictionary<string, string>)diarioModel;
                    dictionary.Add("ShopName", "Shop" + i.ToString());
                    dictionary.Add("WIP", i.ToString());
                    dictionary.Add("CurrentRO", i.ToString());
                    dictionary.Add("Total", i.ToString());
                    dictionary.Add("Amount", i.ToString());
     
                    while (continueLoop)
                    {
                        string columnName = DateTime.Now.AddDays(limit).ToShortDateString() +" " + DateTime.Now.AddDays(limit).DayOfWeek;
                        dictionary.Add(columnName, Convert.ToString(limit + i));
     
                        if (limit == 7)// No of columns (5 static + 56 dynamic)
                            continueLoop = false;
     
                        limit++;
                    }
     
                    var json = serializer.Serialize(dictionary);
                    var d = serializer.Deserialize<dynamic>(json);
                    data.Add(d);
                }

    I have mentioned all the major code here, but if want test project i can share.


    For any help will much appreciated.

    Thanks,
    Jitender
  12. Daniel
    Admin
    Daniel avatar
    2117 posts

    Posted 04 Mar 2013 Link to this post

    Hello Jitender,

    The column field name should be valid for JavaScript and currently the name contains invalid characters. You should remove all invalid characters from the name in order for the data to be bound correctly. If you wish to show the title in a different format then you can use the Title method e.g.

    table.Columns.Add(new DataColumn
           {
               ColumnName = DateTime.Now.AddDays(limit).DayOfWeek + DateTime.Now.AddDays(limit).ToShortDateString().Replace(" ", "").Replace("/",""),
               Caption = DateTime.Now.AddDays(limit).ToShortDateString() + " " + DateTime.Now.AddDays(limit).DayOfWeek,
               DataType = typeof(string)
           });
    columns.Bound(column.DataType, column.ColumnName).Title(column.Caption).Width(50).Sortable(true).Filterable(true);
    Regards,
    Daniel
    the Telerik team
    If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now.
  13. Jitender
    Jitender avatar
    4 posts
    Member since:
    Feb 2013

    Posted 04 Mar 2013 Link to this post

    Thanks Daniel,

    Suggestion provided by you surely the one which i wanted. BTW i have resolved this issue by using GridColumnSettings for column setting and datatable for dynamic data using MVC kendo UI grid control.

    Thanks again :)

    -Jitender
  14. Joel Palmer
    Joel Palmer avatar
    164 posts
    Member since:
    May 2009

    Posted 26 Aug 2013 Link to this post

    Do you have an example of using the DynamicObject or ExpandoObject and using DataAnnotation?  I'd like to use this approach but don't see any obvious way to connect DataAnnotations to each property.  Reflection?  I appreciate an example.
  15. Daniel
    Admin
    Daniel avatar
    2117 posts

    Posted 29 Aug 2013 Link to this post

    Hello,

    There isn't a way to use DataAnnotation attributes with dynamic objects.

    Regards,
    Daniel
    Telerik
    If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to the blog feed now.
Back to Top
UI for ASP.NET Ajax is Ready for VS 2017