We are working on ASP.NET MVC 6 project and it's necessary to render Razor views from other than file system source (Azure Blob storage in particular but it's not important).
Earlier (in MVC 5) it was possible to create and register custom VirtualPathProvider which can take view content from DB or resource DLLs (for example).
It seems that approach has been changed in MVC 6. Does anybody know where to look for?
UPD:
Here is an example of code I'm looking for:
public IActionResult Index()
{
ViewBag.Test = "Hello world!!!";
string htmlContent = "<html><head><title>Test page</title><body>#ViewBag.Test</body></html>";
return GetViewFromString(htmlContent);
}
The question is: how to implement that GetViewFromString function?
You need to configure a ViewLocationExpander:
services.SetupOptions<RazorViewEngineOptions>(options =>
{
var expander = new LanguageViewLocationExpander(
context => context.HttpContext.Request.Query["language"]);
options.ViewLocationExpanders.Insert(0, expander);
});
and here is the implementation for the LanguageViewLocationExpander :
https://github.com/aspnet/Mvc/blob/ad8ab4b8fdb27494f5dece6f1186acea03f9dd52/test/WebSites/RazorWebSite/Services/LanguageViewLocationExpander.cs
Basing your AzureBlobLocationExpander on that one should put you in the right track.
Just posted a sample of store .cshtml in Azure Blob Storage to GitHub.
See also my answer to another question on this
Basically you need to create an implementation of IFileProvider. This can then be registered in Startup.cs by configuring RazorViewEngineOptions
services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProvider = new AzureFileProvider(Configuration);
});
Related
I have the below code which works but is there a better way using the RenderMacroContent method. Not sure how to add parameters to that.
<umbraco:Macro runat="server" language="cshtml">#{
HtmlTextWriter writer = new HtmlTextWriter(this.Output);
var macroPressLogin = new umbraco.presentation.templateControls.Macro();
macroPressLogin.Alias = "Security-PressLogin";
macroPressLogin.Attributes.Add("TargetNode", Parameter.TargetNode);
macroPressLogin.RenderControl(writer); }</umbraco:Macro>
As long as your view inherits from Umbraco.Web.Mvc.UmbracoTemplatePage or Umbraco.Web.Mvc.UmbracoViewPage<YourModel> you should just be able to write something like:
#Umbraco.RenderMacro("MacroAlias", new { ParameterAlias1 = "value1", ParameterAlias2 = "value2" })
So in your case it would be:
#Umbraco.RenderMacro("Security-PressLogin", new { TargetNode = "targetNodeValue" })
If you're talking about calling one macro from within another then this should also work as long as your macro partial view inherits from Umbraco.Web.Macros.PartialViewMacroPage
From your example it looks like you're working with a legacy razor macro using umbraco.MacroEngines. If possible I would recommend upgrading to a partial view macro. Click here for some further info.
I know this question is a year old, but I hope someone finds a use for this information (using Umbraco version 6.x):
#(Html.Raw(umbraco.library.RenderMacroContent("<?UMBRACO_MACRO macroAlias=\"MacroAliasHere\" dynamicParameter=\""+ #dynamicValue + "\" staticParameter=\"staticValue\" ></?UMBRACO_MACRO>", Model.Id)))
Option number 2, which I prefer, is actually code to output a contour form using a value from the form picker datatype:
var helper = new UmbracoHelper(UmbracoContext.Current);
#Html.Raw(helper.RenderMacro("umbracoContour.RazorRenderForm", new { formGuid = #Model.formPickerAlias }))
I'm using EPiServer CMS 7.5 MVC application.
I can only see textboxes and buttons while creating a new form. I would like to have a link, which uploads a document, when clicked. Then this document should be able to view while looking to form data and also this should be attached along with the mail.
Any help?
There is no file upload control in XForm editor. One option - modify how XForm is rendered. XForm in EPiServer uses display templates to render. One way how to add file upload is to create your own XForm display template and add file upload. Display template will be used for all XForms in your application.
To create display template, create XForm.cshtml under /Views/Shared/DisplayTemplates/ in Visual Studio. Here is sample of source code of XForm.cshtml:
#using EPiServer.HtmlParsing
#using EPiServer.Web.Mvc.Html
#model EPiServer.XForms.XForm
#if (ViewData["XFormActionResult"] is EPiServer.Web.Mvc.XForms.XFormSuccessActionResult)
{
<strong>Form posted.</strong>
}
else
{
using (Html.BeginXForm(Model, new { #class = "form xform" }))
{
if (Model != null)
{
foreach (HtmlFragment fragment in (IEnumerable<HtmlFragment>)ViewData["XFormFragments"] ?? Model.CreateHtmlFragments())
{
// here can override particular fragment
// for example, check if TextBox Css class is "file-upload"
// then replace it with file upload
#Html.Fragment(fragment)
}
}
}
}
After that you have to handle posting the form yourself. This article describes well how to do it: http://www.eyecatch.no/blog/2013/01/using-xforms-and-mvc-in-an-episerver-7-block/
Then on OnActionExecuting in BasePageController you can handle file uploading. You can store it in the blob (in EPi 7 VPP) and store reference (GUID) in the XForm.
Mobile display modes in ASP.NET MVC 4 stop serving the correct views after about an hour of uptime, despite browser overrides correctly detecting an overridden mobile device.
Recycling the application pool temporarily solves the problem.
The new browser override feature correctly allows mobile devices to view the desktop version of a site, and vice-versa. But after about an hour of uptime, the mobile views are no longer rendered for a mobile device; only the default desktop Razor templates are rendered. The only fix is to recycle the application pool.
Strangely, the browser override cookie continues to function. A master _Layout.cshtml template correctly shows "mobile" or "desktop" text depending on the value of ViewContext.HttpContext.GetOverriddenBrowser().IsMobileDevice, but the wrong views are still being rendered. This leads me to believe the problem lies with the DisplayModes.
The action in question is not being cached:
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
I am using 51Degrees for mobile detection, but I don't think this should affect the overridden mobile detection. Is this a bug in DisplayModes feature for ASP.NET MVC 4 Beta & Developer Preview, or am I doing something else wrong?
Here is my DisplayModes setup in Application_Start:
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone")
{
ContextCondition = context =>
context.GetOverriddenBrowser().IsMobileDevice
&& (context.Request.UserAgent.IndexOf("iPhone", StringComparison.OrdinalIgnoreCase) >= 0
|| context.Request.UserAgent.IndexOf("Android", StringComparison.OrdinalIgnoreCase) >= 0
|| !context.Request.Browser.IsMobileDevice)
});
/* Looks complicated, but renders Home.iPhone.cshtml if the overriding browser is
mobile or if the "real" browser is on an iPhone or Android. This falls through
to the next instance Home.Mobile.cshtml for more basic phones like BlackBerry.
*/
DisplayModeProvider.Instance.Modes.Insert(1, new DefaultDisplayMode("Mobile")
{
ContextCondition = context =>
context.GetOverriddenBrowser().IsMobileDevice
});
This is a known issue in MVC 4 (Codeplex: #280: Multiple DisplayModes - Caching error, will show wrong View). This will be fixed in the next version of MVC.
In the meantime you can install a workaround package available here: http://nuget.org/packages/Microsoft.AspNet.Mvc.FixedDisplayModes.
For most applications simply installing this package should resolve the issue.
For some applications that customize the collection of registered view engines, you should make sure that you reference Microsoft.Web.Mvc.FixedRazorViewEngine or Microsoft.Web.Mvc.FixedWebFormViewEngine, instead of the default view engine implementations.
I had a similar issue and it turned out to be a bug when mixing webforms based desktop views with razor based mobile views.
See http://aspnetwebstack.codeplex.com/workitem/276 for more info
Possibly a bug in ASP.NET MVC 4 related to caching of views, see:
http://forums.asp.net/p/1824033/5066368.aspx/1?Re+MVC+4+RC+Mobile+View+Cache+bug+
I can't speak for this particular stack (I'm still in MVC2) but check your output caching setup (either in your controllers or views - and in your web.config in your app and at the machine level). I've seen it work initially for the first few users and then a desktop browser comes in right around the time ASP decides to cache, then everyone gets the same view. We've avoided output caching as a result, hoping this would get addressed later.
If you want all mobile devices to use the same mobile layout you can use
DisplayModeProvider.Instance.Modes.Insert(1, new DefaultDisplayMode("Mobile")
{
ContextCondition = context =>
context.GetOverriddenBrowser().IsMobileDevice
});
And of course you need to make a view in the shared layout folder named _Layout.Mobile.cshtml
If you want to have a separate layout for each type of device or browser you need to do this;
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("Android")
{
ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
("Android", StringComparison.OrdinalIgnoreCase) >= 0)
});
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone")
{
ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
("iPhone", StringComparison.OrdinalIgnoreCase) >= 0)
});
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("Mobile")
{
ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
("IEMobile", StringComparison.OrdinalIgnoreCase) >= 0)
});
And of course you need to make a view in the shared layout folder for each named
_Layout.Android.cshtml
_Layout.iPhone.cshtml
_Layout.Mobile.cshtml
Can you not just do this?
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// Code removed for clarity.
// Cache never expires. You must restart application pool
// when you add/delete a view. A non-expiring cache can lead to
// heavy server memory load.
ViewEngines.Engines.OfType<RazorViewEngine>().First().ViewLocationCache =
new DefaultViewLocationCache(Cache.NoSlidingExpiration);
// Add or Replace RazorViewEngine with WebFormViewEngine
// if you are using the Web Forms View Engine.
}
So guys here is the answer to all of your worries..... :)
To avoid the problem, you can instruct ASP.NET to vary the cache entry according to whether the visitor is using a mobile device. Add a VaryByCustom parameter to your page’s OutputCache declaration as follows:
<%# OutputCache VaryByParam="*" Duration="60" VaryByCustom="isMobileDevice" %>
Next, define isMobileDevice as a custom cache parameter by adding the following method override to your Global.asax.cs file:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (string.Equals(custom, "isMobileDevice", StringComparison.OrdinalIgnoreCase))
return context.Request.Browser.IsMobileDevice.ToString();
return base.GetVaryByCustomString(context, custom);
}
This will ensure that mobile visitors to the page don’t receive output previously put into the cache by a desktop visitor.
please see this white paper published by microsoft. :)
http://www.asp.net/whitepapers/add-mobile-pages-to-your-aspnet-web-forms-mvc-application
Thanks and Keep coding.....
Well, I think the ASP.NET MVC team released a pretty significant bug in the developer preview for asp.net mvc 4, or I'm doing something stupid... Here's the issue and steps to reproduce.
Create a new MVC 4 mobile application
create a new section in the layout (ex. #RenderSection("head",false))
in the controller action just throw a Message into the ViewBag
then in a view that uses the main layout, add the following code below.
#section head {
$(function() {
var newVariableName = "#(ViewBag.Message)";
});
}
You'll notice that the razor parser actually thinks that the section has been completed for the jquery on dom loaded ending brace instead of the section's ending brace. I tried the exact same code in an asp.net MVC 3 application and it worked with no issues.
Has anyone else come across this bug in the ASP.NET MVC 4 Developer Preview?
A quick hack to solving this issue is to use < text > blocks < /text > around the java script. Here's what it might look like until the ASP.NET MVC team resolves this bug.
#{
<text>
$(function()
{
var newVariableName = "#(ViewBag.Message)";
});
</text>
}
As mentioned above, try this in your cshtml file...
#section head {
#{
function JSMeth1()
{
// doing your stuff, razor parser wont suck
}
}}
I have just started using the
<% Html.DevExpress().DateEdit()
control and i got it to work fine in my ASP.Net MVC application. The code is as shown below:
aspx page:
<% Html.DevExpress().DateEdit(settings =>
{
settings.Name = "EndDate";
settings.Properties.NullText = "dd/MM/yyyy";
settings.Properties.EditFormat = EditFormat.Custom;
settings.Properties.EditFormatString = "dd/MM/yyyy";
settings.Properties.DisplayFormatString = "dd/MM/yyyy";
settings.Date = Model.EndDate;
settings.Width = 100;
}
).Render();
%>
Above this code i have a reference to my javascript file (DateChanges.js) in this file i want to be able to do something like:
$(document).ready(function(){
$("#EndDate").change(function(){
//do whatever i want
});
})
I cant do this now cause using firefox i can see that the actual textbox that this datepicker assigns a value to has be named "EndDate_I". So my question is how can i easily do this since i want to be able to catch the change event of this control and play around with it in jQuery??
The DevExpress MVC Extensions offer their own infrastructure for the client-side processing needs (see the http://help.devexpress.com/#AspNet/CustomDocument6908 help topic to getting started).
It is necessary to handle the client-side ASPxClientDateEdit.DateChanged event, and retrieve the newly selected Date via the client-side ASPxClientDateEdit.GetDate() method. Use the retrieved js Date object for your additional needs:
<script type="text/javascript">
function OnDateChanged(s, e) {
var newDate = s.GetDate();
alert(newDate);
}
</script>
settings.Properties.ClientSideEvents.DateChanged = "OnDateChanged";
There is a rather long Blog post at http://kennytordeur.blogspot.com/2011/05/aspnet-mvc-where-is-clientid_10.html discussing your problem
( I think it is to long to have it pasted here, and the author deserves the credits )
following on from your comment on Mikhails's answer, there will be a property in the global namespace with the name of your control, so it's just like this:
CalculateDayDifference(s.GetDate(), EndDate.GetDate());
All the mvc controls do this, for some you might have to set the EnableClientSideApi property to start using them.