Model Binder For Two Dimensional Array

     Introduction:

          One of the feature that I really like most in ASP.NET MVC is Model Binding. ASP.NET MVC model binding makes it easy to bind action method parameters with incoming HTTP request data. By default DefaultModelBinder is responsible for binding posted values to action method parameters because DefaultModelBinder knows the rules of action method parameter binding. The default binder does excellent work for most but not all cases. For example, DefaultModelBinder will not bind two dimensional array parameter. For these cases, ASP.NET MVC allows you to create a custom model binder. In this article I will show you how to create a custom model binder that allows you to bind two dimensional array parameter. 

    Description:

          For demonstrating how to create and use custom model binder for two dimensional array, let's say you are creating a web page that is collecting two matrix data and then displaying matrix multiplication result. Also only for demonstration, you are collecting an extra comment matrix. So the web page collecting matrix data will be look like,

 

 

          After submitting these matrices you will see the following screen,

 

  

 

          Let's create a sample ASP.NET MVC application that accomplishes this scenario. First of all open HomeController.cs and add the following code,


        
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Index([ModelBinder(typeof(TwoDimensionalArrayBinder<int>))] int[,] MatrixA, [ModelBinder(typeof(TwoDimensionalArrayBinder<int>))] int[,] MatrixB, [ModelBinder(typeof(TwoDimensionalArrayBinder<string>))] string[,] Comments)
        {
            int[,] Reslutant = new int[MatrixA.GetLength(0), MatrixB.GetLength(1)];
            for (int i = 0; i < MatrixA.GetLength(0); i++)
                for (int j = 0; j < MatrixB.GetLength(1); j++)
                    for (int k = 0; k < MatrixA.GetLength(1); k++)
                        Reslutant[i, j] += MatrixA[i, k] * MatrixB[k, j];
            return View("MatrixResult", new MatrixResult { MatrixA = MatrixA, MatrixB = MatrixB, Comments = Comments, Reslutant = Reslutant });
        }

          Next create a new class file TwoDimensionalArrayBinder.cs inside Model folder and add the following code,


    
    public class TwoDimensionalArrayBinder<T> : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            string key = bindingContext.ModelName;
            try
            {
                int totalRows = 0;
                while (bindingContext.ValueProvider.GetValue(key + totalRows) != null)
                    totalRows++;
                ValueProviderResult[] val = new ValueProviderResult[totalRows];
                for (int i = 0; i < totalRows; i++)
                    val[i] = bindingContext.ValueProvider.GetValue(key + i);
                if (val.Length > 0)
                {
                    int totalColumn = ((string[])val[0].RawValue).Length;
                    T[,] twoDimensionalArray = new T[totalRows, totalColumn];
                    StringBuilder attemptedValue = new StringBuilder();
                    for (int i = 0; i < totalRows; i++)
                    {
                        for (int j = 0; j < totalColumn; j++)
                        {
                            twoDimensionalArray[i, j] = (T)Convert.ChangeType(((string[])val[i].RawValue)[j], typeof(T));
                            attemptedValue.Append(twoDimensionalArray[i, j]);
                        }
                    }
                    bindingContext.ModelState.SetModelValue(key, new ValueProviderResult(twoDimensionalArray, attemptedValue.ToString(), CultureInfo.InvariantCulture));
                    return twoDimensionalArray;
                }
            }
            catch
            {
                bindingContext.ModelState.AddModelError(key, "Data is not in correct Format");
            }
            return null;
        }
    }

          Next create a new class file MatrixResult.cs inside Model folder and add the following code, 


    public class MatrixResult
    {
        public int[,] MatrixA;
        public int[,] MatrixB;
        public int[,] Reslutant;
        public string[,] Comments;
    }

          Next open the Index view of HomeController and add the following lines,


<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">    
    <%using(Html.BeginForm()){ %>
        <table>
            <tr>
                <td>
                    <table cellpadding="0px" cellspacing="0px" style="border:0px">
                        <caption>Matrix A</caption>
                        <%for (int i = 0; i < 3; i++)
                        {%>
                            <tr>
                                <% for (int j = 0; j < 3; j++)
                                {%>
                                    <td style="padding:0px;border:0px">
                                        <%=Html.TextBox("MatrixA" + i, null, new {style="width:40px",maxlength="4" })%>
                                    </td>
                                <%}%>
                            </tr>
                        <%} %>
                    </table>
                </td>
                <td style="font-size:20px;font-weight:bold;">
                    X
                </td>
                <td>
                    <table cellpadding="0px" cellspacing="0px" style="border:0px">
                        <caption>Matrix B</caption>
                        <%for (int i = 0; i < 3; i++)
                        {%>
                            <tr>
                                <% for (int j = 0; j < 3; j++)
                                {%>
                                    <td style="padding:0px;border:0px">
                                        <%=Html.TextBox("MatrixB" + i, null, new {style="width:40px",maxlength="4" })%>
                                    </td>
                                <%}%>
                            </tr>
                        <%} %>
                    </table>
                </td>
                <td>
                    <table cellpadding="0px" cellspacing="0px" style="border:0px">
                        <caption>Comments</caption>
                        <%for (int i = 0; i < 3; i++)
                        {%>
                            <tr>
                                <% for (int j = 0; j < 3; j++)
                                {%>
                                    <td style="padding:0px;border:0px">
                                        <%=Html.TextBox("Comments" + i, null, new {style="width:100px" })%>
                                    </td>
                                <%}%>
                            </tr>
                        <%} %>
                    </table>
                </td>
            </tr>
            <tr>
                <td align="center" colspan="4">
                    <input type="submit" />
                </td>
            </tr>
        </table>
    <%} %>
</asp:Content>

          Next create a MatrixResult partial view and add the following lines,


<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TwoDimensionalModelBinder.Models.MatrixResult>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Matrix Result
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">    
    <table>
            <tr>
                <td>
                    <table>
                        <caption>Matrix A</caption>
                        <%for (int i = 0; i < 3; i++)
                        {%>
                            <tr>
                                <% for (int j = 0; j < 3; j++)
                                {%>
                                    <td>
                                        <%=Model.MatrixA[i,j] %>
                                    </td>
                                <%}%>
                            </tr>
                        <%} %>
                    </table>
                </td>
                <td style="font-size:20px;font-weight:bold;">
                    X
                </td>
                <td>
                    <table>
                        <caption>Matrix B</caption>
                        <%for (int i = 0; i < 3; i++)
                        {%>
                            <tr>
                                <% for (int j = 0; j < 3; j++)
                                {%>
                                    <td>
                                        <%=Model.MatrixB[i,j] %>
                                    </td>
                                <%}%>
                            </tr>
                        <%} %>
                    </table>
                </td>
                <td style="font-size:20px;font-weight:bold;">
                    =
                </td>
                <td>
                    <table>
                        <caption>Result</caption>
                        <%for (int i = 0; i < 3; i++)
                        {%>
                            <tr>
                                <% for (int j = 0; j < 3; j++)
                                {%>
                                    <td>
                                        <%=Model.Reslutant[i,j] %>
                                    </td>
                                <%}%>
                            </tr>
                        <%} %>
                    </table>
                </td>
                <td>
                    <table>
                        <caption>Comments</caption>
                        <%for (int i = 0; i < 3; i++)
                        {%>
                            <tr>
                                <% for (int j = 0; j < 3; j++)
                                {%>
                                    <td>
                                        <%=Model.Comments[i,j] %>
                                    </td>
                                <%}%>
                            </tr>
                        <%} %>
                    </table>
                </td>
            </tr>
            <tr>
                <td align="center" colspan="6">
                    <%=Html.ActionLink("Go To Home Page","Index") %>
                </td>
            </tr>
        </table>
</asp:Content>

 

          I am taking the convention over configuration approach. If MatrixA is a two dimensional array then MatrixA0, MatrixA0, MatrixA0,....., denotes the first Row of MatrixA  and MatrixA1, MatrixA1, MatrixA1,....., denotes the second Row of MatrixA and so on. If you have following names in your view, MatrixA0, MatrixA0, MatrixA1, MatrixA1, then you can say that you have a two dimensional array with two rows and two columns.

 

          TwoDimensionalArrayBinder simply find the total number of rows and columns first, then create a two dimensional array of this size and then fill this new array with RawValue.

 

          Index view display the 3 matrices. Two matrices are used for matrix multiplication and one is used to display comments. HomeController's Post Index action will receive these values as two dimensional arrays after user submit the form values and TwoDimensionalArrayBinder binds these submitted values as two dimensional arrays. Inside this action, I am using the model logic(multiplication of two matrix) inside controller only for demonstration. Finally MatrixResult view with MatrixResult model is returned. MatrixResult view simply display the result of these matrix multiplication(see the above diagram).

 

    Summary:

          In this article I showed how to create a custom model binder for two dimensional array. I also showed an example of using this new custom model binder in your action method. This article also aims to help you to create custom model binder in ASP.NET MVC. I hope this will help you to create your own custom model binder and hope you will enjoy this article too.


6 Comments

  • thanks imran for writing such a wonderful article putting some light on model binding for 2D arrays. i had a lot of trouble making a master in my order form. the binding of order (master) is trivial, however, order detail is a 2D array of elements or more precisely it is 1D array of order detail objects. i still can't find a way to accomplish this on single page. i wonder if u could write something abt it
    thanks once again for ur contribution

  • @tassadaque
    Is you are looking for 1D Data Binding then Built In Model Binding work for you

  • @imran thanks for quickie, but what i want is bit different. My order form will make a single object but it has a property orderdetail that is of type IList. i want to fill order object along with List of objects of orderdetail in order.orderdetail where orderdetail is IList.

  • @tassadaque
    I think Default Model Binder will help you to do this,
    see
    http://weblogs.asp.net/nmarun/archive/2010/03/13/asp-net-mvc-2-model-binding-for-a-collection.aspx

  • thanks imran, actually i have often come across these kind of solutions but the thing is when u r accepting multiple instances of one object on UI u give remove functionality as well. removing a row from middle will result in broken indices and u immediately have to fall to a workaround using javascript or some custom helper written by xyz on the net. furthermore, the link provides example of edit when u pass products and subtotals wrapped in basket to ur View. but when creating new instances what is the option. i would prefer something like a single row of product object with add hyperlink to add additional rows and delete hyperlink to remove a particular row. what i m interested in is a clean solution for this scenario without workaruonds.
    regards

  • @tassadaque,
    I understand what you want, But it need some time to implement this scenario. I will see when I become free.

Comments have been disabled for this content.