i have done as Vdex suggested here:
https://stackoverflow.com/a/5801502/973485
And used the RenderPartialToString method he found. And it works perfectly like this:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Test()
{
string t = ViewToString.RenderPartialToString("Index", null, ControllerContext);
return Content(t);
}
}
But if i want to render the Home > Index from another Controller, i get:
Value cannot be null.
Parameter name: controllerContext
Like this:
public class FooController : Controller
{
public ActionResult Index()
{
string t = ViewToString.RenderPartialToString("Index", null, new HomeController().ControllerContext);
return Content(t);
}
}
Is there any way to pass a View from another Controller to a string? I have tried many different methods, and it all of them fails at the ControllerContext. Many thanks!
Update: Why i need to do this:
Imagine i have a website full of widgets, the amount of widgets on each page is dynamic, so i cannot hardcode them in my cshtml file. But in that file there are different areas defined where the widgets gets printet out. To print out these widget i have a list of IWidgetController wich contains alle the different Widgets available, and the interface sais that they need to containe a ActionResult for edit, new and view. example of widgets: CalenderController, NewsController, GalleryController and so on... So in those areas i need to print out the content of each of those Controllers. Now i could also load the URLHTML but i figured doing it from the inside would be faster... right?
Try this:
string t = ViewToString.RenderPartialToString("Index", null, this.ControllerContext);
Anyway, why do you need to convert to a string?
Related
I have the following code
public class BooksController : Controller
{
[Route("/Books/{id?}")]
public IActionResult Index(string id)
{
return View(id);
}
}
My problem is that when I try to enter the parameter it is (as it seems) considered as controller's action so I keep getting this exception.
I need somebody to explain what am I doing wrong.
If you want to pass some parameter to a view as a string you can make this like below:
public class BooksController : Controller
{
[Route("/Books/{id?}")]
public IActionResult Index(string id)
{
if (string.IsNullOrEmpty(id))
id = "default_value";
return View((object)id);
}
}
If the string type is passing to the View() call without casting to object it will be interpreted as a view name.
And the view model data should be declared as
#model string;
<h2>#Model</h2>
Try changing the route as given below -
[Route("Books", Name = "id")]
I am working on themes at present and the majority of work is going well am just at the phase where I am figuring out what view to present to the user.
This would be on a controller method so far I have.
public static class ThemeViewExtensions
{
public static string GetViewPath(RazorView view , string viewName = "Default")
{
var theme = "Default";
var themeViewPath = $"/Themes/{theme}{view.ViewPath}/{viewName}.cshtml";
return viewPath;
}
}
But for some reason I cannot access it here yes I have my using statement in
public IActionResult Index()
{
return View(this.GetViewPath());
}
What I want to happen is to be able to use it as above and be able to get the ViewPath and name that is being called for example /Controller/Action/ViewName whatever cshtml is that possible on a view?
I have the following controllers
public class StoreController : Controller
{
public ActionResult Index()
{
var model = new SomeViewModel();
return View(model);
}
}
and
public class SofiaStoreController : StoreController
{
public ActionResult GetIndex(string city)
{
return base.Index();
}
}
When invoking the base Index method from the derived class I get this error:
The view 'getindex' or its master was not found or no view engine
supports the searched locations. The following locations were
searched:
Seems the GetIndex() method looks for a view in the derived controller's view folder by default even though no View() method is called but since there isn't such the error occurs.
Any idea why the method implicitly looks for a view and how to overcome the error ?
EDIT: After spending some time research the problem I came across these two posts: http://howtoprogram.eu/question/asp-net-c-asp-net-mvc-inherited-controller-using-base-view,2445 and http://www.davidwhitney.co.uk/Blog/2010/01/19/asp-net-mvc-view-engine-that-supports-view-path-inheritance/ Seems that controller inheritance is not that popular or straightforward decision. The solutions to my problem could be :
1. Not to use controller inheritance
2. Creating a custom view engine as shown in the second link (advanced)
3. As other people mentioned bellow - using full path to the view or RedirectToAction also could work
It does look for a view based on the Action method name you originally called. You can always override this behavior if you use the overloaded View() method that accepts the view name/path:
public class StoreController : Controller
{
public ActionResult Index(string viewName = "Index")
{
var model = new SomeViewModel();
return View(viewName, model);
}
}
public class SofiaStoreController : StoreController
{
public ActionResult GetIndex(string city)
{
return base.Index();
}
}
My application has a view that is linked from various different other views located in different controllers. I would like to have a 'Back' button in this view that will send the user back to the previous view, which ever that may be.
To this end I have added a string attribute to the viewmodel which I would like to use to reference the originating view /MyController/MyAction in the #Html.ActionLink parameters.
Some of the views linking to this view belongs to current controller, some belong to other controllers. This means I have to pass the controller as well as the action to the ActionLink.
As it stands, my code looks something like this:
ViewModel:
public class MyViewModel
{
public int MyData { get; set; }
[HiddenInput]
public string ReturnUrl { get; set; }
}
View:
#Html.ActionLink("Back", Model.ReturnUrl)
This produces the undesirable result of localhost:####/CurrentController/MyController/MyAction
Of course I could always save two strings on the ViewModel (one for the controller and one for the action) and pass them to the ActionLink seperately, but if possible I would like to avoid that. Is there an overload of ActionLink that allows me to use a single return url string, without making implications about the controller?
Also, is it possible to achieve the same thing on the controller side, f.ex. like this:
[HttpPost]
public ActionResult DoStuff(MyViewModel model)
{
// do stuff
return RedirectToAction(model.ReturnUrl);
}
You shouldn't use ActionLink. Just use an anchor tag, like this:
<a href='#Url.Content(Model.ReturnUrl)'>Back</a>
And, in your Action, you can do it like below:
[HttpPost]
public ActionResult DoStuff(MyViewModel model)
{
// do stuff
return Redirect(model.ReturnUrl);
}
Also, another solution would be to have two properties (ReturnController and ReturnAction) in your model.
I've written a base class and some classes which derive from it.
I want to use these classes in one ActionResult, but if I'm trying to cast PSBase to PS1 I'm getting a System.InvalidCastException that type PSBase can not be converted to PS1.
Classes:
public class PSBase
{
public int stationId { get; set; }
public string name { get; set; }
}
public class PS1 : PSBase
{
public string reference { get; set; }
}
public class PS2 : PSBase
{
}
ActionResult:
[HttpPost]
public ActionResult ProductionStep(PSBase ps)
{
if (ModelState.IsValid)
{
var product = db.Product.FirstOrDefault(.........);
switch (ps.stationId )
{
case 1:
{
product.Reference = ((PS1)ps).reference;
db.SaveChanges();
break;
}
}
}
return View();
}
As I don't want to have for each class a own ActionResult (repeating much of the same code many times) I wanted put all this to one ActionResult. Any Ideas how I could implement this?
What you are trying to do will never work without custom ModelBinder (and even then it will be a huge mess I'd not recommend to implement), sorry.
Only when you are passing a model from Controller to View it remembers what type it was originally (including inheritance, etc.) because at that point you are still on the server side of the page and you are merely passing an object.
Once you enter a view and submit a form all that does it creates some POST request with body containing list of values based on input names.
In your case if you have a form based on PS1 and used all the fields as inputs, you would get something like:
POST:
stationId = some value
name = some value
reference = some value
(there is no mention of the original type, controller, method, etc.)
Now, what MVC does is that it checks what argument you are using in the header of the method (in your case ProductionStep(PSBase ps)).
Based on the argument it calls a model binder. What the default model binder does is that it creates new instance of that class (in your case PSBase) and goes via reflection through all the properties of that class and tries to get them from the POST body.
If there are some extra values in the POST body those are forgotten.
Unless you write a custom model binder for this default MVC implementation can't help you there.
I'd recommend creating two separate methods, one of each accepting different implementation of PSBase.
If you want to read more on Model Binders check this out http://msdn.microsoft.com/en-us/magazine/hh781022.aspx
EDIT:
By creating two separate methods I mean something like this:
[HttpPost]
public ActionResult ProductionStepA(PS1 ps)
{
if (ModelState.IsValid)
{
}
return View();
}
[HttpPost]
public ActionResult ProductionStepB(PS2 ps)
{
if (ModelState.IsValid)
{
}
return View();
}
then you have to distinguish between them in the view via different form action.