Showing an Image on a masterpage based on the logged in user - asp.net-mvc

So I have a masterpage that has an image whos source is a controller action returning a filestream.
The image will be different for each logged in user.
right now in my masterpage view i have this code wich relies on viewdata.
<img id="dealerlogo" src='/Files/DealerImage/GetDealerLogo/<%=Html.Encode(ViewData["dealerid"]) %>' alt="" />
obviously the problem with this is that I will need to provide the viewdata containing the ID
on everycontroller action returning a view that uses this master page, which is pretty much all of them.
Is there a better way to do this? Like is there a way to get the username right in the view?
Thanks!

You can easily encapsulate this logic in a [ChildActionOnly] Action that returns a partial view and then use new MVC 2 approach
<% Html.RenderAction("GetUserPhoto", "User"); %>
to have it everywhere in your view pages without passing ViewData in all actions.
Here's the solution:
[ChildActionOnly]
public ActionResult GetUserPhoto()
{
ViewData["UserId"] = Page.User.Identity;
return PartialView();
}
And in your view you use the same logic you used to show user image. Also you can directly send a FileResult to partial view to render image for you. In this approach you don't need to repeat ViewData["XXXX"] in all views and you need just to Render the new Partial View in your main Views.

You can use the Page.User.Identity.Name just like in the default logonusercontrol.aspx that is created for you when you create a new asp.net MVC site.
Welcome <b><%= Html.Encode(Page.User.Identity.Name ) %></b>
So for you, you would want something like this:
<img id="dealerlogo" src='/Files/DealerImage/GetDealerLogo/<%=Html.Encode(Page.User.Identity.Name) %>' alt="" />

I assume your GetDealerLogo action method has a parameter of dealerid. It's better to write something like:
<img src="<%= Url.Action("GetDealerLogo", "DealerImage", new { dealerid = ViewData["dealerid"] }) %>" />
Nothing wrong with passing it in via ViewData. You might also consider a strongly-typed View or your own ViewPage base class which exposes a DealerId property.
To make it even cleaner, I really like T4MVC. It would allow you to write:
<img src="<%= Url.Action(MVC.DealerImage.GetDealerLogo(dealerid)) %>" />
And, even further, you might create an Html helper, so you can write:
<%= Html.DealerLogo(dealerid) %>

Related

In ASP.NET Mvc, what's the best way to fork to multiple action methods, and back to the same view?

So far:
I have a page with multiple submits on it, where each submit depends on the previous one.
The same page is rendered each time a submit is clicked.
I found myself writing spaghetti code in the controller method (branching based on the ViewModel), and wanted to factor out the behaviour for each submit into a separate method.
I implemented the solution found here - specifically the solution posted by mkozicki based on the article by Maartin Balliauw.
This worked well for forking to different controller methods. But I encountered two problems:
Returning to the same view each time.
Hard-wiring the action method names in the View.cshtml
Here's the code:
Controller:
public class PlayerStatController : Controller
{
public class PlayerStatViewModel . . . //quite complex ViewModel
// HTTP GET
public ActionResult SelectPlayer()
{
List<string> idx_list = getSeasonIndex();
return View(new PlayerStatViewModel(idx_list));
}
// One of three forked action methods
[HttpPost]
[MultipleButton(Name = "action", Argument = "ChosenSeason")]
public ActionResult ChosenSeason(PlayerStatViewModel viewModel)
{
List<string> team_idx = getTeamNameIndex(viewModel.selected_seasonIndex);
return View("SelectPlayer",new PlayerStatViewModel(new List<string>(), team_idx, new List<string>(), 0));
}
Here an excerpt from the view (SelectPlayer.cshtml)
<form action="/PlayerStat/ChosenSeason" method="post">
<fieldset>
<legend>Select Season</legend>
<div class="editor-field">
#Html.LabelFor(m => m.selected_seasonIndex)
#Html.DropDownListFor(m => m.selected_seasonIndex, Model.seasonIndex_select_list)
#Html.ValidationMessageFor(m => m.selected_seasonIndex)
</div>
<p>
<input type="submit" value="Choose Season" name="action:ChosenSeason" />
</p>
</fieldset>
</form>
Hence:
Is returning from the forked action method with return View("SelectPlayer",new PlayerStatViewModel(...); the best solution to forcing the same view (SelectPlayer.cshtml) to be rendered every time?
Is there a way to avoid hard-coding the action method name in the View (i.e., <form action="/PlayerStat/ChosenSeason" method="post">) I would like to keep using #using (Html.BeginForm()) if possible.
Specifying the view name in the return statement is the best and most practical way to return a view that is named something different than the current action method being executed. I believe this is by design in order to decouple action methods from a single view.
Again, for the view if you want the form to post to an action other than the one specified in the current URL you have to specify it explicitly. Using an empty BeginForm() will cause the form to post to the same URL that was returned on the previous request.
I believe what you have is the best way to tackle the problem and is the way I have my MVC application implemented as well. There is nothing wrong with being explicit, especially when it comes to views and view logic because they are by their very nature explicit. Separating the different submit buttons into different action methods is a solid approach and one that will inherently require you to specify which action to target for each submit button. You can think of this approach as analogous to Web Forms Server Side Event Handlers for button clicks (minus all the nasty page life cycle). This approach is elegant and clean, only the server side code corresponding to the submit is executed.

ASP.NET MVC: Reuse of View by two controller actions

'Add' and 'Edit' views are typically more or less identical. How can I reuse a View so that Foos/Add and Foos/Edit/[Id] both use it? What would the actions look like?
Thanks
Simply specify the view name when calling the View() method like
public ViewResult Add() {
//...
return View("Foo");
}
public ViewResult Edit(int id) {
//...
var model = repository.get(id);
return View("Foo", model);
}
Your view will have to handle null/empty model values for the Add action or you could populate your model with default values.
You may want to consider using an Editor Template as opposed to reusing the same View. An Editor Template is a partial View that is used for editing and/or inserting data.
This would require separate views but the code would be minimal. The bulk of the code would be in the template which you would reuse for both the Add and Edit actions.
After you create your template, your Add View would look like (Razor):
#model Models.Foo
<h2>Add</h2>
<p>
#Html.EditorFor(model => model) // equivalent to EditorForModel()
</p>
And your Edit View would look like:
#model Models.Foo
<h2>Edit</h2>
<p>
#Html.EditorFor(model => model) // equivalent to EditorForModel()
</p>
I would use jQuery ajax. Clicking on Add or Edit would call serve action which will return the same PartialView (if you want to reuse it).
Then, in the success function of ajax call, you have just to put returned html (from that PartialView) into certain part of your page (or popup).
Nice and clean and no page reload...

New window using asp mvc in html form

I am building an mvc app for reporting. I have a page that has a form on it which contains multiple dropdownlist to choose some criteria for a report. I then have an input button to create the report. This button call a new view from the same controller. The new view gets the values from the page where the criteria is chosen from parameters and uses that to populate it's own view model. This is all working fine.
I would like to open the reports in a new window. When I look at the controller, all of the parameters that are supposed to be coming from the selection page are null. I assume I will have to pass these in via the querystring to be picked up by the controller. Is there a way that I can get the values of the dropdownlists from within my viewpage to construct the querystring?
Is this a good way to accomplish what I am trying to do? Would I be better of using an ActionLink instead of an input button? does it make any difference?
I hope this all makes sense. Thanks for any thoughts.
Just set a target attribute on your form to _blank and it should open the request in a new page/tab depending on the browser being used.
<% using (Html.BeginForm(myAction, myController, FormMethod.Post, new { target = "_blank" })
{ %>
<%-- ... --%>
<% } %>
As NickLarsen says...
You could use the target="_blank" attribute of the form element to display the results in a new window.
<form action="/controller/action" method="post" target="_blank">
Or
<% Html.BeginForm("action", "controller", FormMethod.Post, new { target="_blank" }); %>
//...
<% Html.EndForm(); %>

passing the original controller and action name - mvc

I have an ascx control for fruits that contains following code:
<div id = "fruits">
....
Ajax stuff1 UpdateTargetId = "fruits"
Ajax stuff2 UpdateTargetId = "fruits"
<%Html.RenderPartial("PagerAjax", (Pager)ViewData["pager"]); %>
</div>
now this fruit control can appear on a number of webpages and the pager would need to know what page the user is on so that it can pull next set of fruits accordingly. For example, if I am on red-fruit page, then PagerAjax should know I am only pulling out red fruits. So, basically I would need to know ControllerName (assume it's home) and actionName (assume it's redFruits()). (Example may seem inefficient but it makes sense for the real purpose.) Now, I could do something like this:
<%Html.RenderAction("PagerAjax", (Pager)ViewData["pager"], ViewContext.RouteData.Values["action"], controllerFilter = ViewContext.RouteData.Values["controller"] }); %>
However, as you noticed, the above RenderAction is inside the div that is being updated by Ajax callback, which means it will contain action and controller of the Ajax stuff rather than the original URL's.
What should be an efficient workaround?
You could pass the original ViewContext.RouteData.Values["action"] as parameter when calling your AJAX action. This way the action knows what was the original action and store it in the model (or ViewData as in your case I see that your views are not strongly typed). Now your markup could become:
<% Html.RenderPartial(
"PagerAjax",
(Pager)ViewData["pager"],
new ViewDataDictionary() { { "originalAction", ViewData["originalAction"] } }
); %>

Working with partial views

I'm trying to create a page that contains a grid and searching. The issue is that I want to have a partial view for the grid and one for the searching.
If doing a search, this should render the grid partial view with the new information.
At the moment I need information, such as what column I'm sorting by and so on, from the grid (currently stored in viewdata), in order to do the search as I want to keep those settings. This information is only available in the grid partial though.
What's the best approach of this to make it neat and nice in the code, but not a mess to work with?
Where can I store information that I need in the other partial view?
Partial View 1;
<table>
<%= Html.CreateGrid(Model, "Grid", "Grid", (int)ViewData["SortColumn"], (bool)ViewData["SortedASC"])%>
</table>
Partial View 2;
<div class="searchControl">
<input type="text" class="SearchBox" href="<%= Url.Action("Grid", "Grid", new {page = 1, columnToSortBy=/* would like to access viewdata from partial view 1 here. */, sortASC = /* would like to access viewdata from partial view 1 here. */ } ) %>" />
<input type="submit" value="Search" class="SearchButton" />
</div>
I know I might take the completely wrong approach on this, so feel free to point me in the right one!
Thanks!
ViewData is a good place to store data that is accessed in Views and Partials.
Even better if you use strongly typed views. Then you could access the data for sorting an filtering via a typed model.
I would have the model-classes implement an interface IGridFeatures that has properties for SortedASC, SortColumn, Page.
Its often a good idea to have these optional properties not in the route but in a querystring.
I think you'll be better of controlling your link through javascript, since all you really want is to control the UI.

Resources