How to pass MVC User Controls Different Data - asp.net-mvc

I have a user control I use multiple times on a page. It uniformly formats some object "X" and allows certain interactions. What I'd like to do is to pass a separate object to each separate instance of the user control. I'm currently rendering the controls as below, and just depending on some specific ViewData being set. I'd like to make it more generic than that.
<% Html.RenderPartial("Vignet"); %>

Use a strongly typed partial view, and pass the model explicitly:
<% Html.RenderPartial("Vignet", Model.VignetData); %>

Related

Dynamically compose ASP MVC view based on context

I have several views that are composed of several partial views based on context.
For example, we have a Project view that shows all the details of a project, which includes the scalar values, name etc, and also all the assigned employees, tasks and/or clients.
The problem I have is that certain types of project have all of the above sections while others have only two or even on section, ie. details only.
What is the best way to compose the Projects master view? I don't want to have logic to check the project in the view. Is there a way to compose a view in code by programatically rendering the relevant partials and ignoring the rest?
Otherwise are there any other ideas how to do this in a maintainable way? I could of course just render the partials using if statements to check if they apply, but that way the view contains VERY important logic. In another situation we want to use this method to display content based on the type of subscription a user has.
Thanks!
I would stick with the if approach. To avoid coding important logic in the view define properties in your view model and let the controller set the value so that inside the view you only have:
<% if (Model.HasDetails) { %>
<% Html.RenderPartial("details"); %>
<% } %>
Or if you are working with display/editor templates you could simply:
<%= Html.DisplayFor(x => x.Details) %>

Encapsulating User Controls in ASP.NET MVC

Sorry if this is a basic question - I'm having some trouble making the mental transition to ASP.NET MVC from the page framework.
In the page framework, I often use ASCX files to create small, encapsulated chunks of functionality which get inclded in various places throughout a site. If I'm building a page and I need one of these controls - I just add a reference and everything just works.
As far as I can tell, in MVC, the ASCX file is just a partial view. Does this mean that wherever I want to add one of these units of functionality I also have to add some code to the controller's action method to make sure the relevant ViewData is available to the ASCX?
If this is the case, it seems like a bit of a step backwards to me. It means, for example, that I couldn't just 'drop' a control into a master page without having to add code to every controller whose views use that master page!
I suspect I'm missing something - any help would be appreciated.
Thanks,
- Chris
As far as I can tell, in MVC, the ASCX
file is just a partial view. Does this
mean that wherever I want to add one
of these units of functionality I also
have to add some code to the
controller's action method to make
sure the relevant ViewData is
available to the ASCX?
Yes.
However, you can use a RenderAction method in your view instead of RenderPartial, and all of your functionality (including the data being passed to the sub-view) will be encapsulated.
In other words, this will create a little package that incorporates a controller method, view data, and a partial view, which can be called with one line of code from within your main view.
Your question has been answered already, but just for sake of completeness, there's another option you might find attractive sometimes.
Have you seen how "controls" are masked on ASP.NET MVC? They are methods of the "HtmlHelper". If you want a textbox bound to "FirstName", for example, you can do:
<%= Html.Textbox("FirstName") %>
And you have things like that for many standard controls.
What you can do is create your own methods like that. To create your own method, you have to create an extension method on the HtmlHelper class, like this:
public static class HtmlHelperExtensions
{
public static string Bold(this HtmlHelper html, string text)
{
return "<b>" + text + "</b>\n";
}
}
Then in your view, after opening the namespace containing this class definition, you can use it like this:
<%= Html.Bold("This text will be in bold-face!") %>
Well, this is not particularly useful. But you can do very interesting things. One I use quite often is a method that takes an enumeration and created a Drop Down List with the values from this enumeration (ex: enum Gender { Male, Female }, and in the view something like Gender: <%= Html.EnumDropDown(Model.Gender) %>).
Good luck!
You can render a partial view and pass a model object to.
<% Html.RenderPartial("MyPartial", ViewData["SomeObject"]);
In your partial view (.ascx) file, you can then use the "Model" object (assuming you've inherited the proper object in your # Control deceleration) to do whatever you need to with that object.
You can, ofcourse, not pass and Model and just take the partial view's text and place it where you want it.
In your main view (.aspx file), you will need to define the proper object in the ViewData that you're passing to the partial view.
Another method you can do is use:
<% Html.RenderAction("MyAction", "MyController", new { Parameter1="Value1"}) %>
What the previous method does is call a controller Action, take its response, and place it where you called the "RenderAction()" method. Its the equivalent of running a request against a controller action and reading the response, except you place the response in another file.
Google "renderaction and renderpartial" for some more information.

View Models (ViewData), UserControls/Partials and Global variables - best practice?

I'm trying to figure out a good way to have 'global' members (such as CurrentUser, Theme etc.) in all of my partials as well as in my views.
I don't want to have a logic class that can return this data (like BL.CurrentUser) I do think it needs to be a part of the Model in my views So I tried inheriting from BaseViewData with these members. In my controllers, in this way or another (a filter or base method in my BaseController), I create an instance of the inheriting class and pass it as a view data. Everything's perfect till this point, cause then I have my view data available on the main View with the base members. But what about partials?
If I have a simple partial that needs to display a blog post then it looks like this:
<%# Control Language="C#" AutoEventWireup="true" Inherits="ViewUserControl<Post>" %>
and simple code to render this partial in my view (that its model.Posts is IEnumerable<Post>):
<%foreach (Post p in this.Model.Posts) {%>
<%Html.RenderPartial("Post",p); %>
<%}%>
Since the partial's Model isn't BaseViewData, I don't have access to those properties. Hence, I tried to make a class named PostViewData which inherits from BaseViewData, but then my containing views will have a code to actually create the PostViewData in them in order to pass it to the partial:
<%Html.RenderPartial("Post",new PostViewData { Post=p,CurrentUser=Model.CurrentUser,... }); %>
Or I could use a copy constructor
<%Html.RenderPartial("Post",new PostViewData(Model) { Post=p }); %>
I just wonder if there's any other way to implement this before I move on.
Any suggestions?
Thanks!
Have you considered keeping these things in the session and writing a strongly-typed wrapper around the session that will give you access to this information? Then in any view you can simply create a new wrapper class with the ViewPage's (or ViewUserControl's) Session property and use it.

Post back complex object from client side

I'm using ASP.NET MVC and Entity Framework. I'm going to pass a complex entity to the client side and allow the user to modify it, and post it back to the controller. But I don't know how to do that and whether the client side entity will lost relationship tracking of EF as it is detached from object context.
ASP.NET MVC is capable of Model Binding complex objects and it's quite good at it. An easy way to do this is to name your view fields that same as the properties for your object. That way in your action method that the form posts to you only need the complex object as a parameter. Example:
<% using(Html.BeginForm()) { %>
<%= Html.TextBox("Property1") %>
<%= Html.TextBox("Property2") %>
<%= Html.TextBox("Property3") %>
<%= Html.TextBox("Property4") %>
<%= Html.CheckBox("PropertyBool") %>
<%= Html.TextBox("Property5") %>
<% } %>
Which posts to an action method like so:
public ActionResult Index(ComplexObject complexoObject)
{
}
That's a fairly simple example as you could have different form controls in the view for corresponding properties of the object. If your object is very complex you could always write your own model binder for it and over-ride the default model binder.
The entity will be detached. You will need to fetch the entity again, update the properties, and then commit the changes. Alternatively you could reattach the entity, but this can be tricky when you are dealing with multiple related entities being attached at the same time. If this is the case for you, check out http://www.codeproject.com/KB/architecture/attachobjectgraph.aspx.
You are saying that you display a list of 100 questions on one single page? I hope you are not doing that. First of all, this is not very user friendly. It involves a lot of scrolling down, user can get lost, its error prone and if something goes wrong he has to do it all over again.
A better approach is one or few questions at a time. With Next/Prev buttons for moving between them.
You could make it even easier for yourself and forget about postbacks and go the AJAX way - issue a post request to some Json service the AJAX way. And save the progress after each question, so if something goes wrong user can resume later from where he left off. It will be much more maintable, faster and user friendlier.

are strongly typed user control views not allowed asp.net mvc?

Here's the setup - I have a view that lists products. On that same page I have a user control view that lists categories.
I pass the list of product to the view like so:
return View(myProducts);
The user control view gets the data it needs via ViewData["Category"]
Now if I try to use a strongly typed user control view like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<myData.Models.Category>>" %>
I get this error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[myData.Models.Product]' but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[myData.Models.Category]'.
The user control view seems to be confused since I am passing in a "Product" list to the view. So if I remove the strong typing like so:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
Everything works fine.
So, are strongly typed user control views just not allowed? Or am I just doing something wrong?
When you render the partial view, use the ViewData["Category"] as the model that you pass to the control.
<% Html.RenderPartial( "MyUserControl", ViewData["Category"], ViewData ); %>
A generic list of type A is not assignable to a generic list of type B (where B is derived from A). This is a general rule for all generic collections in .Net.

Resources