I need a way to instantiate a class thats supposed to help me with some querystring-building when it comes to my links in the view, and I cant use this methods that i require as static methods since that would cause the querystringbuilder to keep data during the whole life-time of the application (which would cause some serious problems)
So my question to you guys is,
Is it possible to some how be able to instantiate the class/object that i require but not in the actual view itself?
SO to keep the question simple.. is there anyway that I could do something like:
#MyInstantiatedObject.DoStuff()
in my view with out doing this before in my view:
#{
var MyInstantiatedObject = new MyClass()
}
I do get that I some where some how will need to instansiate the object, but my question is if its possible to do it in some other manner (like telling the web.config to handel it..or using some app_code #helper magic or something)
Thanks in advance!
What you are trying to achieve goes against the philosophy of MVC. If you want to keep the query string data between the actions, you can create your custom ActionLink html helper like this:
public static MvcHtmlString ActionLinkWithQueryString(this HtmlHelper htmlHelper,
string linkText, string actionName)
{
var routeValueDictionary = new RouteValueDictionary();
return htmlHelper.ActionLinkWithQueryString(linkText,
actionName, routeValueDictionary);
}
public static MvcHtmlString ActionLinkWithQueryString(this HtmlHelper htmlHelper,
string linkText, string actionName, RouteValueDictionary routeValues)
{
var queryString = HttpContext.Current.Request.QueryString;
if (queryString.Count > 0)
{
foreach (string key in queryString.Keys)
{
routeValues.Add(key, queryString[key]);
}
}
return htmlHelper.ActionLink(linkText, actionName, routeValues);
}
You can also create a custom RedirectToAction method in your Controller or in a Controller Extention like this:
private RedirectToRouteResult RedirectToActionWithQueryString(string actionName)
{
var queryString = Request.QueryString;
var routeValues = new RouteValueDictionary();
foreach (string key in queryString.Keys)
{
routeValues.Add(key, queryString[key]);
}
return RedirectToAction(actionName, routeValues);
}
I have an MVC4 project that I am trying to create a helper for. I have added a folder called "App_Code", and in that folder I added a file called MyHelpers.cshtml. Here are the entire contents of that file:
#helper MakeButton(string linkText, string actionName, string controllerName, string iconName, string classes) {
Primary link
}
(I know there are some unused params, I'll get to those later after I get this fixed)
I "cleaned" and built the solution, no errors.
In the page that uses the helper, I added this code.
#MyHelpers.MakeButton("Back","CreateOffer","Merchant","","btn-primary")
When I attempt to run the project, I get the following error:
Compiler Error Message: CS0103: The name 'Url' does not exist in the
current context
I can't seem to find the correct way to write this - what am I doing wrong? It seems to be correct as compared to examples I've seen on the web?
As JeffB's link suggests, your helper file doesn't have access to the UrlHelper object.
This is an example fix:
#helper MakeButton(string linkText, string actionName,
string controllerName, string iconName, string classes) {
System.Web.Mvc.UrlHelper urlHelper =
new System.Web.Mvc.UrlHelper(Request.RequestContext);
<a href='#urlHelper.Action(linkText,actionName,controllerName)'
class="btn #classes">Primary link</a>
}
For my helpers I create a base class:
using System.Web.WebPages;
using System.Web.Mvc;
namespace MyProject
{
public class HelperBase : HelperPage
{
public static new HtmlHelper Html
{
get { return ((WebViewPage)WebPageContext.Current.Page).Html; }
}
public static System.Web.Mvc.UrlHelper Url
{
get { return ((WebViewPage)WebPageContext.Current.Page).Url; }
}
}
}
And then in my helper I do (to use yours as an example):
#inherits MyProject.HelperBase
#using System.Web.Mvc
#using System.Web.Mvc.Html
#helper MakeButton(string linkText, string actionName, string controllerName, string iconName, string classes) {
Primary link
}
Also, are you sure you didn't mean to use #Html.ActionLink (via LinkExtensions) instead of #Url.Action? The latter doesn't seem to have a linkText, actionName, controllerName overload, the former does?
Normally in an ASP.NET view one could use the following function to obtain a URL (not an <a>):
Url.Action("Action", "Controller");
However, I cannot find how to do it from a custom HTML helper. I have
public class MyCustomHelper
{
public static string ExtensionMethod(this HtmlHelper helper)
{
}
}
The helper variable has the Action and GenerateLink methods, but they generate <a>’s. I did some digging in the ASP.NET MVC source code, but I could not find a straightforward way.
The problem is that the Url above is a member of the view class and for its instantiation it needs some contexts and route maps (which I don’t want to be dealing with and I’m not supposed to anyway). Alternatively, the instance of the HtmlHelper class has also some context which I assume is either supper of subset of the context information of the Url instance (but again I don’t want to dealing with it).
In summary, I think it is possible but since all ways I could see, involve some manipulation with some more or less internal ASP.NET stuff, I wonder whether there is a better way.
Edit: For instance, one possibility I see would be:
public class MyCustomHelper
{
public static string ExtensionMethod(this HtmlHelper helper)
{
UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
urlHelper.Action("Action", "Controller");
}
}
But it does not seem right. I don't want to be dealing with instances of UrlHelper myself. There must be an easier way.
You can create url helper like this inside html helper extension method:
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var url = urlHelper.Action("Home", "Index")
You can also get links using UrlHelper public and static class:
UrlHelper.GenerateUrl(null, actionName, controllerName, null, null, null, routeValues, htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, true)
In this example you don't have to create new UrlHelper class what could be a little advantage.
Here is my tiny extenstion method for getting UrlHelper of a HtmlHelper instance :
public static partial class UrlHelperExtensions
{
/// <summary>
/// Gets UrlHelper for the HtmlHelper.
/// </summary>
/// <param name="htmlHelper">The HTML helper.</param>
/// <returns></returns>
public static UrlHelper UrlHelper(this HtmlHelper htmlHelper)
{
if (htmlHelper.ViewContext.Controller is Controller)
return ((Controller)htmlHelper.ViewContext.Controller).Url;
const string itemKey = "HtmlHelper_UrlHelper";
if (htmlHelper.ViewContext.HttpContext.Items[itemKey] == null)
htmlHelper.ViewContext.HttpContext.Items[itemKey] = new UrlHelper(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection);
return (UrlHelper)htmlHelper.ViewContext.HttpContext.Items[itemKey];
}
}
Use it as:
public static MvcHtmlString RenderManagePrintLink(this HtmlHelper helper, )
{
var url = htmlHelper.UrlHelper().RouteUrl('routeName');
//...
}
(I'm posting this ans for reference only)
I am writing my own HtmlHelper extenstion for ASP.NET MVC:
public static string CreateDialogLink (this HtmlHelper htmlHelper, string linkText,
string contentPath)
{
// fix up content path if the user supplied a path beginning with '~'
contentPath = Url.Content(contentPath); // doesn't work (see below for why)
// create the link and return it
// .....
};
Where I am having trouble is tryin to access UrlHelper from within my HtmlHelper's definition. The problem is that the way you normally access HtmlHelper (via Html.MethodName(...) ) is via a property on the View. This isn't available to me obviously from with my own extension class.
This is the actual MVC source code for ViewMasterPage (as of Beta) - which defines Html and Url.
public class ViewMasterPage : MasterPage
{
public ViewMasterPage();
public AjaxHelper Ajax { get; }
public HtmlHelper Html { get; }
public object Model { get; }
public TempDataDictionary TempData { get; }
public UrlHelper Url { get; }
public ViewContext ViewContext { get; }
public ViewDataDictionary ViewData { get; }
public HtmlTextWriter Writer { get; }
}
I want to be able to access these properties inside an HtmlHelper.
The best I've come up with is this (insert at beginning of CreateDialogLink method)
HtmlHelper Html = new HtmlHelper(htmlHelper.ViewContext, htmlHelper.ViewDataContainer);
UrlHelper Url = new UrlHelper(htmlHelper.ViewContext.RequestContext);
Am I missing some other way to access the existing HtmlHelper and UrlHelper instances - or do i really need to create a new one? I'm sure there isn't much overhead but I'd prefer to use the preexisting ones if I can.
Before asking this question I had looked at some of the MVC source code, but evidently I missed this, which is how they do it for the Image helper.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")]
public static string Image(this HtmlHelper helper, string imageRelativeUrl, string alt, IDictionary<string, object> htmlAttributes) {
if (String.IsNullOrEmpty(imageRelativeUrl)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "imageRelativeUrl");
}
UrlHelper url = new UrlHelper(helper.ViewContext);
string imageUrl = url.Content(imageRelativeUrl);
return Image(imageUrl, alt, htmlAttributes).ToString(TagRenderMode.SelfClosing);
}
Looks like instantiating a new UrlHelper is the correct approach after all. Thats good enough for me.
Update: RTM code from ASP.NET MVC v1.0 Source Code is slightly different as pointed out in the comments.
File: MVC\src\MvcFutures\Mvc\ImageExtensions.cs
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")]
public static string Image(this HtmlHelper helper, string imageRelativeUrl, string alt, IDictionary<string, object> htmlAttributes) {
if (String.IsNullOrEmpty(imageRelativeUrl)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "imageRelativeUrl");
}
UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);
string imageUrl = url.Content(imageRelativeUrl);
return Image(imageUrl, alt, htmlAttributes).ToString(TagRenderMode.SelfClosing);
}
I faced a similar issue and decided that it would be easier to just call the UrlHelper in the view and pass the output to my HtmlHelper extension. In your case it would look like:
<%= Html.CreateDialogLink( "text", Url.Content( "~/...path.to.content" ) ) %>
If you want to access the extension methods on the existing HtmlHelper that is passed into your class, you should only need to import System.Web.Mvc.Html in your source code file and you will get access to them (that's where the extension classes are defined). If you want a UrlHelper, you'll need to instantiate that as the HtmlHelper you are getting doesn't have a handle for the ViewPage that it's coming from.
If you need to create a UrlHelper in a utility class you can do the following :
string url = "~/content/images/foo.jpg";
var urlHelper = new UrlHelper(new RequestContext(
new HttpContextWrapper(HttpContext.Current),
new RouteData()), RouteTable.Routes);
string absoluteUrl = urlHelper.Content(url);
This allows you to use routing or '~ expansion' away from an MVC context.
Well, you can always pass the instance of the page to the extension method. I think that is a much better way of doing this than creating new instances in your method.
You could also define this method on a class that derives from MasterPage/ViewMasterPage and then derive the page from that. This way, you have access to all the properties of the instance and don't have to pass them around.
In ASP.NET MVC is there an equivalent of the Html.ActionLink helper for Img tags?
I have a controller action that outputs a dynamically generated JPEG and I wanted to use the same Lambda expressions to link to it as I do HREFs using ActionLink.
Alternatively, a helper that just gives the URL to a route (again specified using Lambdas) would also be acceptable.
EDIT: I had originally specified that I was using Preview 5, however I see that a Beta has been released. So all-in-all the version number was an unneeded piece of info as I may be upgrading soon :-)
You can use the URL.Action method
<img src="../../Content/Images/add_48.png" />
This question is older, and I just started recently with ASP.NET MVC when the RC was already out, but for those who find this question later like me this might be interesting:
At least in the RC you can use Url.Action() also with anonymous types, the result looks much nicer than the suggestions above, I guess:
<a href="<%= Url.RouteUrl("MyRoute", new { param1 = "bla", param2 = 5 }) %>">
put in <span>whatever</span> you want, also <img src="a.gif" alt="images" />.
</a>
There are many other overloads for RouteUrl as well, of course.
Url.Action() will get you the bare URL for most overloads of Html.ActionLink, but I think that the URL-from-lambda functionality is only available through Html.ActionLink so far. Hopefully they'll add a similar overload to Url.Action at some point.
I used a workaround to place a marker instead of text for ActionLink and then replace it with my image code. Something like this:
<%= Html.ActionLink("__IMAGE_PLACEHOLDER__", "Products").Replace("__IMAGE_PLACEHOLDER__", "<img src=\"" + myImgUrl + "\" />")%>
Not the most elegant solution but it works.
In MVC3, your link would look like this:
<img src="../../Content/Images/add_48.png" />
In ASP.NET MVC Beta, you can use the Html.BuildUrlFromExpression method in the Futures assembly (which is not included in the default ASP.NET MVC install, but is available from CodePlex) to create a link around an image--or any HTML--using the lambda-style ActionLink syntax, like this:
<a href="<%=Html.BuildUrlFromExpression<MyController>(c => c.MyAction())%>">
<%=Html.Image("~/Content/MyImage.gif")%>
</a>
To keep your image links borderless, you'll need to add a CSS rule like this:
img
{
border: none;
}
You can use this control.It behaves like ActionLink.
http://agilefutures.com/index.php/2009/06/actionimage-aspnet-mvc
It's pretty simple to achieve in MVC 2. I have created my own very simple extension method to support Lambda expressions for the Url.Action helper. It requires that you reference MVC 2 Futures.
Here's the code:
using System;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;
using ExpressionHelperInternal=Microsoft.Web.Mvc.Internal.ExpressionHelper;
namespace Bnv.Bssi.Web.Infrastructure.Helpers
{
public static class UrlExtensions
{
public static string Action<TController>(this UrlHelper helper, Expression<Action<TController>> action) where TController : Controller
{
RouteValueDictionary routeValuesFromExpression = ExpressionHelperInternal.GetRouteValuesFromExpression<TController>(action);
return helper.Action(routeValuesFromExpression["action"].ToString(), routeValuesFromExpression);
}
}
}
This is how you use it:
<img src="<%= Url.Action<YourController>(c => c.YourActionMethod(param1, param2)); %>" />
I know that my post is too late but i wanna share :)
I added new extension method something like this :
public static class ImageExtensions
{
public static MvcHtmlString ImageLink(this HtmlHelper htmlHelper, string imgSrc, string additionalText = null, string actionName = null, string controllerName = null, object routeValues = null, object linkHtmlAttributes = null, object imgHtmlAttributes = null)
{
var urlHelper = ((Controller)htmlHelper.ViewContext.Controller).Url;
var url = "#";
if (!string.IsNullOrEmpty(actionName))
url = urlHelper.Action(actionName, controllerName, routeValues);
var imglink = new TagBuilder("a");
imglink.MergeAttribute("href", url);
imglink.InnerHtml = htmlHelper.Image(imgSrc, imgHtmlAttributes) + " " + additionalText;
linkHtmlAttributes = new RouteValueDictionary(linkHtmlAttributes);
imglink.MergeAttributes((IDictionary<string, object>)linkHtmlAttributes, true);
return MvcHtmlString.Create(imglink.ToString());
}
public static MvcHtmlString Image(this HtmlHelper htmlHelper, string imgSrc, object imgHtmlAttributes = null)
{
var imgTag = new TagBuilder("img");
imgTag.MergeAttribute("src", imgSrc);
if (imgHtmlAttributes != null)
{
imgHtmlAttributes = new RouteValueDictionary(imgHtmlAttributes);
imgTag.MergeAttributes((IDictionary<string, object>)imgHtmlAttributes, true);
}
return MvcHtmlString.Create(imgTag.ToString());
}
}
Hope this helped.
Is Url.Content() what you're looking for?
Give it something like Url.Content("~/path/to/something.jpg") it will turn it into the appropriate path based on the application root.
-Josh
I took the above answers and made a bit of a wrapper extension:
public static MvcHtmlString ActionImageLink(this HtmlHelper helper, string src, string altText, UrlHelper url, string actionName, string controllerName)
{
return ActionImageLink(helper, src, altText, url, actionName, controllerName, null, null);
}
public static MvcHtmlString ActionImageLink(this HtmlHelper helper, string src, string altText, UrlHelper url, string actionName, string controllerName, Dictionary<string, string> linkAttributes, Dictionary<string, string> imageAttributes)
{
return ActionImageLink(helper, src, altText, url, actionName, controllerName, null, linkAttributes, imageAttributes);
}
public static MvcHtmlString ActionImageLink(this HtmlHelper helper, string src, string altText, UrlHelper url, string actionName, string controllerName, dynamic routeValues, Dictionary<string, string> linkAttributes, Dictionary<string, string> imageAttributes)
{
var linkBuilder = new TagBuilder("a");
linkBuilder.MergeAttribute("href", routeValues == null ? url.Action(actionName, controllerName) : url.Action(actionName, controllerName, routeValues));
var imageBuilder = new TagBuilder("img");
imageBuilder.MergeAttribute("src", url.Content(src));
imageBuilder.MergeAttribute("alt", altText);
if (linkAttributes != null)
{
foreach (KeyValuePair<string, string> attribute in linkAttributes)
{
if (!string.IsNullOrWhiteSpace(attribute.Key) && !string.IsNullOrWhiteSpace(attribute.Value))
{
linkBuilder.MergeAttribute(attribute.Key, attribute.Value);
}
}
}
if (imageAttributes != null)
{
foreach (KeyValuePair<string, string> attribute in imageAttributes)
{
if (!string.IsNullOrWhiteSpace(attribute.Key) && !string.IsNullOrWhiteSpace(attribute.Value))
{
imageBuilder.MergeAttribute(attribute.Key, attribute.Value);
}
}
}
linkBuilder.InnerHtml = MvcHtmlString.Create(imageBuilder.ToString(TagRenderMode.SelfClosing)).ToString();
return MvcHtmlString.Create(linkBuilder.ToString());
}
has made it easier for me anyway, hope it helps someone else.
I tried to put the output of the Html.Image into my Html.ImageLink helper.
#(new HtmlString(Html.ActionLink(Html.Image("image.gif").ToString(), "myAction", "MyController").ToString().Replace("<", "<").Replace(">", ">")))
The problem for me is, that the ActionLink name is encoded so I have < instead of <.
I just removed this encoding and the result works for me.
(Is there a better way of doing this instead using replace?)
Adding to the other posts: in my case (asp.net mvc 3) I wanted an image link to act as a language selector so I ended up with:
public static MvcHtmlString ImageLink(this HtmlHelper htmlHelper, string imgSrc, string cultureName, object htmlAttributes, object imgHtmlAttributes, string languageRouteName = "lang", bool strictSelected = false)
{
UrlHelper urlHelper = ((Controller)htmlHelper.ViewContext.Controller).Url;
TagBuilder imgTag = new TagBuilder("img");
imgTag.MergeAttribute("src", imgSrc);
imgTag.MergeAttributes((IDictionary<string, string>)imgHtmlAttributes, true);
var language = htmlHelper.LanguageUrl(cultureName, languageRouteName, strictSelected);
string url = language.Url;
TagBuilder imglink = new TagBuilder("a");
imglink.MergeAttribute("href", url);
imglink.InnerHtml = imgTag.ToString();
imglink.MergeAttributes((IDictionary<string, string>)htmlAttributes, true);
//if the current page already contains the language parameter make sure the corresponding html element is marked
string currentLanguage = htmlHelper.ViewContext.RouteData.GetRequiredString("lang");
if (cultureName.Equals(currentLanguage, StringComparison.InvariantCultureIgnoreCase))
{
imglink.AddCssClass("selectedLanguage");
}
return new MvcHtmlString(imglink.ToString());
}
The internalization support was done via a language route - original source here.
Nice solutions here, but what if you want to have more then just an image in the actionlink? This is how I do it:
#using (Html.BeginForm("Action", "Controler", ajaxOptions))
{
<button type="submit">
<img src="image.png" />
</button>
}
The drawback is that I still have to do a bit of styling on the button-element, but you can put all the html you want in there.
And it works with the Ajax helper as well: https://stackoverflow.com/a/19302438/961139