How to pass an array / model object to master layout page - asp.net-mvc

I've created a xml file (layout.xml) to render its contents to layout page,
Action LoadPage is used to deserialize this xml.
var ser = new XmlSerializer(typeof(MyModel));
string path = Server.MapPath(Url.Content("~/LayoutConfig/layout.xml"));
var arrList = (MyModel)ser.Deserialize(File.OpenRead(path));
How to pass arrList to mvc master layout page.
I've tried setting ViewBag.Data = arrList; it works only for that particular controller action
I know ViewBag is actually just a wrapper around the ViewData object, and its purpose is to use dynamics to access the data instead of using magic strings
And i tried a dirty hack i.e #Html.Action("LoadPage","Main") to the master layot page and its not worked for me..
My question is it possible to get arrList in master layout page on each time it is called.
Any guidance and help is appreciated..!

Well, if you have in every View this key on ViewBag you could read on the Layout page., you could create your own base controller that inherits from Controller class and override the OnActionExecuting method, to fill this property on the ViewBag. In your controllers you should inherit from this custom base controller and you would get in all Views this Key on the ViewBag such as the asp.net mvc does with Title property on the ViewBag. For sample:
public class InfoController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var ser = new XmlSerializer(typeof(MyModel));
string path = Server.MapPath(Url.Content("~/LayoutConfig/layout.xml"));
var arrList = (MyModel)ser.Deserialize(File.OpenRead(path));
ViewBag.ArrayInfo = arrList;
base.OnActionExecuting(filterContext);
}
}
And in the other controllers:
public class HomeController : InfoController
{
public ActionResult Index()
{
return View();
}
}

Related

ASP .NET MVC 5 Filter get View.ViewBag data

I need log all GET request url + page.Title. Page title set in _Layout.cshtml as
<title>#ViewBag.Title - #S.AppName</title>
ViewBag.Title set in View.cshtml
ViewBag.Title = S.MemberDayReports;
I try use custom ActionFilterAttribute. In OnResultExecuted(..) ViewBag.Title is null.
I try override in controller OnResultExecuted but ViewBag.Title is null.
How intercept view output and log ViewBag.Title?
override WebViewPage
public abstract class WebViewPageAdv<T> : WebViewPage<T>
{
public string Title
{
get
{
var title = String.Format("{0} - {1}", ViewBag.Title, S.AppName);
//Because Title get in _Layout.cshtml its full page load
//and can log user GET request
return title;
}
}
}
Views\web.config
<pages pageBaseType="WebViewPageAdv">
_Layout.cshtml
<title>#this.Title</title>
Viewbag main purpose is controller to view communication. Value are set to null after redirection.TempData keep the information for the time of an HTTP Request. This mean only from one page to another.Tye to use temp data .
or also you can update a static variable for title in each post/get. Fro this purpose you can use filter to track current and last data. ViewBag wont fullfill your requirement.
The ViewBag description in MSDN describes where ViewBag is used for:
The ViewBag property enables you to dynamically share values from the
controller to the view. It is a dynamic object which means it has no
pre-defined properties. You define the properties you want the ViewBag
to have by simply adding them to the property. In the view, you
retrieve those values by using same name for the property.
If you change the property of the ViewBag in the View is not returned back to the pipeline and it is not visible in either filters or controller.
As a workaround you can set Title property in the view - in this case it would be visible both in filters and in controller:
public ActionResult Index()
{
ViewBag.Title = "Your title";
return View();
}
I haven't checked if values set in the controller are readable this way, but you can set the viewbag like
public class FilterNameHere : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewBag.propertyNameHere = "value here";
}
}
Then register the filter as a GlobalFilter via GlobalAsax such as
GlobalFilters.Filters.Add(new FilterNameHere());

Asp.Net MVC 5 How to send ViewBag to Partial View

I have a _LoginPartial View and want to send data to it by ViewBag, but the Controller that I'am sending data from, doesn't have a View.
public PartialViewResult Index()
{
ViewBag.sth = // some data
return PartialView("~/Views/Shared/_LoginPartial.cshtml");
}
This code didn't work for me.
It seems you're expecting this Index action to be called when you do: #Html.Partial('_LoginPartial'). That will never happen. Partial just runs the partial view through Razor with the current view's context and spits out the generated HTML.
If you need additional information for your partial, you can specify a custom ViewDataDictionary:
#Html.Partial("_LoginPartial", new ViewDataDictionary { Foo = "Bar" });
Which you can then access inside the partial via:
ViewData["Foo"]
You can also use child actions, which is generally preferable if working with a partial view that doesn't need the context of the main view. _LoginPartial seems like a good candidate, although I'm not sure how exactly you're using it. Ironically, though, the _LoginPartial view that comes with a default MVC project with individual auth uses child actions.
Basically, the code you have would already work, you would just need to change how you reference it by using Html.Action instead of Html.Partial:
#Html.Action("Index")
Notice that you're calling the action here and now the view.
You can always pass data directly to the partial view.
public PartialViewResult Index()
{
var data = // some data
return PartialView("~/Views/Shared/_LoginPartial.cshtml", data);
}
Pass multiple pieces of data
public class MyModel
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
}
public PartialViewResult Index()
{
var data = new MyModel(){ Prop1 = 5, Prop2 = 10 };
return PartialView("~/Views/Shared/_LoginPartial.cshtml", data);
}
I passed viewBag data to my partial view like below, and I converted that viewBag data object to JSON in my partial view by using #Html.Raw(Json.Encode(ViewBag.Part));
my code sample is given below.
public async Task<ActionResult> GetJobCreationPartialView(int id)
{
try
{
var client = new ApiClient<ServiceRepairInspectionViewModel>("ServiceRepairInspection/GetById");
var resultdata = await client.Find(id);
var client2 = new ApiClient<PartViewModel>("Part/GetActive");
var partData = await client2.FindAll();
var list = partData as List<PartViewModel> ?? partData.ToList();
ViewBag.Part = list.Select(x => new SelectListItem() {Text = x.PartName, Value = x.Id.ToString()});
return PartialView("_CreateJobCardView" ,resultdata);
}
catch (Exception)
{
throw;
}
}
Here i have passed both model and viewBag .
First off, the code in your question does not run. When you do #Html.Partial("_SomeView") the Index() method you have there does not run. All #Html.Partial("_SomeView") does is render _SomeView.cshtml in your current view using the current view's ViewContext.
In order to get this to work you need a bit of functionality that's common to all the controllers in your project. You have two options: extension method for ControllerBase or a BaseController that all the controllers in your project inherit from.
Extension method:
Helper:
public static class ControllerExtensions
{
public static string GetCommonStuff(this ControllerBase ctrl)
{
// do stuff you need here
}
}
View:
#ViewContext.Controller.GetCommonStuff()
BaseController
Controller:
public class BaseController : Controller
{
public string GetCommonStuff()
{
// do stuff you need here
}
}
Other controllers:
public class SomeController : BaseController
...
...
View:
#((ViewContext.Controller as BaseController).GetCommonStuff())

MVC Layout model design pattern

Every page in my site should have some of the same data, similar to how in SO every page displays info about the current user at the top of the page. The way I implemented this was to have a base controller class that all my controllers derive from. In that base controller's constructor I put my model in the ViewBag, and then my layout page uses that.
I'm running into problems with this because my layouts aren't strongly typed. For example, I have to construct new HtmlHelpers in the layout page:
#{var layoutHtml = new HtmlHelper<LayoutModel>(Html.ViewContext, Html.ViewDataContainer); }
#* OK, now we can use the html helper... *#
#layoutHtml.TextAreaFor(model => model.Feedback)
I really don't want to have to have my models subclass from a layout model, since that would force each action to fill out the shared model data individually, but manually creating HtmlHelpers also seems like a bad idea.
Any thoughts?
I might be wrong, but shouldn't you use partial views for this?
Your solution looks way too complicated.
A base controller is a great way to start. I would also introduce a base viewmodel. The base viewmodel would contain user specific information.
public abstract class BaseController : Controller
{
public string CurrentAccountName
{
get
{
return (HttpContext.User == null) ? null : HttpContext.User.Identity.Name;
}
}
public T CreateViewModel<T>() where T : BaseViewModel, new()
{
T viewModel = new T
{
UserName = CurrentAccountName
};
return viewModel;
}
}
public abstract class BaseViewModel
{
public string UserName { get; set; }
}
Then on each request you would populate your viewmodel with user specific information and whatever information is required for the view. MyViewModel is just a viewmodel that inherits from BaseViewModel.
public class MyController : BaseController
{
public ActionResult Index()
{
MyViewModel viewModel = CreateViewModel<MyViewModel>();
return View(viewModel);
}
}
In the master view I would pass in the BaseViewModel and the View I would pass in the inherited MyViewModel.
Now you have access to your user information in your master view and can pass it to a partial or render it directly to the page.
you can define multiple Layouts which you can use in your appropiate views! Just include them like so:
#{
Layout = "~/Views/Shared/_MySubLayout.cshtml";
}
i believe you can use RenderAction to solve this problem. Because information this action will display is common on all pages, you can put it in BaseController and call it from your site master. it will compute its own model and return that model to partial view which can be strongly typed and you don't have to instantiate htmlHelper the way you are doing now.

How do you use Models in a master page with ASP.NET MVC?

public ActionResult Index(){
var dataContext = new DataEvidencijaDataContext();
MembershipUser myObject = Membership.GetUser();
string KorisnickoIme = myObject.UserName.ToString();
var user = from i in dataContext.korisniks
where i.korisnik1 == KorisnickoIme
select i;
ViewData.Add("user", user);
return View(user);
}
In master page i put this
<%= Html.RenderPartial("profPredmeti", ViewData["user"])%>
but this is not work
You can use RenderAction for this to delegate menu rendering to some controller. Another option is to have your controller (or base controller class, or action filter) put the menu object into ViewData, and then your master page will do
<% Html.RenderPartial("MenuRenderView", ViewData["menu"]) %>
where MenuRenderView.aspx partial view consumes the menu object from ViewData["menu"]. What does this object contain depends on your database/code.
The approach that I have taken in the past is to have a base controller class from which all the other controllers inherit. In that base controller class, you can add items into ViewData after the controller is initialized:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
ViewData.Add("CommonPageData", CommonPageData);
}
The "CommonPageData" in this case is a property or type CommonPageData (a custom class) whose properties are lazy-loaded. One of the properties is a navigation item collection that is consumed on the master page.
User RenderPartial for the Menu, but you're gonna need to pass in the ViewData the datasource for the menu all the time, a solution is to make an abstract BaseController and to put the datasource in the ViewData in the constructor of that base controller
all the controllers will inherit from the base controller
I have this block sitting in my Site.Master page:
<script runat="server" type="text/C#">
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
MasterModel = SiteMasterViewData.Get();
}
protected SiteMasterViewData MasterModel;
</script>
That Get() method is a static factory method tacked on to the View Data class. All of this is embarrassing but here we are.

.NET MVC instantiate controller inside another controller

Is it possible for an ASP.NET MVC controller to create a new instance of a different controller and effectively delegate resonsibility to that?
Let's say for example that I have two controllers in the /Controllers/ directory:
public class HomeController : Controller
{
public ActionResult Index()
{
var otherController = new OtherController();
return otherController.ShowNumberOfThings(100);
}
}
public class OtherController : Controller
{
public ActionResult ShowNumberOfThings(int index)
{
return View(index);
}
}
...and a View called Views/Other/ShowNumberOfThings.aspx:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<int>" %>
Number of things: <%= Model.ToString() %>
When I hit the url:
http://localhost/Home/Index
I want to be presented with a page that reads:
"Number of things: 100"
I would like to be able to persist temporary data between controller redirections without being forced to use the session object (TempData[""] uses the session object for cross-controller redirections). My real world case has a complex object which needs passing (not just an int) so using a URL/Cookie is out of the question, and session state is a no-no.
In WebForms at least we could use Server.Transfer and maintain any state in the HttpContext.Items collection. In MVC the only option I can see is to call the controller method directly passing in required arguments.
At the moment it's having trouble trying to resolve the view folder as the "context" is still running under the HomeController.
I guess where I am going with this is trying to cludge ASP.NET MVC into acting like a FrontContoller.
Any ideas?
EDIT
In the end we had to serialise everything into a session and use that. A shame, but I have heard that MVC2 will support serialising objects into a ViewState.
If you want to be presented with "Number of things: 100" when you hit the Index action why not directly render the corresponding view:
public class HomeController : Controller
{
public ActionResult Index()
{
return View("~Views/Other/ShowNumberOfThings.aspx", 100);
}
}
I think it would be preferred to use.
return RedirectToAction("Controller", "Action")
However I'm guessing you want to maintain the Url Home/Index.
If you're looking at the FrontController pattern then you should investigate writing a Custom ControllerFactory which inherits from DefaultControllerFactory then Override the CreateController method.
You can register your factory using the code below.
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(new MyCustomControllerFactory();
RegisterRoutes(RouteTable.Routes);
}
In the Controller factory you have access to the RequestContext so you can change the RouteData as needed and delegate to the correct controller.
You could of course just set a a Custom route for Home/Index which goes to OtherController.ShowNumberOfThings()
routes.MapRoute("Home", "Home/Index/{id}",
new {controller = "Other", action = "ShowNumberOfThings", id = 100});
a different approach would be the use of partial views
instead of ~Views/Other/ShowNumberOfThings.aspx
you could put your view in ~Views/shared/ShowNumberOfThings.ascx
have both views ~Views/Other/ShowNumberOfThings.aspx and ~Views/Home/Index.aspx implement the partial view
public class HomeController : Controller
{
public ActionResult Index()
{
return View(100);
}
}
public class OtherController : Controller
{
public ActionResult ShowNumberOfThings(int index)
{
return View(index);
}
}
and in both views implement the partial view
<% Html.RenderPartial("~Views/shared/ShowNumberOfThings.ascx", ViewData.Model); %>
you can change the int for any object that will be passed to the model
Another possibility (similar to partial views) is to use Html.RenderAction. This allows for different view model classes and separate controller methods.
<% Html.RenderAction("yourActionName", "yourControllerName", routeValues); %>

Resources