TextBoxFor displaying initial value, not the value updated from code
Your problem is (step by step)
- Your
SomeInformation()
method sets the value oftest1.Latitude
to "LATITUDE2". - You then pass that model to your
Index()
method using the overload
ofRedirectToAction
that accepts an object. Internally this uses
reflection to build aRouteValueDictionary
based on the properties
of your model (in this case its simplylatitude="LATITUDE2"
). - When you hit the
Index
method the model is bound by theDefaultModelBinder
and now the value ofDataSiteList.Latitude
is "LATITUDE2" (which is why you enter
theif
block) - In the process of binding, the
DefaultModelBinder
sets theModelState
value ofLatitude
to "LATITUDE2". Any attempts to set
the value ofLatitude
are now ignored because the view usesModelState
value to render the control.
It not clear what your trying to do here. You can make it work as you expect by adding ModelState.Clear();
as the first line of your Index()
method. This clears all existing ModelState
values an you can now set the value to "LATITUDE".
But your if
block makes no sense. Perhaps you were just doing some kind of test, but you may as well remove the parameter from the Index()
method and just initialize a new instance of DataSites
in the method.
Edit
To give a bit more information as to why updating a model property has no affect once ModelState
has been set.
Imagine you have a form to collect user information where the model contains int Age
. The user is asked to enter their age and someone enters "I'm five next week!". Of course this wont bind to an int so the DefaultModelBinder
adds the value (the attemptedValue
) and adds a ModelStateError
.
When the view is returned it will typically display an error message such as "The field Age must be a number". If the html helper rendering the control used the model value, then it would display "0" (the default value for int). It would be somewhat confusing for the user to see "0" in the textbox and next it a message saying it must be a number (What! but zero is a number and what the heck happened to what I entered?). So instead, the helper uses the value from ModelState
and now the users sees "I'm five next week!" and an associated error message that makes sense for the value.
So even though you thoughts were that "its not logical", there is actually some logic to this behavior.
MVC2 TextBoxFor value not updating after submit?
Default Html helper try to redisplay the data that is posted to them.
They first use the value from posted data and if no posted data is available they take the data from the Model.
This is not what you want obviously, but still the most common usage: You display some data in formfields after receiving a get request. You post to an Update action. If you have errors you want to redisplay the form with the values you entered still available.
I have seen some people getting around this (I think by writing to ModelState), but my choice was always to not use the default helpers if they dont help me. Thats especially true for hidden fields: Most people get confused when they set a value to a hidden field but the value that is realy used is from the post. At least there is a question every other day about it on SO :-)
Forget the "Most people" and replace it with "Everybody".
ASP.NET MVC: Hidden field value does not get rendered using HtmlHelper.Hidden
http://blog.johnwest.com/post/ASPNET-MVC-Hidden-Form-Field-Bug.aspx
http://blogs.msdn.com/b/simonince/archive/2010/05/05/asp-net-mvc-s-html-helpers-render-the-wrong-value.aspx?utm_medium=Twitter&utm_source=Shared
UPDATE
Oh I found another one from today (You are not alone):
How to update textbox value
How to set a default value with Html.TextBoxFor?
It turns out that if you don't specify the Model to the View method within your controller, it doesn't create a object for you with the default values.
[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Create()
{
// Loads default values
Instructor i = new Instructor();
return View("Create", i);
}
[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Create()
{
// Does not load default values from instructor
return View("Create");
}
TextBoxFor value not updating after post
Use ModelState.Clear()
before setting value
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(Repair r)
{
ModelState.Clear(); //Added here
r.Number = "New Value";
return View(r);
}
HTML.TextBoxFor is set readonly before value set
When you post back a model, its values are added to ModelState
. Html helpers bind to the vales in ModelState
, not the values of the model properties, so modifying the value of a model property in the POST method will not be reflected in the view unless you first clear model state before setting the value using
ModelState.Clear(); // clears all properties
or
if (ModelState.ContainsKey("active"))
{
ModelState["active"].Errors.Clear(); //clears the property 'active'
}
The reason for this behavior is explained in the second part of this answer.
However, clearing ModelState
should be used with caution as it also clears validation errors, and in any case the correct approach is to follow the PRG pattern
Default value for TextBoxFor in ASP.NET MVC
Use TextBox instead of TextBoxFor
@Html.TextBox("WEIGHT", Model.WEIGHT ?? "0", new {...})
or if WEIGHT is an empty string
@Html.TextBox("WEIGHT", Model.WEIGHT == "" ? "0" : Model.WEIGHT, new {...})
View displays different values, then it has in its model
The issue is that when you submit your collection, the posted values are added to ModelState
. If we look at just the Name
property, the following values are added
Columns[0].Name // value = "a"
Columns[1].Name // value = "b"
Columns[2].Name // value = "c"
Now you insert a new column (say before the first item). The values in the model are now
Columns[0].Name // value = "ADDED"
Columns[1].Name // value = "a"
Columns[2].Name // value = "b"
Columns[3].Name // value = "c"
Now when you return the view, you (correcty) use the HtmlHelper
methods to generate the view, but all the HtmlHelper
methods that generate form controls render the value
attribute by checking the following (in order)
- the value in
ModelState
(if any) - the value in the
ViewDataDictionary
(if any) - the value from the model
The reason for this behavior is explained in TextBoxFor displaying initial value, not the value updated from code.
In the first iteration of the for
loop, your @Html.TextBoxFor(m => m.Columns[i].Name)
sees a value of "a" in ModelState
and renders "a" in the textbox (even though the value of the model is "ADDED").
In the 2nd iteration, the value is again taken from ModelState
, so "b" is rendered.
In the 3rd iteration, the value is again taken from ModelState
, so "c" is rendered.
And in the 4th and final iteration, a value is not found in ModelState
(or ViewData
) for Columns[3].Name
, so it reads the value from the model, so "c" is rendered.
As a side note, if you added @Model.Columns[i].Name
in a table cell, you would see the values rendered in accordance with the property value i.e. Row 1: "ADDED", Row 2: "a", Row 3: "b", Row4: "c".
To solve this, you can add ModelState.Clear()
before your return the view. However, the side effect of this is that it also clears any validation errors, so at the least, you should immediately return the view if ModelState
is not valid
public ViewResult Index(CreateTableModels model, ...)
{
if (!ModelState.IsValid)
{
return View(model);
}
... // code to insert new row
ModelState.Clear();
return View(model);
}
However, the best approach is to follow the PRG pattern and redirect to a method that generates the new data.
Related Topics
How to Convert HTML to Plain Text
How to Deserialize Xml Document
How to Add User-Supplied Input to an SQL Statement
How to Get the Application'S Path in a .Net Console Application
How to Match an Entire String With a Regex
Listing All Permutations of a String/Integer
No Connection Could Be Made Because the Target Machine Actively Refused It
How to Prevent the App from Terminating When I Close the Startup Form
Unity Scripts Edited in Visual Studio Don't Provide Autocomplete
Encrypt and Decrypt a String in C#
Mvvm: Tutorial from Start to Finish
How To: Execute Command Line in C#, Get Std Out Results
How to Copy the Contents of One Stream to Another