Change where custom designer views are located.
Is there a way to change where custom designer views for widgets are located?
The default location is:
~/Mvc/Views/[WidgetName]/DesignerView.[ViewName].[extension]
I would like to override that if possible. Has anyone done this before?
Here's the guide I'm referencing that specifies that requirement: http://docs.sitefinity.com/feather-create-custom-designer-views
Hello
The designer view must start with DesignerView. by convention. However, it can be resolved from different locations. If you want to set a specific one not searched by the default ViewEngines, you need to register a custom one:
http://www.sitefinity.com/developer-network/knowledge-base/details/mvc-add-new-view-engine-to-sitefinity
Regards,
Nikola Zagorchev
Telerik
Hi Nikola,
Thanks for the link to that post. It was helpful. I was actually inheriting from RazorViewEngine to begin with, and it was good to see an example of inheriting from VirtualPathProviderViewEngine.
Unfortunately it is still not working, here's a link to my current code: http://pastebin.com/HiMjdquD
I do have my Application_Start calling "ViewEngines.Engines.Add(new WidgetFolderViewEngine());"
What's interesting is Sitefinity is picking up the main view that I dictate for the widgets, but not the designer views. It only picks up the designer views when they're under "~/Mvc/Views/1/DesignerView.0.cshtml, and not at my custom route.
So is there a way to get the designer views picked up at a custom location? My current custom location is "~/Widgets/1/Views/0.cshtml".
I wonder if the view engine isn't picking up these designer views because it's confused by the extra dot in it's full name?
It does not seem that Sitefinity is using the VirtualPathProviderViewEngine for loading the DesignerViews. Here's some new code I wrote: http://pastebin.com/rSJKnJga
I put a breakpoint in every overridden method when trying to see the designer view for the widget, and a relevant view was not searched for or created by the ViewEngine.
Hello
The ViewEngine implementation seems correct. You also state that it is finding the Controllers views fro the frontend, so the ViewEngine is working.
I will need some time to set it up and test with this view engine and will let you know my results.
Regards,
Nikola Zagorchev
Telerik
Hey Nikola,
I started really digging into the difference between the ways the designer views are called when changing the view locations, here's what I found:
(1) edit is clicked on the widget
(2) a call to the following URL is made (always the same URL despite where the designer views are): localhost:53008/.../Navigation
(3) Here's the interesting part, the response back from that URL differs with a directive used on the html that's returned. When I put my designer views where I want them to be this directive looks like this:
"<server-data default-view='PropertyGrid' widget-name='Navigation' control-id='532bcbd9-908f-6451-9e80-ff00000c1994' culture='en'></server-data>"
Whereas if I put the designer views where Sitefinity wants them, it looks like this:
"<server-data default-view='NavigationView' widget-name='Navigation' control-id='532bcbd9-908f-6451-9e80-ff00000c1994' culture='en'></server-data>"
"
So, whatever is setting the values of that directive in the responding html is what seems to dictate where my designer views are, or how it finds them? I hope this helps.
Hi
It seems this is a limitation, as well.
The virtual paths that are resolved using the ViewEngines locations and the WidgetName for the Designer, always assume that the folder with the Views is under a Mvc folder.
Check the FrontendControllerFactory AppendDefaultPath method and the path transformations implementation.
I will follow up with a feature request on this.
However, if you really need to make the designer views from other locations, you can handle this in the custom ViewEngine. Iterating the folders inside the main one (~/Widgets) will do the trick to map the location for the widgets controllers.
Sample code:
public
class
WidgetFolderViewEngine : VirtualPathProviderViewEngine
/// <summary>
/// Initializes a new instance of the <see cref="WidgetFolderViewEngine"/> class.
/// </summary>
public
WidgetFolderViewEngine()
string
[] widgetFolderFormats =
new
[]
// 1 Widget name 0 View name
"~/Widgets/1/0.cshtml"
,
// For embedded views use Frontend virtual path following the assembly name
"~/Frontend-Assembly/SitefinityWebApp/Widgets/1/0.cshtml"
;
try
var dirs = Directory.GetDirectories(System.Web.HttpContext.Current.Server.MapPath(
"~/Widgets/"
));
foreach
(var dir
in
dirs)
var folderName = Path.GetFileName(dir);
this
.EnhanceLocationsUsingWidgetFolderName(folderName,
ref
widgetFolderFormats);
catch
(System.Exception ex)
// log ex or handle it differently
this
.FileExtensions =
new
string
[]
"cshtml"
;
this
.AreaViewLocationFormats = widgetFolderFormats;
this
.AreaMasterLocationFormats = widgetFolderFormats;
this
.AreaPartialViewLocationFormats = widgetFolderFormats;
this
.ViewLocationFormats = widgetFolderFormats;
this
.MasterLocationFormats = widgetFolderFormats;
this
.PartialViewLocationFormats = widgetFolderFormats;
this
.ViewLocationFormats = widgetFolderFormats;
...
private
void
EnhanceLocationsUsingWidgetFolderName(
string
widgetForlderName,
ref
string
[] folderFormats)
if
(!
string
.IsNullOrEmpty(widgetForlderName))
var widgetNameFormats =
new
string
[folderFormats.Length];
for
(
int
i = 0; i < folderFormats.Length; i++)
string
format = folderFormats[i];
format = format.Replace(
"1"
, widgetForlderName);
widgetNameFormats[i] = format;
folderFormats = widgetNameFormats.Concat(folderFormats).Distinct().ToArray();
Hi ,
Here is the feature request in our portal:
http://feedback.telerik.com/Project/153/Feedback/Details/190523-feather-ability-to-modify-designer-views-conventions
You can vote to increase its popularity.
Use the workaround provided in the meantime.
Regards,
Nikola Zagorchev
Telerik
Thank you!
Hey Nikola, while that workaround works great for the designer views, is there any way to tell sitefinity where to pick up the corresponding javascript files for that designer view?
Say I have a designer view located at "~Widgets/Navigation/Views/DesignerView.Simple.cshtml", I would like to have a script for that located at "~/Widgets/Navigation/Scripts/designerview-simple.js".
Thanks in advance.
This is an ancient post but I was looking for the same answer to the last question (relocating the script) myself.
I found a way to do it but it isn't pretty - embed the script in your project then change its logical name by editing the project file.
For instance, in your example you would have your designerview-simple.js file located under /Widgets/Navigation/Scripts/designerview-simple.js. In Visual Studio right click on the script file and select Properties. On the Properties tab change Build Action to Embedded Resource.
After doing that save the project. Then, right click on the project in the solution tab and select Unload Project. After the project unloads right click on the unloaded project and select Edit <name of your project file>.
When the editor appears do a search for your designer view script file. It should look something like this:
<EmbeddedResource Include="Widgets\Navigation\Scripts\designerview-simple.js" />
Edit this markup so that it looks like this:
<EmbeddedResource Include="Widgets\Navigation\Scripts\designerview-simple.js">
<LogicalName><name of your assembly>.Mvc.Scripts.Navigation.designerview-simple.js</LogicalName>
</EmbeddedResource>
Save the project file then close the editor window. In the solution explorer, right click the project and select Reload Project.
What these steps do is a) embed the script into the project dll and b) moves the resource location for the script to a place in the assembly where Sitefinity looks. You can edit the project file in a different editor instead of going through these steps in Visual Studio. After editing the project file in another editor Visual Studio will prompt you to reload the project, which you should. Further, we are editing the project file in this manner because the properties tab for a file in the solution unfortunately does not allow us to edit the LogicalName property.
When loading the designer Sitefinity looks for your corresponding designerview-simple script in /Mvc/Scripts/<your module>/. I believe it looks for it both in the file system and in the assembly. If it is found a call is made to /Frontend-Assembly/<your assembly name>/Mvc/Scripts/<your module>/designerview-simple.js. I believe the call to /Frontend-Assembly figures out where the script resides and serves it accordingly.
This is a slightly messy solution but keeps the solution tidy.