asp.net mvc3 and multi-level navigation - asp.net-mvc-3

i am writing new asp.net mvc application and i have question about creating multi-level navigation system.
For example i have web with main navigation (Cpu --- Gpu ---- Ram)
and sub-navigation with (intel,amd --- ati,nvidia --- DDR2,DD3)
Well, my first implementation is here :
public class NavigationItem
{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual string Controller { get; set; }
public virtual string Action { get; set; }
public virtual string Url { get; set; }
public virtual string Section { get; set; }
}
public class NavigationController : Controller
{
private readonly IUnitOfWork _unitOfWork;
public NavigationController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public ActionResult MainMenu()
{
return View(_unitOfWork.NavigationItems.Where(x => x.Section == null).ToList());
}
public ActionResult SectionMenu()
{
return View(_unitOfWork.NavigationItems.Where(x => x.Section == "// name of section").ToLis());
}
}
And finally my layout page is :
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
</head>
<body>
<div class="main-menu">
#{Html.RenderAction("MainMenu", "Navigation");}
</div>
<div class="section-menu">
#{Html.RenderAction("SectionMenu", "Navigation");}
</div>
#RenderBody()
</body>
</html>
With this implementation i have problem how i handle in SectionMenu with MainMenu is active, because i want generate SectionMenu depends on MainMenu and highlight them.
One workaround of witch i think is handle url(controller) in SectionMenu.
For example :
if (RouteData.Values["controller"].ToString() == "Administration")
{
// Generate section menu for Administration main menu
}
I dont like this solution because i work with "magic string" values and i havent only one controller per one MainMenu.
How implement this solution?
Thanks for advice

I guess you have to get deeper into MVC's routing system. It doesn't look very straightforward in the beginning, but this is probably the most important area of the entire MVC. Sometimes though you can easily get confused especially if you have many routes defined in Global.asax. There is a project called Route debugger, it helps. I don't remember the link. Try to google it... If you can't find it I'll send the link later...

Ok, I use ViewBag feature. Not clean, but works

Related

ASP .NET Core Razor: Model bound complex types must not be abstract or value types and must have a parameterless constructor

If I have a property like this in my model:
[BindProperty]
public IPagedList<Product> Products { get; set; }
then when I try to post, I get this error:
An unhandled exception occurred while processing the request.
InvalidOperationException: Could not create an instance of type 'X.PagedList.IPagedList`1[Data.Models.Product, Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, set the 'Products' property to a non-null value in the 'Areas.Catalog.Pages.ProductListModel' constructor.
The error says I can set the property to a non-null value in the constructor, so I try to do this in the constructor:
Products = new PagedList<Product>(Enumerable.Empty<Product>(), 1, 10);
But I get the same error.
When I remove [BindProperty] it works. I was under the impression I needed that to bind a property on a Razor page, but I guess not?
If a new Razor Pages project is created and the following amends made it works fine:
Product.cs:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
Index.cshtml:
#page
#using X.PagedList;
#using X.PagedList.Mvc.Core;
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about building Web apps with ASP.NET Core.</p>
</div>
#{
foreach (var item in Model.Products)
{
<div> #item.Name</div>
}
}
#Html.PagedListPager((IPagedList)Model.Products, page => Url.Action("Index", new { page }))
Index.cshtml.cs
public class IndexModel : PageModel
{
public IndexModel()
{
Products = new PagedList<Product>(Enumerable.Empty<Product>(), 1, 10);
}
[BindProperty]
public IPagedList<Product> Products { get; set; }
public void OnGet()
{
}
}
As such I suspect the issue to be with complexity in your Product class which you have not provided the code for.
To verify that, use a temporary simple Product class (like that in my example) as a test.
Once confirmed, try projecting the product class to a simpler class using Automapper or linq's Select method and see if that helps:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/basic-linq-query-operations#selecting-projections
http://docs.automapper.org/en/stable/Projection.html

How to get Asp.net Core Identity User in View

In Asp.net core mvc project, how to get the current IdentityUser information in View ?
What #rdhainaut said works OK if you just want to display the username on the View. If that's all you need, you can even use the following without the needs of injecting anything!
#if (User.Identity.IsAuthenticated)
{
<div>Hello, #User.Identity.Name</div>
}
The reason is, with the default configuration (assuming you're using username), the username is stored in #User.Identity.Name after successful signin.
Now if you do want to display additional information about the logged in user, such as display name on your site top navigation, that's when I think ViewComponent` comes into play!
For example, let's say you have defined an Admin area in your MVC project and you want to create a top navigation bar.
You create a folded called ViewComponents under Admin folder.
Create a view component called TopNavbarViewComponent.cs.
public class TopNavbarViewComponent : ViewComponent
{
private readonly UserManager<IdentityUser> _userManager;
// You can inject anything you want here
public TopNavbarViewComponent(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var appUser = await _userManager.FindByNameAsync(
HttpContext.User.Identity.Name);
var vm = new TopNavbarViewModel
{
// Just as an example... perhaps you have additional
// property like FirstName and LastName in your IdentityUser.
DisplayName = appUser?.DisplayName,
Email = appUser?.Email,
Phone = appUser?.PhoneNumber
};
return View(vm);
}
}
Define the model behind the view undler ViewComponents\Models.
public class TopNavbarViewModel
{
public string DisplayName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
Define the view for the view component, which needs to be under Areas\Admin\Views\Shared\Components\TopNavbar\Default.cshtml by convention.
#model Web.UI.Areas.Admin.ViewComponents.Models.TopNavbarViewModel
<nav class="navbar navbar-light">
<ul class="navbar-nav">
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown">
Hello, #Model.DisplayName
</a>
</ul>
</nav>
In a web application asp.net core mvc 2.0, You can have the IdentityUser object directly available in the view with the following code :
#using Microsoft.AspNetCore.Identity
#inject SignInManager<IdentityUser> SignInManager
#inject UserManager<IdentityUser> UserManager
#if (SignInManager.IsSignedIn(User))
{
<div>Hello #UserManager.GetUserName(User)!</div>
}

MVC 3 / Entity Framework: Binding Collections

I have 2 models, employee and person:
public class Employee
{
[Key]
public int Id { get; set; }
public int? PersonId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Person { get; set; }
}
public class Person
{
public IList<PhoneNumber> PhoneNumbers { get; set; }
public int Id { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public Person()
{
PhoneNumbers = new List<PhoneNumber>
{
new PhoneNumber()
};
}
}
Editor Template for Phone:
#Html.TextBoxFor(x => x.Number)
#Html.DropDownListFor(m => m, new SelectList(Enum.GetNames(typeof (WebMVC.Core.Common.PhoneType))))
To reduce clutter, I removed the other (non-pertinent) properties.
The difficulty I am having is while in the Employee Create(), I can bind the person FName & LName, I cannot bind the PhoneNumbers collection.
I know about the 2008 Haack blog but I do not think it mirrors this situation.
Does anyone know a solution to bind the person phone numbers collection in the employee's Create()?
I'm not exactly sure if PhoneNumber is a custom class that you created, or one that is built into the framework. But if you're having problems with MVC3 mapping posted data to the Employee class like you specified, you might want to look at creating a custom binding. Keep in mind that if your editor template code is incorrect this wont really matter, so I would take a look at that using fiddler first.
Here are a few good sites to get you started, I found them all on SO at one point.
http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx
http://odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx
http://www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx
Creating a custom binder gives you complete control over the way that MVC parses your posted model data and populates the object. There are 2 main functions that most people override, CreateModel and BindModel. BindModel is the function you will most likely want to override if this is the way you would like to go.
I don't know what the html from the editor template looks like, but to bind to a collection of custom types it should look something like this:
<input name="[0].Number">
<input name="[0].PhoneType">
<input name="[1].Number">
<input name="[1].PhoneType">
<input name="[2].Number">
<input name="[2].PhoneType">

Ignoring properties when serializing

I'm pulling my hair out on this one.
I am trying to implement a multi-step wizard, and i'm using the Html.Serialize html helper in MVC3 Futures. This works well, except one of the properties in my model is a SelectList. I don't want this property serialized (and it blows up when it tries anyways).
I can't use [NonSerialized] because that only works on fields, not properties. I've even tried some of the other normal ways such as [XmlIgnore] (which I didn't think would work anyways).
Can anyone suggest an attribute that will ignore a property in a model when using Html.Serialize?
EDIT:
The error I get when I try to serialize is a InvalidDataContractException. There is this message:
Type 'System.Web.Mvc.SelectList' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
However, if I do this then I have to mark all the members with [DataMember] just to exclude 1 property, which seems kind of stupid.
UPDATE:
A quick example of this is this bit of code (make sure to add reference to System.Runtime.Serialization.dll):
Test.cs
[Serializable]
public class Test
{
public int ID { get; set; }
[IgnoreDataMember]
public SelectList TestList { get; set; }
}
HomeController.cs
public ActionResult About()
{
return View(new Test() { ID = 0, TestList = new SelectList(new [] {""})});
}
Home/About.cshtml
#using Microsoft.Web.Mvc
#model MvcApplication3.Models.Test
#Html.Serialize("Test", Model)
This generates the InvalidDataContractException
public class MyViewModel
{
[IgnoreDataMember]
public SelectList Items { get; set; }
...
}
or simply:
public class MyViewModel
{
public IEnumerable<SelectListItem> Items { get; set; }
...
}

How to add images as details in a ASP MVC 3 Razor View using Entity Framework

For an ongoing project i have (amongst other classes) the following:
public class Page
{
[Key]
public int PageId { get; set; }
public string Name { get; set; } //eg. "AboutUs", "Location"
[Column(TypeName = "ntext")] //force Entity Framework to create a ntext column
public string Title { get; set; }
public string Subtitle { get; set; }
public string Content { get; set; }
//navigational properties
public virtual ObservableCollection<Image> Images{ get; set; } //one Page has many Images
public Page()
{
Images= new ObservableCollection<Image>();
}
}
I'm using Entity Framework code first approach in this ASP MVC 3 project (using Razor) and do not have any problem inserting and updating objects of this type.
BUT: how can i have a master detail view in which the detail part is composed by images only (see class definition).
So how is it possible to add an image, if the user doesn't want it to have it deleted and of course how to show all the images in a list?
Any hint is deeply appreciated!
Look at this post: Display image from database in asp mvc
If you are trying to render from the database, create a method in your controller to get the image and use that as your image source like in the example above. The only difference is yours will be contained in a
<ul>
#foreach(var image in Model.Images) {
<li><img src="#Url.Action("Show", "Image", new {id = image.Id})" /></li>
}
</ul>
Then your model would contain the Id's of the images, and it would be the job of the action method to retrieve the image.

Resources