3

I have a ViewModel called AssetSearchModel with a property of type: List.

When I post from the ViewModel, all the properties of my List get binded back to the model successfully, except for this one: public DomainsDto DomainControl { get; set; } which is defined at the bottom.

That domaincontrol has a couple of properties and then its own nested list... none of its properties get set, they are all null

public class AssetSearchModel
{
    public List<SearchControlModel> SearchControls { get; set; }

    public AssetSearchModel()
    {
    }

}

public class SearchControlModel
{

    /// <summary>
    /// Asset identifier.
    /// </summary>
    [DisplayName("AssetId ID")]
    public int SearchOptionID { get; set; } // this gets bound ok

    [DisplayName("Asset Customer ID")]
    public int AssetCustomerID { get; set; } // this gets bound ok

    [DisplayName("Portal ID")]
    public int PortalID { get; set; } // this gets bound ok

    [DisplayName("Calling Page")]
    public string CallingPage { get; set; } // this gets bound ok

    [DisplayName("Display Control")]
    public string DisplayControl { get; set; } // this gets bound ok

    [DisplayName("Display Text")]
    public string DisplayText { get; set; } // this gets bound ok

    public string ControlValue { get; set; } // this gets bound ok

    [DisplayName("Search Table")]
    public string SearchTable { get; set; } // this gets bound ok

    [DisplayName("Search Column")]
    public string SearchColumn { get; set; } // this gets bound ok

    [DisplayName("Source Table")]
    public string SourceTable { get; set; } // this gets bound ok

    [DisplayName("Source Display Column")]
    public string SourceDisplayColumn { get; set; } // this gets bound ok

    [DisplayName("Source Value Column")]
    public string SourceValueColumn { get; set; } // this gets bound ok

    [DisplayName("SourceCustomFilter")]
    public string SourceCustomFilter { get; set; } // this gets bound ok

    [DisplayName("SortOrder")]
    public int SortOrder { get; set; } // this gets bound ok

    [DisplayName("DomainControl")]
    public DomainsDto DomainControl { get; set; } // THIS DOES NOT BIND

    public SearchControlModel()
    {



    }

}

public class DomainsDto
{
    public int DomainMasterId;      // this does not bind
    public string DisplayName;     // this does not bind
    public List<DomainDto> Domains;  // this does not bind
    public DomainsDto()
    {

    }

}

[DataContract(Name = "Domain", Namespace = "http://www.comcom.com/")]

public class DomainDto
{
    [DataMember]
    public int DomainID { get; set; }   // this does not bind

    [DataMember]
    public string DomainName { get; set; }  // this does not bind

    [DataMember]
    public bool Checked { get; set; }   // this does not bind


    public DomainDto()
    {

    }
}

and my view:

         @for (int y = 0; y < Model.SearchControls.Count; y++)
     {

        @Html.Label("Search option: " + Model.SearchControls[y].DisplayText)
        <br />
        @Html.HiddenFor(x => x.SearchControls[y].DisplayText)
        @Html.HiddenFor(x => x.SearchControls[y].CallingPage)
        @Html.HiddenFor(x => x.SearchControls[y].DisplayControl)
        @Html.HiddenFor(x => x.SearchControls[y].SourceCustomFilter)
        @Html.HiddenFor(x => x.SearchControls[y].SearchTable)
        @Html.HiddenFor(x => x.SearchControls[y].SearchColumn)
        @Html.HiddenFor(x => x.SearchControls[y].SourceValueColumn)


         if (Model.SearchControls[y].DomainControl != null)
         {

             @Html.HiddenFor(x => x.SearchControls[y].DomainControl.DisplayName) 

             @Html.HiddenFor(x => x.SearchControls[y].DomainControl.DomainMasterId)

             if (Model.SearchControls[y].DomainControl.Domains != null)
             {
                 for (int z = 0; z < Model.SearchControls[y].DomainControl.Domains.Count; z++)
                 {

                    @Html.HiddenFor(x => x.SearchControls[y].DomainControl.Domains[z].DomainID)
                    @Html.HiddenFor(x => x.SearchControls[y].DomainControl.Domains[z].DomainName)
                    @Html.HiddenFor(x => x.SearchControls[y].DomainControl.Domains[z].Checked)


                    <br />
                 }
             }
         }

         <br />
         <br />
         <hr width=100px />


     }

the html (sorry for the single line, it wouldn't let me paste it in differently)

<label for="Search_option:_Product_Lines">Search option: Product Lines</label> <input id="SearchControls_1__DisplayText" name="SearchControls[1].DisplayText" type="hidden" value="Product Lines" /> <input id="SearchControls_1__CallingPage" name="SearchControls[1].CallingPage" type="hidden" value="AssetSearch" /> <input id="SearchControls_1__DisplayControl" name="SearchControls[1].DisplayControl" type="hidden" value="M" /> <input id="SearchControls_1__SourceCustomFilter" name="SearchControls[1].SourceCustomFilter" type="hidden" value="26" /> <input id="SearchControls_1__SearchTable" name="SearchControls[1].SearchTable" type="hidden" value="AssetDomainValue" /> <input id="SearchControls_1__SearchColumn" name="SearchControls[1].SearchColumn" type="hidden" value="Brand" /> <input id="SearchControls_1__SourceValueColumn" name="SearchControls[1].SourceValueColumn" type="hidden" value="DomainCodeValueID" />   <input id="SearchControls_1__DomainControl_DisplayName" name="SearchControls[1].DomainControl.DisplayName" type="hidden" value="Product Line" /> <input id="SearchControls_1__DomainControl_DomainMasterId" name="SearchControls[1].DomainControl.DomainMasterId" type="hidden" value="26" />  <input id="SearchControls_1__DomainControl_Domains_0__DomainID" name="SearchControls[1].DomainControl.Domains[0].DomainID" type="hidden" value="43680" /> <input id="SearchControls_1__DomainControl_Domains_0__DomainName" name="SearchControls[1].DomainControl.Domains[0].DomainName" type="hidden" value="YORK" /> <input id="SearchControls_1__DomainControl_Domains_0__Checked" name="SearchControls[1].DomainControl.Domains[0].Checked" type="hidden" value="False" />                        <input id="SearchControls_1__DomainControl_Domains_1__DomainID" name="SearchControls[1].DomainControl.Domains[1].DomainID" type="hidden" value="43683" /> <input id="SearchControls_1__DomainControl_Domains_1__DomainName" name="SearchControls[1].DomainControl.Domains[1].DomainName" type="hidden" value="Johnson Controls" /> <input id="SearchControls_1__DomainControl_Domains_1__Checked" name="SearchControls[1].DomainControl.Domains[1].Checked" type="hidden" value="False" />                        <br />
2
  • Could you put your controller code? Commented Jul 20, 2012 at 19:17
  • Can you post your application_start? Commented Jul 20, 2012 at 19:19

1 Answer 1

11

Ah no, the model binder works only with properties only, not fields.

So replace:

public class DomainsDto
{
    public int DomainMasterId;       // this does not bind
    public string DisplayName;       // this does not bind
    public List<DomainDto> Domains;  // this does not bind
    public DomainsDto()
    {

    }
}

with:

public class DomainsDto
{
    public int DomainMasterId { get; set; }       // this does bind
    public string DisplayName  { get; set; }      // this does bind
    public List<DomainDto> Domains { get; set; }  // this does bind
    public DomainsDto()
    {

    }
}

But really, what's the whole point of stuffing a gazzilion of hidden fields in a form? You already have this stuff in your db or something. So simply put a hidden field containing an id that will allow you to go and fetch the data wherever this data resides. I've always wondered people why are people so desperately trying to reinvent the ViewState for MVC. It's just not the way this pattern is meant to be used. You are no longer doing classic WebForms.

Here's a rule that should drive you when designing an ASP.NET MVC view:

  • whatever fields the user is supposed to modify use corresponding input fields (textboxes, radios, ddls and stuff)
  • whatever fields the user is not supposed to modify, it shouldn't appear in the form. You should have at maximum one hidden field per form containing at maximum an unique identifier allowing you to retrieve the information you need

Conclusion: use view models.

So the real solution to your problem is to replace:

[DisplayName("DomainControl")]
public DomainsDto DomainControl { get; set; } // THIS DOES NOT BIND

with:

public int DomainControlId { get; set; }

and using a single hidden field for this id in the view. That's it.

Sign up to request clarification or add additional context in comments.

1 Comment

that was exactly it... I'm new to MVC, so I'm just trying to fix an application that already exists. The funny thing is, I spent a week trying to figure this out, reading EVERYTHING on stackoverflow. 5 minutes after posting it, I found this: stackoverflow.com/questions/3244178/… the guy said exactly what you said... thanks again!!!!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.