yeomandev
yeomandev

Reputation: 11796

How to add properties to a previously created object

I'm not sure if what I'm trying to do makes sense. I am attempting to make a portable pagination widget to use in asp.net mvc.

The tricky part is that I'm storing an object for route values.

public class PaginatedList<T> : List<T>
{
    public PaginationData PaginationData { get; private set; }

    public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize)
    {
        PaginationData = new PaginationData( source.Count() , pageIndex, pageSize);
        this.AddRange(source.Skip((PaginationData.PageIndex - 1) * PaginationData.PageSize).Take(PaginationData.PageSize));
    }

}

public class PaginationData
{
    ////////////////////////////////////////////
    public object PageRoute { get; set; } //   <-- object for route values
    ////////////////////////////////////////////
    public bool HasPreviousPage { get { return (PageIndex > 1); } }
    public bool HasNextPage { get { return (PageIndex < TotalPages); } }
    public int PageIndex { get; private set; }
    public int PageSize { get; private set; }
    public int TotalCount { get; private set; }
    public int TotalPages { get; private set; }

    public PaginationData(int count, int pageIndex, int pageSize)
    {
        PageIndex = pageIndex;
        PageSize = pageSize;
        TotalCount = count;
        TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
    }
}

So I can then define the base route info at the controller level like this:

        PaginatedList<Member> paginatedMembers = new PaginatedList<Member>(members, page, 2);
        // define base url route
        paginatedMembers.PaginationData.PageRoute = new { controller = "AdminMember", action = "MemberList", keyword = keyword };

This allows me to add values like keyword=keyword for the case where the page links should have additional data.

Then The pagination is displayed with a shared, partial view:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Genesis_0_02.Utilities.PaginationData>" %>

<% if (Model.HasPreviousPage) { %> 
        <%: Html.ActionLink("Previous", ViewContext.RouteData.Values["action"].ToString(), new { page = (Model.PageIndex - 1) })%>

    <% } %> 
    <% for (int i = 1; i <= Model.TotalPages; i++) %>
    <% { %>
            <!--How do I add {page=i} to Model.PageRoute object below?-->
            <%: Html.RouteLink(i.ToString(), Model.PageRoute)%>

    <% } %>

    <% if (Model.HasNextPage) {  %> 
        <%: Html.ActionLink("Next", ViewContext.RouteData.Values["action"].ToString(), new { page = (Model.PageIndex + 1) })%>
    <% } %> 

As you can see... the above partial view is not completed yet. I am specifically working on this line:

            <!--How do I add {page=i} to Model.PageRoute object below?-->
            <%: Html.RouteLink(i.ToString(), Model.PageRoute)%>

Is there a way to do this?

Upvotes: 0

Views: 1217

Answers (1)

Dr. Wily&#39;s Apprentice
Dr. Wily&#39;s Apprentice

Reputation: 10280

You could revise your PaginationData class to something like the following. Note: I renamed the PageRoute property to RouteValues for consistency with the MVC framework.

public class PaginationData
{
    private System.Web.Routing.RouteValueDictionary _RouteValues;

    public System.Web.Routing.RouteValueDictionary RouteValues
    {
        get
        {
            if (_RouteValues == null)
            {
                _RouteValues = new System.Web.Routing.RouteValueDictionary();
            }
            return _RouteValues;
        }
        private set { _RouteValues = value; }
    }

    public void SetRouteValues(object routeValues)
    {
        this.RouteValues = new System.Web.Routing.RouteValueDictionary(routeValues);
    }

    public bool HasPreviousPage { get { return (PageIndex > 1); } }
    public bool HasNextPage { get { return (PageIndex < TotalPages); } }
    public int PageIndex { get; private set; }
    public int PageSize { get; private set; }
    public int TotalCount { get; private set; }
    public int TotalPages { get; private set; }

    public PaginationData(int count, int pageIndex, int pageSize)
    {
        PageIndex = pageIndex;
        PageSize = pageSize;
        TotalCount = count;
        TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
    }
}

You could then add to the route values by doing the following:

Model.RouteValues.Add("key", value);

or

Model.RouteValues["key"] = value;

Alternative Approach using Extension Methods to Merge RouteValueDictionaries

Below are some extension methods that provide some functionality for RouteValueDictionaries.

public static class RouteValueExtensions
{
    public static void Merge(this RouteValueDictionary routeValuesA, object routeValuesB)
    {
        foreach (var entry in new RouteValueDictionary(routeValuesB))
        {
            routeValuesA[entry.Key] = entry.Value;
        }
    }

    public static RouteValueDictionary With(this RouteValueDictionary routeValuesA, object routeValuesB)
    {
        routeValuesA.Merge(routeValuesB);
        return routeValuesA;
    }

    public static RouteValueDictionary With(this RouteValueDictionary routeValues, params object[] routeValuesToMerge)
    {
        if (routeValues != null)
        {
            for (int i = 0; i < routeValuesToMerge.Length; i++)
            {
                routeValues.Merge(routeValuesToMerge[i]);
            }
        }
        return routeValues;
    }

    public static RouteValueDictionary RouteValues(this HtmlHelper htmlHelper, object routeValues)
    {
        return new RouteValueDictionary(routeValues);
    }

    public static RouteValueDictionary RouteValues(this HtmlHelper htmlHelper, object routeValuesA, object routeValuesB)
    {
        return htmlHelper.RouteValues(routeValuesA).With(routeValuesB);
    }

    public static RouteValueDictionary RouteValues(this HtmlHelper htmlHelper, params object[] routeValues)
    {
        if (routeValues != null && routeValues.Length > 0)
        {
            var result = htmlHelper.RouteValues(routeValues[0]);
            for (int i = 1; i < routeValues.Length; i++)
            {
                result.Merge(routeValues[i]);
            }
            return result;
        }
        else
        {
            return new RouteValueDictionary();
        }
    }
}

In this case, I think you could use them with your original model implementation like this:

Html.RouteLink(i.ToString(), Html.RouteValues(Model.PageRoute, new { page = i }))

or

Html.RouteLink(i.ToString(), Html.RouteValues(Model.PageRoute).With(new { page = i }))

The downside is that there is some excessive Reflection and instantiation of new RouteValueDictionary objects going on with this approach.

Upvotes: 2

Related Questions