How to add a codebehind to as asp.net mvc view? - asp.net-mvc

I'm experiencing some craziness that I just can't figure out.
I've created the following class:
public abstract class AbstractView<T> : ViewPage<T> where T : class
which gives me some useful helpers and I'm using it in my views like this:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="Project.Web.Mvc.AbstractView<Project.Domain.Entities.Example>" %>
This all works fine. Now I've got to make a view which needs lots of complex rendering code, so I want to give my view a codebehind, so I can put all this presentational logic in it.
However, when I do:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="Project.Web.Views.Examples.View" CodeBehind="~/Views/Examples/View.aspx.cs" %>
public class View : Project.Web.Mvc.AbstractView<Project.Domain.Entities.Example>
Model is always null when I debug the view, which subsequently gives me:
"The view 'View' or its master could not be found. The following locations were searched..."
What's going on?
P.S. don't tell me codebehinds are evil. I'm writing presentation specific logic, and lots of it. It's not going inline in the aspx.

You could try using a code-alongside. By that I mean a class deriving from AbstractView<T> that is only used by this one view. It's virtually the same concept as a code-behind. Then just point at that instead of using AbstractView<T>.
Also, your Model won't be hooked up until OnLoad (could be earlier, I've never done it myself) so that could be your problem if you're trying to use it in the constructor.
Otherwise you might want to look at using a ViewModel so that instead of you passing what I assume are raw domain objects, you do the custom formatting in the ViewModel and pass that to AbstractView<T>.

Using View as your class name is probably not a good idea. Using something a more specific.
I take it you ae declaring the class in the namespace specified in the Inherits attribute?

Ok my bad, the model isnt ready in the codebehind until onload, and i was doing things in the constructor.
Strange that MVC would say "cant find the view" when it clearly can, there was just an exception thrown

Related

How to access strongly typed data in MVC

I've been searching for a good explanation of how to access my strongly typed data from the View of my MVC app (first time touching MVC) and can't seem to find it. Here's the code in my controller:
PersonDetailsModel personDetails = personProvider.GetPersonDetails(id);
return View("Person", personDetails);
I have a view called Person.aspx which looks like this (pretty much empty):
<%# Page Title="Title" Language="C#" Inherits="System.Web.Mvc.ViewPage<Models.PersonDetailsModel>" MasterPageFile="../MvcMasterPage.Master" %>
I would've thought I could just do something like Model.property or Person.property in the view to access the data, but I don't see how I can access the instance of my model. I'm sure it's easy, but I just don't see it.
Since you have a strongly typed view, Model is the instance of your model that you passed from the controller. So you could directly access its properties:
<%# Page
Title="Title"
Language="C#"
Inherits="System.Web.Mvc.ViewPage<Models.PersonDetailsModel>"
MasterPageFile="../MvcMasterPage.Master"
%>
<div><%: Model.SomeProperty %></div>
The Model property will be of type Models.PersonDetailsModel.
And if you were using the Razor view engine the equivalent view would look like this:
#model Models.PersonDetailsModel
<div>#Model.SomeProperty</div>

Pass data to User Control ASP.NET MVC

I have a user control which shows list of latest announcements. This user control would be used in almost 90% of my pages. Now my concern is how to pass data to this user control for latest announcements.
My first approach is to make a base controller and in Initialise method I pass data to user control via ViewBag/ViewData. All my other controllers derive from this base controller. This looks nice but my concern is that it may become an overkill for some simple solution existing already out there. Also I would need to make sure that no controller ever fiddles with my Viewdata/Viewbag data meant for my usercontrol.
Please let me know if this is correct way of proceeding ahead or there exists some better solution.
Thanks
Assuming you have a "user control" (you should try to refer to them as partial view's in MVC) that looks like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Announcement>>" %>
This means your partial view expects a list of Announcement objects.
Now, the question is - where are you rendering this partial view?
You could be doing it from a master page, you could be doing it from a view, or you could be doing it from another partial view.
Either way, the code to render the partial needs to look like this:
<% Html.RenderPartial("LatestAnnouncements", announcements) %>
But - where do you get the announcements from.
Assuming you have a Repository/DAL/helper method to get the latest announcements - i think you should have the ViewModel's you require inheriting from a base ViewModel:
public class AnnouncementViewModelBase
{
protected IEnumerable<Announcement> GetAnnouncements()
{
// call DAL
}
}
Then any master/view/partial that needs to render the latest announcements partial should be bound to a ViewModel which inherits from that base view model.
In the cases where the master/view/partial is not strongly-typed (e.g dynamic view), you can stick it in the ViewData. But if you have organized your view's correctly this shouldn't be required.
Is this the kind of thing you're after? How to pass data from view to UserControl in ASP.NET MVC?
You should use RenderAction in this kind of scenario, so that you do not have bother to pass the required data in each action method of your controllers.
I think the best way would be to use #Html.Action. This would allow me to call my actions dedicated to my usercontrols data and I can call it from anywhere.

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.

Does this really violate MVC Separation of Concerns

Simple question. I must be totally wrong but I thought worth asking this question.
Is accessing ViewData[“Message”] within the View correct according to separation of concerns described in MVC?
For example, in Controller:
ViewData[“Message”] = “Display this message”;
Within View we call
<%= ViewData[“Message”] %>
The alternative (does not violates Separation of Concerns) is to have the Message set in the view model.
For example in Controller:
UserViewModel.Message = “Display this message”
Within View we call
<%# Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<UserViewModel>" %>
<%= Html.TextBox("Message", Model Message)%>
Any ideas greatly appreciated.
The difference between ViewData and Model is that the former is untyped property bag and the latter is a strongly typed object. But they both act as 'models' for your View.
I don't see how these should be different, other than the last one being strongly typed? You pass data along to the view, and let the view do its thing.
To me, I think the concept of all three (ViewData, ViewBag and TempData) is a violation of MVC. Very convenient to use but not following the diagram found here.
The View shouldn't know what the Controller is passing. I actually asked the same question just a few minutes ago and then I found this post.

asp.net mvc partialview

It seems that my understanding of the use of partial views is not quite right.
I am trying to add a partial view which builds from a database and use the partial view within a master page.
Using the NerdDinner project (wich is great for mvc) I have added the following:
Views\Shared\dinners.ascx" :
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<NerdDinner.Models.Dinner>>" %>
<ul>
<% foreach (var dinner in Model) { %>
<li>
<%= Html.Encode(dinner.Title) %>
</li>
<% } %>
</ul>
Within "Views\Shared\Site.Master" :
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
...
<% Html.RenderPartial("dinners"); %>
The above will only currently function on the pages where the dinners object is currently availabe as a list (eg site/Dinners). Is there a way to do this in an elegant and mvc way or is this something that requires a clever compromise?
Why do you have it in your master? I would add another place holder to your master and then add it where it makes sense. I feel that master pages should be as agnostic to specifics. If you want it be on all dinner pages, just make nested master of the original one.
This is, IMO, one of the biggest limitations of ASP.NET MVC - managing shared data across multiple views (next to rendering partial ascx views to strings!). If you google or search on stackoverflow for something like 'managing shared view data asp.net mvc' you'll get a ton of results with various options, none of which are really perfect. The MVC team at Microsoft have acknowledged this as a problem and will hopefully include a standard solution in a future release.
Depending on how you manage data access, the easiest way may be to create a base Controller class and retrieve the data you need for the partial either inside the constructor or inside OnActionExecuting().
The option that I have chosen is to use the Html.RenderAction() helper method inside the MvcContrib project. It basically enables you to call an action method from your view and render the response. This isn't great because it requires your view to have yet more knowledge about controllers, but it gives an easy short-term solution that doesn't require hooking up any extra code on your part.
This tutorial on stephan Walther's site deal with this issue. If you use an abstract base class where the dinners object is populated and inherit from that, it will always be available, but you'll have to be aware that it's there always even when you don't need it ;).
One method I use is to create a helper method and use it in your Master Page.
public static void RenderDinners(this HtmlHelper helper)
{
helper.RenderAction<DinnersController>(c => c.Dinners());
}
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
...
<% Html.RenderDinners(); %>
As you can see the helper calls the Dinners Action method on the DinnersController.
public ActionResult Dinners()
{
...get dinners and put in the View
return Dinners(view);
}
partialview seems to me to be inherently flawed. It creates module coupling and breaks cohesion intentionally by definition.
I agree with Daniel, even if your control does not shows on every page, it shows on some of them, you should create your master as template only

Resources