Passing Data from Controller to Webform Page - asp.net-mvc

I have a asp.net mvc application. One of it's controller calls an aspx page instead of normal razor view page. I followed this https://www.hanselman.com/blog/MixingRazorViewsAndWebFormsMasterPagesWithASPNETMVC3.aspx
Now the problem is I need to send some data from my controller to that aspx page which I can't do using Viewbag.
Any idea how can I send data from my regular controller to my aspx page?

Passing data using model to view(aspx engine) from controller:
Model:
public class Product
{
public string ProductID { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public int Price {get; set;}
}
Controller:
public ActionResult Index()
{
List<Product> productLst = new List<Product>{
new Product{ProductID="P01",ProductName="Pen",Quantity=10,Price=12},
new Product{ProductID="P02",ProductName="Copy",Quantity=12,Price=20},
new Product{ProductID="P03",ProductName="Pencil",Quantity=15,Price=22},
new Product{ProductID="P04",ProductName="Eraser",Quantity=20,Price=27}
ViewData["Message"] = "Your message comes here";
return View();
}
ASPX View:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Product>" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Index</title>
</head>
<body>
<div>
<h3>Passing Data From Controller To View using ViewData</h3>
<h3><%= Html.Encode(ViewData["Message"]) %></h3>
<%foreach (var item in Model)
{ %>
<p><%=item.ProductID %></p>
<p><%=item.ProductName %></p>
<p><%=item.Quantity %></p>
<p><%=item.Price %></p>
<%} %>
</div>
</body>
</html>
References for model binding in aspx engine,
https://www.codeproject.com/Articles/391289/Implementing-ASP-NET-MVC-Views-in-three-different
https://weblogs.asp.net/gunnarpeipman/asp-net-mvc-3-using-multiple-view-engines-in-same-project
Assign dynamic HTML attribute for any HTML tags:
<input checked="#isRazor" type="checkbox"><!-- Razor engine -->
<input checked="<%:isASPX%>" type="checkbox"><!-- ASPX engine -->
You can do more mixing of HTML tags with Razor and ASPX View Engine, Following code block shows how you can do that.
Your Sample Html Code or Text #RazorCode (#AnotherRazorCode)
Your Sample Html Code or Text <%: ASPXCode %> (<%:AnotherASPXCode %>)

Instead of that, You can go with creating your own Custom Model to passing the data from Controller to View.
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<YourModel>" %>
<% foreach(var item in Model) { %>
<tr>
<td><%: item.Name %></td>
</tr>

Have you tried passing it through ViewData, like so?
in the action: ViewData["myvar"] = "realvalue";
in the view: string parl = ViewData["myvar"];
NOTE: Alternatively you can use Session like this example:
in MVC action:
Session["UserName"] = "Test";
in WebForms:
string UserName = (string)Session["UserName"];
That is all!

Related

ASP.net MVC2 Passing data between strongly typed views

I have the following MVC2 view that is strongly typed with a viewmodel, the viewmodel contains a list of values from one db table, I need to display a single value from a second table in the view, this is my view code
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<CustomerDatabase.WebUI.Models.CustomerSitesListViewModel> " %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Customer Sites
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% foreach (var customerSite in Model.CustomerSites) { %>
<% Html.RenderPartial("CustomerSiteSummary", customerSite); %>
<%} %>
</asp:Content>
This is the viewmodel, notice i am including a Customer member in the viewmodel as i want to display the customer name in addition to the list of customer sites
namespace CustomerDatabase.WebUI.Models
{
public class CustomerSitesListViewModel
{
public IList<CustomerSite> CustomerSites { get; set; }
public PagingInfo PagingInfo { get; set; }
public Customer customer { get; set; }
}
}
This is my controller code for the view
public ViewResult List([DefaultValue(1)] int page)
{
var customerSitesToShow = customerSiteRepository.CustomerSites;
var viewModel = new CustomerSitesListViewModel
{
CustomerSites = customerSitesToShow.Skip((page - 1) * PageSize).Take(PageSize).ToList(),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = customerSitesToShow.Count()
}
};
return View(viewModel); //Passed to view as ViewData.Model (or simply model)
}
This is my partial view that renders the list,
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CustomerDatabase.Domain.Entities.CustomerSite>" %>
<div class="item">
<div class="customer-list-item">
<h2><%:Model.customer.CustomerName%></h2>
<%: Model.AddressLine1 %>
<%: Model.AddressLine2%>
Although intellisense lets me access the customer object from the view with
<h2><%:Model.customer.CustomerName%></h2>
An error is thrown when i navigate to the view,
Object reference not set to an instance of an object.
Source Error:
Line 7: <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
Line 8: <% foreach (var customerSite in Model.CustomerSites) { %>
Line 9: <%:Model.customer.CustomerName%>
Line 10: <% Html.RenderPartial("CustomerSiteSummary", customerSite); %>
Line 11: <%}
I think the error is due to the view rendering a list, i tried changing the viewmodel member to
public IList<Customer> {get; set;}
but this doesn't work either.
Can anyone suggest a way i can achieve this or offer any advice on where i am going wrong this is one problem i haven't been able to resolve after hours or researching on the Internet?
It looks like one of the model properties are not initialized. If you add a breakpoint on that line and check the variables I'm pretty sure you'll find 1 that is null.

ASP.Net MVC2 Custom Templates Loading via Ajax and Model Updating

I have a view model with a collection of other objects in it.
public ParentViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<ChildViewModel> Child { get; set; }
}
public ChildViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
}
In one of my views I pass in a ParentViewModel as the model, and then use
<%: Html.EditorFor(x => x) %>
Which display a form for the Id and Name properties.
When the user clicks a button I call an action via Ajax to load in a partial view which takes a collection of Child:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Child>>" %>
<%: Html.EditorFor(x => x) %>
which then uses the custom template Child to display a form for each Child passed in.
The problem I'm having is that the form created by the Child custom template does not use the naming conventions used by the DefaultModelBinder.
ie the field name is (when loaded by Ajax):
[0].FirstName
instead of:
Child[0].FirstName
So the Edit action in my controller:
[HttpPost]
public virtual ActionResult Edit(int id, FormCollection formValues)
{
ParentViewModel parent = new ParentViewModel();
UpdateModel(parent);
return View(parent);
}
to recreate a ParentViewModel from the submitted form does not work.
I'm wondering what the best way to accomplish loading in Custom Templates via Ajax and then being able to use UpdateModel is.
Couple of things to start with is that you need to remember the default ModelBinder is recursive and it will try and work out what it needs to do ... so quite clever. The other thing to remember is you don't need to use the html helpers, actual html works fine as well :-)
So, first with the Model, nothing different here ..
public class ParentViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<ChildViewModel> Child { get; set; }
}
public class ChildViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
}
Parent partial view - this takes an instance of the ParentViewModel
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ParentViewModel>" %>
<h2>Parent</h2>
<%: Html.TextBox("parent.Name", Model.Name) %>
<%: Html.Hidden("parent.Id", Model.Id) %>
<% foreach (ChildViewModel childViewModel in Model.Child)
{
Html.RenderPartial("Child", childViewModel);
}
%>
Child partial view - this takes a single instance of the ChildViewModel
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ChildViewModel>" %>
<h3>Child</h3>
<%: Html.Hidden("parent.Child.index", Model.Id) %>
<%: Html.Hidden(string.Format("parent.Child[{0}].Id", Model.Id), Model.Id)%>
<%: Html.TextBox(string.Format("parent.Child[{0}].FirstName", Model.Id), Model.FirstName) %>
Something to note at this point is that the index value is what is used for working out the unique record in the list. This does not need to be incremental value.
So, how do you call this? Well in the Index action which is going to display the data it needs to be passed in. I have setup some demo data and returned it in the ViewData dictionary to the index view.
So controller action ...
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewData["Parent"] = GetData();
return View();
}
private ParentViewModel GetData()
{
var result = new ParentViewModel
{
Id = 1,
Name = "Parent name",
Child = new List<ChildViewModel>
{
new ChildViewModel {Id = 2, FirstName = "first child"},
new ChildViewModel {Id = 3, FirstName = "second child"}
}
};
return result;
}
In the real world you would call a data service etc.
And finally the contents of the Index view:
<form action="<%: Url.Action("Edit") %>" method="post">
<% if (ViewData["Parent"] != null) { %>
<%
Html.RenderPartial("Parent", ViewData["Parent"]); %>
<% } %>
<input type="submit" />
</form>
Saving
So now we have the data displayed how do we get it back into an action? Well this is something which the default model binder will do for you on simple data types in relatively complex formations. So you can setup the basic format of the action which you want to post to as:
[HttpPost]
public ActionResult Edit(ParentViewModel parent)
{
}
This will give you the updated details with the original ids (from the hidden fields) so you can update/edit as required.
New children through Ajax
You mentioned in your question loading in custom templates via ajax, do you mean how to give the user an option of adding in another child without postback?
If so, you do something like this ...
Add action - Need an action which will return a new ChildViewModel
[HttpPost]
public ActionResult Add()
{
var result = new ChildViewModel();
result.Id = 4;
result.FirstName = "** to update **";
return View("Child", result);
}
I've given it an id for easy of demo purposes.
You then need a way of calling the code, so the only view you need to update is the main Index view. This will include the javascript to get the action result, the link to call the code and a target HTML tag for the html to be appended to. Also don't forget to add your reference to jQuery in the master page or at the top of the view.
Index view - updated!
<script type="text/javascript">
function add() {
$.ajax(
{
type: "POST",
url: "<%: Url.Action("Add", "Home") %>",
success: function(result) {
$('#newchild').after(result);
},
error: function(req, status, error) {
}
});
}
</script>
<form action="<%: Url.Action("Edit") %>" method="post">
<% if (ViewData["Parent"] != null) { %>
<%
Html.RenderPartial("Parent", ViewData["Parent"]); %>
<% } %>
<div id="newchild"></div>
<br /><br />
<input type="submit" /> add child
</form>
This will call the add action, and append the response when it returns to the newChild div above the submit button.
I hope the long post is useful.
Enjoy :-)
Hmm... i personally would recommend to use a JSON result, instead of a HTML result, that you fiddle in the page...
makes the system cleaner. and your postback working ;-)
I found another way to accomplish this which works in my particular situation.
Instead of loading in a partial via via Ajax that is strongly typed to a child collection like:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Child>>" %>
I created a strongly typed view to the parent type and then called EditorFor on the list like so:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Parent>" %>
<%: Html.EditorFor(x => x.ChildList) %>
This then calls a Custom Display Template and the result is that all the HTML elements get named correctly and the Default Model binder can put everything back together.

Asp.net mvc, view with multiple updatable parts - how?

I have started doing asp.net mvc programming and like it more everyday.
Most of the examples I have seen use separate views for viewing and editing details of a specific entity.
E.g. - table of music albums linking to separate 'detail' and 'update' views
[Action] | Title | Artist
detail, update | Uuuh Baby | Barry White
detail, update | Mr Mojo | Barry White
With mvc how can I achieve a design where the R and the U (CRUD) are represented in a single view, and furthermore where the user can edit separate parts of the view, thus limiting the amount of data the user can edit before saving?
Example mockup - editing album detials:
I have achieved such a design with ajax calls, but Im curious how to do this without ajax.
Parts of my own take on this can be seen below. I use a flag (enum EditCode)
indicating which part of the view, if any, that has to render a form. Is such a design in accordance with the framework, could it be done more elegantly?
AlbumController
public class AlbumController : Controller
{
public ActionResult Index()
{
var albumDetails = from ManageVM in state.AlbumState.ToList()
select ManageVM.Value.Detail;
return View(albumDetails);
}
public ActionResult Manage(int albumId, EditCode editCode)
{
(state.AlbumState[albumId] as ManageVM).EditCode = (EditCode)editCode;
ViewData["albumId"] = albumId;
return View(state.AlbumState[albumId]);
}
[HttpGet]
public ActionResult Edit(int albumId, EditCode editCode)
{
return RedirectToAction("Manage", new { albumId = albumId, editCode = editCode });
}
// edit album details
[HttpPost]
public ActionResult EditDetail(int albumId, Detail details)
{
(state.AlbumState[albumId] as ManageVM).Detail = details;
return RedirectToAction("Manage", new { albumId = albumId, editCode = EditCode.NoEdit });// zero being standard
}
// edit album thought
[HttpPost]
public ActionResult EditThoughts(int albumId, List<Thought> thoughts)
{
(state.AlbumState[albumId] as ManageVM).Thoughts = thoughts;
return RedirectToAction("Manage", new { albumId = albumId, editCode = EditCode.NoEdit });// zero being standard
}
Flag - EditCode
public enum EditCode
{
NoEdit,
Details,
Genres,
Thoughts
}
Mangae view
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Controllers.ManageVM>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Manage
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Manage</h2>
<% if(Model.EditCode == MvcApplication1.Controllers.EditCode.Details)
{%>
<% Html.RenderPartial("_EditDetails", Model.Detail); %>
<% }else{%>
<% Html.RenderPartial("_ShowDetails", Model.Detail); %>
<% } %>
<hr />
<% if(Model.EditCode == MvcApplication1.Controllers.EditCode.Thoughts)
{%>
<% Html.RenderPartial("_EditThoughts", Model.Thoughts); %>
<% }else{%>
<% Html.RenderPartial("_ShowThoughts", Model.Thoughts); %>
<% } %>
The last part feels messy to me. I would recommend wrapping those with Html Helpers to clean up your view.
<h2>Manage</h2>
<% Html.RenderDetailsPartial(Model.EditCode) %>
<hr />
<% Html.RenderThoughtsPartial(Model.EditCode) %>
Let the HTMLHelper determine which view to use based on the EditCode.

ASP.NET MVC 2 client-side validation rules not being created

MVC isn't generating the client-side validation rules for my viewmodel. The HTML just contains this:
<script type="text/javascript">
//<![CDATA[
if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; }
window.mvcClientValidationMetadata.push({"Fields":[],"FormId":"form0","ReplaceValidationSummary":false});
//]]>
</script>
Note that Fields[] is empty!
My view is strongly-typed and uses the new strongly-typed HTML helpers (TextBoxFor(), etc).
View Model / Domain Model
public class ItemFormViewModel
{
public Item Item { get; set; }
[Required] [StringLength(100)] public string Whatever { get; set; } // for demo
}
[MetadataType(typeof(ItemMetadata))]
public class Item
{
public string Name { get; set; }
public string SKU { get; set; }
public int QuantityRequired { get; set; }
// etc.
}
public class ItemMetadata
{
[Required] [StringLength(100)] public string Name { get; set; }
[Required] [StringLength(50)] public string SKU { get; set; }
[Range(0, Int32.MaxValue)] public int QuantityRequired { get; set; }
// etc.
}
(I know I'm using a domain model as my / as part of my view model, which isn't a good practice, but disregard that for now.)
View
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<ItemFormViewModel>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Editing item: <%= Html.Encode(Model.Item.Name) %></h2>
<% Html.EnableClientValidation(); %>
<%= Html.ValidationSummary("Could not save the item.") %>
<% using (Html.BeginForm()) { %>
<%= Html.TextBoxFor(model => model.Item.Name) %>
<%= Html.TextBoxFor(model => model.Item.SKU) %>
<%= Html.TextBoxFor(model => model.Item.QuantityRequired) %>
<%= Html.HiddenFor(model => model.Item.ItemID) %>
<%= Html.TextBox("Whatever", Model.Whatever) %>
<input type="submit" value="Save" />
<% } %>
</asp:Content>
I included the Whatever property on the view model because I suspected that MVC wasn't recursively inspecting the sub-properties of ItemFormViewModel.Item, but even that isn't being validated? I've even tried delving into the MVC framework source code but have come up empty. What could be going on?
About five seconds after I posted the question, I realized something: My view didn't have ValidationMessage placeholders anywhere. I added <%= Html.ValidationMessageFor(model => model.Item.Name) %> and lo and behold, MVC added validation rules for Item.Name to the JS block at the bottom of the page.
It turns out that MVC does not emit client-side validation rules for a field unless you actually do one of the following:
Call Html.ValidationMessage() for the property.
Call Html.Validate() for the property. (This one won't output error messages)
Render the controls using Html.EditorForModel(). (source)
Doing any of these tells MVC, "This property of my viewmodel is editable by the user, so you should be validating it." Just using the HTML helper to stick a textbox on the page -- even if you're using the new strongly-typed helpers -- isn't enough.
I have had no luck getting this to work in MVC 2 RC. According to other questions here on SO, you have to get the MicrosoftMvcJQueryValidation.js file from the MVC Futures release, hold your left foot behind your head, and whistle Dixie for half an hour. I did this and more and have not been able to make it work.
Hopefully it will be fixed in RTM.

Pass SelectList "SelectedValue" to Controller Action Method

I have a registration form which displays a users Name (textbox), Email (textbox) and Division (SelectList). The Name and Email are pre-populated (I'm using Windows Authentication, Intranet app), and I want to send the SelectedValue from the DropDown to my controller as an Int32, I don't want to send the entire SelectList back. This list is small now, but will grow to considerable size.
I a class called RegistrationViewModel, it contains public properties for these fields. However, when I use SelectList for the DivisionList, I receive this error: No parameterless constructor defined for this object..
If i change the Type, it works no problem, but Division is null or 0. Is there a way to pass the SelectedValue from a DropDown to a Controller Action method as a Int32?
Edit 1:
I'm not really sure what I'm doing, I've been using MVC for about 48 hours, watched the PDF, TechEd, and TechDays videos.
My apologies, here is my controller code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(RegistrationViewModel rvm)
{
IApplicationContext context = ContextRegistry.GetContext();
IValidationErrors errors = new ValidationErrors();
IValidator validator = (IValidator)context.GetObject("RegistrationValidator");
bool valid = validator.Validate(rvm, errors);
if (valid)
repo.SaveRegistration();
else
ViewData["DivisionList"] = repo.GetDivisions();
return View(rvm);
}
RegistrationViewModel Class
public class RegistrationViewModel
{
public string Name { get; set; }
public string Email { get; set; }
//public SelectList DivisionList { get; private set; }
public int Division { get; set; }
}
Here's the view
<%# Page Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<RegistrationViewModel>" %>
<%# Import Namespace="Project1.Entities"%>
<%# Import Namespace="Project1.Models"%>
<asp:Content ID="registerTitle" ContentPlaceHolderID="TitleContent" runat="server">
Register
</asp:Content>
<asp:Content ID="registerContent" ContentPlaceHolderID="MainContent" runat="server">
...
<% using (Html.BeginForm())
{ %>
<div>
<fieldset>
<legend>Account Information</legend>
<p>
<label for="name">Name:</label>
<%= Html.TextBox("Name", User.Identity.Name.GetDisplayName()) %>
<%= Html.ValidationMessage("username") %>
</p>
<p>
<label for="email">Email:</label>
<%= Html.TextBox("email", User.Identity.Name.GetEmailFromLogin()) %>
<%= Html.ValidationMessage("email") %>
</p>
<p>
<label for="division">Division:</label>
<%= Html.DropDownList("DivisionList", ViewData["DivisionList"] as SelectList)%>
<%= Html.ValidationMessage("confirmPassword") %>
</p>
<p>
<input type="submit" value="Register" />
</p>
</fieldset>
</div>
<% } %>
</asp:Content>
Edit 2:
Eilon: Here is what I changed it too:
Controller:
public ActionResult Register()
{
ViewData["DivisionList"] = repo.GetDivisions();
return View();
}
View:
<%= Html.DropDownList("DivisionValue", ViewData["DivisionList"] as SelectList)%>
I recieve this exception:
There is no ViewData item with the key 'DivisionValue' of type 'IEnumerable'.
When I updated the View to this:
<%= Html.DropDownList("DivisionList", ViewData["DivisionList"] as SelectList)%>
It works just great! It only seems to work if all the "Division" items named identically. If I change the name the View crashes or the ViewModel "Division" property is sent as 0.
Why is that?
The RegistrationViewModel type should contain a simple-typed property such as:
public string DivisionValue { get; set; }
Or change the type to int, DateTime, or whatever the appropriate type is.
In HTML and HTTP the only thing that gets posted back for a drop down list is the name of the field and the selected value.
To get everything to match up you also need to change the view to render a different input name for the drop down list:
<%= Html.DropDownList("DivisionValue", ViewData["DivisionList"] as SelectList)%>
Notice that I'm using "DivisionValue" is the value of the list, and DivisionList as the list of all available items.
I'd just be more explicit with the SelectList type. I'd suggest creating the SelectList in the controller action and forget about casting it in the view. My code works like this (CRUD Edit page):
..in the Action:
ViewData["WorkType.ID"] = new SelectList(this._vacancySvc.GetVacancyWorkTypes(),
"ID", "Name", ViewData["WorkType.ID"] ?? vacancy.WorkType.ID);
..and in the view:
<p><% =Html.Encode("Work Type:") %><br />
<% =Html.DropDownList("Worktype.ID")%><span class="smallgrey">(required)</span><br />
.. you can see that either the initial selection (from DB) is persisted or the ViewData from post backs (like if the form fails validation) thru the use of the [null coalescing operator][1] (??).
Moreover, if i refactored this code, i'd prob like to use a ViewModel object like you are.
The only thing is: (1) you'd never need to reference the ViewModel SelectList property in the view coz MVC auto binds this for us by the Html.DropDownList() overload.. and (2) i'd still need to ref the ViewData in the action anyway to get the selected value from a failed validation post back so what's the point really??

Resources