ASP.Net WebForms User Controls With Code Behind In MVC - c#

I'm trying to move from the Old WebForms .NET approach to the newer MVC version. I just can't seem to find a solution to this issue.
In my current projects I use a lot of custom made User Controls. These controls will in almost all cases have several properties that are populated as parameters in the code behind of the parent 'aspx' page.
The User Control will have an 'ascx' page where all the html and controls exist. There will also be a 'ascx.cs' file attached to it where all the properties, methods and back-end logic occur.
What I can't seem to work out is how this logical process works in MVC? The .ascx file is similar to an MVC PartialView... that does make sense.
But where do you store all the backend logic for a PartialView?
How do I set multiple properties and construct the View based on these values?
I've seen some people suggesting you can still use .ascx files in MVC but I'm not sure this is the correct route to go down... certainly not the best practice route anyway?
I'll give a small example which may help:
country.aspx
<%# Register tagprefix="CUSTOM" tagname="Weather" src="~/controls/Weather.ascx" %>
<CUSTOM:Weather ID="Weather" runat="server"></CUSTOM:Weather>
country.aspx.cs
Weather.W_CountryCode = CountryCode;
Weather.W_CountryName = CountryName;
weather.ascx.cs
public string W_CountryCode { get; set; }
public string W_CountryName { get; set; }
Ok that is very basic structure of a control.
The control is embedded into the parent page.
Parameters are set in the code behind of the parent page.
The properties in the control will be used to collect the selected countries weather data from the database as well as running various other methods.
This easily reusable self contained code... I just can't see how you do the same thing in MVC? Where do you set the parameters... where is the code behind for the View stored?
Thanks in advance for any help

When you create a "page" in MVC, you have a controller action that builds a model and passes it to a view.
A "partial" page works exactly the same way - you have a controller action that builds a model and passes it to a view.
The only difference is that the action returns View() or PartialView().
When you want to re-use the partial, you can do so in two ways - load via the action or load via the partial. When you load via the action #Html.Action, you call the controller-action (perhaps with parameters) and that action builds the model and returns the (partial) view. When you load via #Html.Partial your view passes the model to the partial directly (ie not via a controller). Either is acceptable, it depends on how you are building the partial and whether you've already loaded some data or not etc.
So, for your example:
StaffViewModel.cs (partial)
public string W_CountryCode { get; set; }
public string W_CountryName { get; set; }
Staff.cshml (partial)
#model StaffViewModel
<div>Country: #Model.W_CountryCode / #Model.W_CountryName</div>
CountryViewModel.cs (view)
public IList<StaffViewModel> StaffList { get; set;}
Country.cshtml (view)
#model CountryViewModel
#foreach (var staff in Model.StaffList)
{
#Html.Partial("Staff", staff)
}
Controller.cs
public ActionResult Country()
{
var model = new CountryViewModel();
model.StaffList = new List<StaffViewModel>();
// populate staff list from DB etc here
return View(model);
}

Related

Get URL of current page in C# with AngularJS

I am working on tracking system for my SignalR Hub purpose. For this I have class where I want to store URL which will represent current page where user is, then his last request since I use paged list for my data tables and I need to know exactly on which page user is and of course user id.
This is my class where I want to store information
public class UserTracking
{
public string URL { get; set; }
public string LastRequest { get; set; }
public string UserId { get; set; }
}
Since I have a single page application I have problem with tracking user position on website because of angular routing, but for displaying views I have dashboard controller with ActionResults methods to allow me to display .cshtml pages with angular routing, something like this below
public ActionResult ProjectTask()
{
return View();
}
public ActionResult Project()
{
return View();
}
My question is if I am able somehow to get URL of user position on my website. For example if user is on http://localhost:2969/Dashboard#/tasks/ I want to get this /Dashboard#/tasks. Maybe I can get some information from my ActionResults, but I have no idea how.
It is hard to know if this will work for you with the information you've provided, but the following could work.
If you know the specific routes that a user must navigate to to reach you MVC Controller then you could pass in that information as a model when you return the view. Say I have an MVC route that is meant to return a Razor page that displays all the items from my list named "Freddy's Birthday". The URL in this case would look something like:
localhost/list/Freddy's%20Birthday
matching a route:
[Route("~/list/{id}")]
To inform Angular of what you're dealing with, simply pass in the information as a model:
public ActionResult List(string id)
{
return View(model: id);
}
You can access that model in your cshtml with #Model. If the id passed in was "Freddy's Birthday" like before:
<div todo-list-item-directive>#Model</div>
Would return from ASP as:
<div todo-list-item-directive>Freddy's Birthday</div>
This way works, and you could build some view models to reference in your Razor pages if you need to pass in more complicated information. However, if at all possible, it'd likely be worth your while to set up ui-router and use Angular for your view routing and ASP.NET for your API.

Different start layouts for different user types with asp.net mvc

I have three types of roles for each of the menu links.
When the Billing guy is logging into the site
how can I determine dynamically the partial.html file that is shown in the content area?
I can not hardcode the content to the first actionlink in the menu, that means that always the Administration is loaded initially.
What can I do in such a case?
These types of decisions are best made in the Controller.
Example:
public HomeController: Controller
{
public ActionResult Administration()
{
// Determine the user's role.
// "GetRole()" does not really exist on the controller - use your own method.
string role = GetRole();
if (role == "Billing Guy")
return View("AdministrationBillingGuy")
else if (role == "SalesGuy")
return View("AdministrationSalesGuy")
else
return View();
// etc.
}
}
I can think of several ways to do this.
if you need all users to get the same url/action then you could do something like this
public ActionResult Custom(RoleEnum userRole)
{
switch(userRole)
{
case RoleEnum.Admin:
.....
return Partial("_adminPartial", viewModel);
// rest of you cases here
}
}
OR:
public ActionResult Custom(RoleEnum userRole)
{
var view = GetViewByRole(userRole);
// where GetViewByRole takes the enum and
// returns a string with the name of the partial
return Partial(view, viewModel);
}
Another way to do this is, and one that I'd recommend is to make an MVC Area for each user requiring a different layout and then at login you can redirect them to the proper Area, I recommend it because it allows for deeper differentiation between roles in the UI layer.
Another way to achieve the different layouts (am talking about MVC Layout Pages similar to ASP.Net Master pages) is to pass a string Layout to the view, using the ViewBag or any other method you like, then in the Razor code you could do something like this:
#model MyViewModel
#{
Layout = (string)ViewBag.Layout;
}
I leaved this last one for last as it appears a bit hacky to me. Hope this helps you
Well, you haven't provided enough information give any explicit direction, but generally, you should just alter your login post action to redirect to a different place depending on some identifying factor like a role (following is pseudocode)
// do login
if (user is "Billing")
{
// redirect to billing action
}
// etc.
The only reason you should be switching out partials or views is if you're doing a SPA (single page application) and utilizing JavaScript for routing. In that case, you would just need some endpoint you could hit with AJAX to get the user's "role".
However, I don't think that's what you're actually doing. If you're just using MVC directly, then you should be actually changing the URL, not just loading a different Razor view.

Sending to different Pages depending on Web.config Razor

So I want to have a status page that will show a different layout of the equipment depending on who's using it which will be a variable defined in the web.config. Should I be creating a separate controller per view? Some of the background functions should be similar but some will probably be different in the future. Or should I have the same cshtml file and hide html markup depending on who's using it or not?
I was thinking of doing something like:
#if(System.Configuration.ConfigurationManager.AppSettings["IsSuperUser"] == "true")
{
Status
}
else {
Status
}
Or is this a bad idea?
There are several options, it all depends on your needs and preferences.
Your code will work, however you must also double check permission in your controller! For example, your url will be "/SuperUser/Status" and "/User/Status". Now, what's stopping non-super user to type in "/SuperUser/Status" to the address bar?
One important rule, never trust the end users! Assume that they will not do what you intend them to do.
Given all, my preference would be to include a variable in your Model to identify the user level (super vs non super), then use that to determine the layout in your views. Remember, you can also change the layout of the view based on variable/expression.
#Layout = Model.IsSuperUser ? "_SuperLayout.cshtml" : "_RegularLayout.cshtml";
Sounds like a view concern. I would pass the config data through a dependency in the controller and render partials:
#if (Model.IsSuperUser)
{
#Html.Partial("_SuperUser")
}
else
{
#Html.Partial("_User")
}
The controller can then do something like:
public ActionResult Index()
{
var vm = new MyViewModel();
vm.IsSuperUser = _config.GetSuperUser();
return View(vm);
}

How to render field in PageEditor when using Template inheritance with Sitecore Glass Mapper

I have the following setup in my solution, described in an earlier post:
How to implement Sitecore Template inheritance with Glass Mapper
I'm able to display the content of inherited fields now. However when I try to edit them in the page editor the EditFrame is not rendered, nothing gets rendered actually. Not sure what I'm still missing. Here's my controller and view:
Controller:
public class NavigationController : Controller
{
// GET: /Navigation/
public ActionResult Index()
{
var context = new SitecoreContext();
var page = context.GetCurrentItem<HomePage>();
return View("/Views/Navigation.cshtml", page);
}
}
View:
#inherits Glass.Mapper.Sc.Web.Mvc.GlassView<Sitecore.Training7.Internet.Core.Models.HomePage>
#if (Model != null)
{
<h3 class="text-muted">Field: #Editable(x => x.NavigationTitle)</h3>
}
When I hit the preview button the value is displayed, I don't see the value in the PageEditor though. What am I still missing here? Any ideas?
This is just a guess based on the HomePage model that you included in your other post, but I think you need to add an ID property to your model. See http://glass.lu/docs/tutorial/sitecore/tutorial05/tutorial05.html
I see your other post now, please add the Id:
[SitecoreId]
Guid Id{ get; }
In page editor mode, sitecore would require the mapped Id to know which item the value belongs to. So you will need to add this to your model too.
If by any chance you are using TDS or something similar - you'll see that the property is added by default in the base class for all model classes by default.

how does asp.net mvc relate a view to a controller action?

I have opened a sample ASP.NET MVC project.
In HomeController I have created a method (action) named MethodA
public ActionResult MethodA()
{
return View();
}
I have right clicked on MethodA and created a new view called MethodA1
Re-did it and created a new view called MethodA2.
How is this magical relationship done? I looked for the config to tell the compiler that views MethodAX are related to action MethodA, but found none.
What view will the controller return when MethodA is called?
The convention is that if you don't specify a view name, the corresponding view will be the name of the action. So:
public ActionResult MethodA()
{
return View();
}
will render ~/Views/ControllerName/MethodA.cshtml.
But you could also specify a view name:
public ActionResult MethodA()
{
return View("FooBar");
}
and now the ~/Views/ControllerName/FooBar.cshtml view will be rendered.
Or you could even specify a fully qualified view name which is not inside the views folder of the current controller:
public ActionResult MethodA()
{
return View("~/Views/Foo/Baz.cshtml");
}
Now obviously all this assumes Razor as view engine. If you are using WebForms, replace .cshtml with .aspx or .ascx (if you are working with partials).
For example if there is no view it will even tell you where and in what order is looking for views:
Remember: ASP.NET MVC is all about convention over configuration.
The MVC framework use convention over configuration. The framework calls the ExecuteResult on the ViewResult object (created by the return View();). The framework by convention then looks in a number of locations to find a view
If you are using areas the framework will look in the following locations for a view.
/Areas//Views/ControllerName/ViewName.aspx
/Areas//Views/ControllerName/ViewName.ascx
/Areas//Views/Shared/ViewName.aspx
/Areas//Views/Shared/ViewName.ascx
/Areas//Views/ControllerName/ViewName.cshtml
/Areas//Views/ControllerName/ViewName.vbhtml
/Areas//Views/Shared/ViewName.cshtml
/Areas//Views/Shared/ViewName.vbhtml
Without areas (or if you are using areas and no view has been found) the framework will look at the following locations
/Views/ControllerName/ViewName.aspx
/Views/ControllerName/ViewName.ascx
/Views/Shared/ViewName.aspx
/Views/Shared/ViewName.ascx
/Views/ControllerName/ViewName.cshtml
/Views/ControllerName/ViewName.vbhtml
/Views/Shared/ViewName.cshtml
/Views/Shared/ViewName.vbhtml
As soon as the Framework tests a location and finds a file, then the search stops,
and the view that has been found is used to render the response to the client.
There are a number of overriden versions of the View method. The most common one is to render a specific view, outside of the framework convention, by calling it by name. For example
return View("~/Views/AnotherIndex.cshtml");
As an interesting footnote, the framework looks for legacy ASP, C# and VB Razor views (aspx, ascx, cshtml and vbhtml) even though you have a specific view engine.
In MVC controller action is not bound to view.
It uses delegate mechanism to pickup the view.
Model Binding(Mapping)
I was looking for the same and I just made a couple of tests and figured out.
It doesn't save anywhere.
To understand how it works; just do these steps:
In your controller, right click, Add View
Then enter a different View Name
and Ctrl F5
you will get Server error in application.
For example if you right click , Add View in following Index action method and type "Index2" in View name, you will get the error.
public class TestController : Controller
{
// GET: Test
public ActionResult Index()
{
return View();
}
}
So basically there is a 1-1 matching between action name and View name. And you cannot add view for the same method so there is no need to save in a config file.
Now change the view file name in Visual Studio from Index2.cshtml to Index.cshtml then Ctrl+F5. You should see it is working.

Categories