Access Unordered list control id's in codebehind - c#

I have ul elements in usercontrol like below
<u><ul>
<li id="liMiddle" class="off">
<a href="#">One
</a>
<ul id="One" runat="server">
</ul>
</li>
<li id="liMiddle" class="off">
<a href="#">Two
</a>
<ul id="Two" runat="server">
</ul>
</li>
<li id="liMiddle" class="off">
<a href="#">Three
</a>
<ul id="Three" runat="server">
</ul>
foreach (SPWeb supersubsite in subsites)
{
int i = 0;
HtmlGenericControl li = new HtmlGenericControl("li");
CurrentTab.Controls.Add(li);
HtmlGenericControl anchor = new HtmlGenericControl("a");
anchor.Attributes.Add("href", supersubsite.Url);
anchor.InnerText = supersubsite.Title;
li.Controls.Add(anchor);
} </u>
Here in each loop Current tab should be changed to corresponding next ul id. How to access it?
I have to generate 'li' elements dynamically under above ul's. So I need to access all the 'ul' id's one by one in the codebehind.
Can anybody tell me the solution?

I would implement a collection in codebehind where you can add listitems by presenter/controller or in page_load/click_events/..
And then simple looping in an ASP.NET MVC style..
// YourPage.aspx.cs:
private readonly ICollection<string> items = new Collection<string> { "one", "two" };
// YourPage.aspx
<ul>
<% foreach (var stringItem in this.items) { %>
<li><%= stringItem %></li>
<% } %>
</ul>

You could make your own ListBuilder class with an Add method that takes a custom UserListItem class/struct. Inside of these classes you could use TagBuilder to create the LI and UL tags using the custom class/struct you built for the UserListItem. You could store a static dictionary of current Lists you're building in that custom ListBuilder class, using an user defined key.
That way if you needed to get at your List, or ListItems dynamically from the code behind, you could just use your ListBuilder and reference them by ID.
Code below is a bit rough, but here's the general idea:
using System.Collections.Generic;
using System.Web.Mvc;
using System;
public class UserList
{
public List<UserListItem> UserListItems = new List<UserListItem>();
public void Add(UserListItem item)
{
UserListItems.Add(item);
}
}
public class UserListItem
{
public string Name { get; set; }
}
public class ListBuilder
{
static Dictionary<string, UserList> userLists = new Dictionary<string, UserList>();
public ListBuilder(string listId)
{
UserList newList = new UserList();
newList.Add(new UserListItem() { Name = "Item1" });
newList.Add(new UserListItem() { Name = "Item2" });
userLists.Add(listId, newList);
}
public static UserList GetList(string listId)
{
return userLists[listId];
}
public static string BuildList(string listId)
{
UserList list = userLists[listId];
TagBuilder listTagBuilder = new TagBuilder("ul");
list.UserListItems.ForEach(listItem =>
{
TagBuilder listItemTagBuilder = new TagBuilder("li") { InnerHtml = listItem.Name };
listTagBuilder.InnerHtml += listItemTagBuilder.ToString(TagRenderMode.Normal);
});
return listTagBuilder.ToString(TagRenderMode.Normal);
}
}

Related

Getting error foreach statement cannot operate on variables of type 'xyz' because 'xyz' does not contain a public definition for 'GetEnumerator'

I have created a Model based on an EpiServer LinkItemCollection:
namespace ProjectX.Site.Models.Blocks
{
[SiteContentType(
GUID = "b9978bf2-f3da-4164-8fa2-3694c2ce0377",
AvailableInEditMode = false)]
[SiteImageUrl]
public class CustomTopNavigationItemtBlock : SiteBlockData
{
[CultureSpecific]
[MinItemCount(0)]
[MaxItemCount(2)]
[Display(Order = 10)]
public virtual LinkItemCollection CustomTopNavigationLinks { get; set; }
public override int WordCount
{
get => throw new System.NotImplementedException();
set => throw new System.NotImplementedException();
}
}
}
And I am trying to create a shared view for it:
#model CustomTopNavigationItemtBlock
#if (Model != null)
{
#foreach (var linkItem in Model)
{
<li>
<a class=""
href="#Url.PageUrl(linkItem.Href)"
target="#linkItem.Target"
title="#linkItem.Title"
tabindex="1"
data-toggle=""
role="button">
#linkItem.Text
</a>
</li>
}
}
Unfortunatley I don't understand what I am doing wrong.
Here, you are attempting to iterate over Model:
foreach (var linkItem in Model)
However, Model is of type CustomTopNavigationItemtBlock, which cannot be iterated using foreach, as it does not implement IEnumerable.
It seems that you are trying to loop over the CustomTopNavigationLinks property, which can be done like this:
foreach (var linkItem in Model.CustomTopNavigationLinks)

How to combine two Unordered list using for-each

I have two lists in my page which display similar data, i used for-each to display the list. I it possible to display in a single list
My first List
<ul>
#foreach (var item in rpInfo)
{
<li data-id="#item.ID">
#item.PartyName
<ul>
<li> #item.spName </li>
</ul>
</li>
}
My Second List
<ul>
#foreach (var dRPs in deletedRpHistoryInfo)
{
<li data-id="#dRPs.ID">
#dRPs.PartyName
<ul>
<li>#dRPs.spName</li>
</ul>
</li>
}
</ul>
I need your help creating a list by combining the the two?
Thanks!
At its simplest you don't need two lists, you can simply have the two foreach statements inside the one list...
<ul>
foreach(...){
<li>....</li>
}
foreach(...){
<li>....</li>
}
</ul>
As an aside I'm not sure what value you get embedding the second ul inside the li either.
I take it your datatypes are similar but not identical, right? Do they inherit from the same parent - or is it maybe worth making them inherit from same parent(if it brings any other benefits). Then you can create new temporary list, typed with the common parent, and merge in both lists.
When foreaching a list with different datatypes in, when accessing a non-common datamember, you need to check what type the current item is.
Classes:
abstract internal class EmergencyEmployees
{
internal string Name { get; set; }
internal int Age { get; set; }
}
internal class Police : EmergencyEmployees
{
internal int PoliceLicence { get; set; }
}
internal class Firestarter : EmergencyEmployees
{
internal int FirestarterLicence { get; set; }
}
Program:
class Program
{
static void Main(string[] args)
{
var officers = new List<Police>();
var firestarters = new List<Firestarter>();
officers.Add(new Police { Name = "Kjell", Age = 45, PoliceLicence = 12345 });
officers.Add(new Police { Name = "Linda", Age = 42, PoliceLicence = 54321 });
firestarters.Add(new Firestarter { Name = "Arne", Age = 32, FirestarterLicence = 4322 });
firestarters.Add(new Firestarter { Name = "Anna", Age = 35, FirestarterLicence = 3322 });
var templist = new List<EmergencyEmployees>();
templist.AddRange(officers);
templist.AddRange(firestarters);
foreach(var ee in templist)
{
Console.WriteLine("Name: " + ee.Name);
Console.WriteLine("Age: " + ee.Age.ToString());
if(ee is Police)
{
var p = ee as Police;
Console.WriteLine("Police Licence: " + p.PoliceLicence.ToString());
}
if (ee is Firestarter)
{
var p = ee as Firestarter;
Console.WriteLine("Firestarter Licence: " + p.FirestarterLicence.ToString());
}
Console.WriteLine("--------------------");
}
}
}
Output:
Here is an article showing several ways that you can do this.
Given your example you could use the .Concat() as that way you keep the original data untouched and keep this all in the foreach loop. This will only work if the type used in both Lists are the same:
#foreach (var item in rpInfo.Concat(deletedRpHistoryInfo))
{
// do stuff
}

Element targeting where onclick has occurred in Blazor

I have a HTML section with multiple list elements as such:
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown #DropdownClass" #onclick="dropMenu">
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown #DropdownClass" #onclick="dropMenu">
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown #DropdownClass" #onclick="dropMenu">
The goal is that when one of the elements is clicked on, a new class c-show is added which then show the submenu's underneath.
My code in blazor to add this is:
#code{
bool MenuDroppedDown = true;
string DropdownClass => MenuDroppedDown ? "c-show" : "";
public async void dropMenu()
{
MenuDroppedDown = !MenuDroppedDown;
}
}
But the problem here is that once an element is clicked on, then c-show is added everywhere.
How can I have it so only the element where the onclick event occurred is affected?
Copy the code below into the Index component, add this amazing css class
.c-show {
background-color: red;
}
to the site.css located in the wwwroot folder, and run the code
<ul>
#foreach (var item in items)
{
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown #item.DropdownClass" #onclick="#(() => item.MenuDroppedDown = !item.MenuDroppedDown)" >click me</li>
}
</ul>
#code{
List<LiTag> items = Enumerable.Range(1, 10).Select(i => new LiTag { ID = i }).ToList();
public class LiTag
{
public int ID { get; set; }
public bool MenuDroppedDown { get; set; }
public string DropdownClass => MenuDroppedDown ? "c-show" : "";
}
}
Please don't hesitate to ask questions, as this code is rather self-explanatory, I did not bother to explain what I was doing.

Lamba expression in a razor view

Can somebody help me on the Lambda expression.
My ModelVM looks like :
namespace MReports.Models
{
public class FullDetailVM
{
public FullDetailVM()
{
DetailSet = new List<FullDetailSet>();
}
........
public List<FullDetailSet> DetailSet { get; set; }
}
public class FullDetailSet
{
public FullDetailSet(){ }
public string Mnum { get; set; }
public string Label { get; set; }
public string LabelValue { get; set; }
}
}
Data in the above model will be :
DetailSet[0] = {1,"MCity","LosAngeles"}
DetailSet[0] = {1,"MState","California"}
DetailSet[0] = {1,"MZip","90045"}
DetailSet[0] = {1,"MStreet","Cardiff"}
DetailSet[0] = {1,"MHouse No","1234"}
DetailSet[0] = {1,"MApt","1"}
View(Razor) :
#model MReports.Models.FullDetailVM
#if(Model != null)
{
<div class="row contentHeaderInfo">
<ul class="list-inline">
<li> City :
</li>
<li>
//Display LabelValue corresponding to Mcity
Model.DetailSet.select(LabelValue).Where(Label== "Mcity");
</li>
<li> State:
</li>
<li>
//Display LabelValue corresponding to MState
Model.DetailSet.select(LabelValue).Where(Label== "MState");
</li>
</ul>
</div>
}
Model.DetailSet.Where(x=>x.Label == "Mcity").Select(x=>x.LabelValue)
or if you have just one recors Label == Mcity
Model.DetailSet.SingleOrDefault(x=>x.Label == "Mcity").LabelValue
Lambda expressions can be used to create delegate types. I've found the easiest way to explain this to someone is to show them a list of items, such as your List<FullDetailSet> DetailSet, and ask them what items from that list do you want based on a specific condition?
If you wanted all the items with label "Dog" you would do something like this:
Model.DetailSet.Where(d => d.Label == "Dog").Select(d => d.Value);
This will go over the items in DetailSet and check if each item has a Label of "Dog". For lack of a better understanding on the correct terminology, you are iterating over that list and grabbing what you need based on your conditions. This is why I used d as the placeholder, to me it looks as though d is a singluar representation of DetailSet.
If you needed only one record from that DetailSet you would use Single over Where.
Model.DetailSet.Single(d => d.Label == "Dog").Select(d => d.Value);
If you didn't need just the Value of those records that met your conditions you can grab the entire list like this:
Model.DetailSet.Where(d => d.Label == "Dog").ToList();
You need to select one record using Single, then just get the property.
Model.DetailSet.Single(m => m.Label == "MState").LabelValue

asp.net databound menu multilevel

I am currently using an asp.net menu control to load from a table parent/child items. The problem I am having is that if the child has another child. My code is kindof static in that sense and I can't seem to find a better or "the" way to do it. I have seen sitemap as datasources but i don't need a sitemap and feel that would just be overkill for what I need to achieve.
foreach (ClassName option in list)
{
MenuItem module = new MenuItem(option.Description.ToLower(), "", "", option.Url + "?option=" + option.Optionid);
module.Selectable = true;
navigation.Items.Add(module);
//this is my second level
foreach (ClassName child in listfromparent(option.Optionid))
{
MenuItem childmenu = new MenuItem(child.Description.ToLower(), "", "", child.Url + "?option=" + child.Optionid);
module.ChildItems.Add(childmenu);
}
}
as you can see this works but for 2 levels :(
and of course i could put another childlevel inside child to create the 3rd level but what if there is a 4th, 5th? So that's why I need it to do it itself. I noticed treeview has onpopulate but apparently Menu doesn't. Thanks in advance.
Here's one way you could do it.
Represent parent/child relationship in your table with an adjacency list
Map that adjacency list into a tree structure
Convert that tree structure into your structure of menu items
Maybe you could skip that middle step and map the adjacency list straight to a tree of MenuItems, maybe with some extension methods on MenuItem.
But anyway...
Default.aspx
<%# Page Language="C#" Inherits="MenuTreeDemo.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head runat="server">
<title>Default</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Menu ID="MyMenu" runat="server" StaticDisplayLevels="3" />
</form>
</body>
</html>
Default.aspx.cs
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Collections.Generic;
namespace MenuTreeDemo
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
MenuNode root = ConvertTableToTree(GetTreeTable());
foreach (MenuNode topLevelNode in root.Children)
{
MyMenu.Items.Add(topLevelNode.ToMenuItem()); // Visits all nodes in the tree.
}
}
}
// The menu tree as an adjacency list in a table.
static DataTable GetTreeTable()
{
DataTable table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Description", typeof(string));
table.Columns.Add("Url", typeof(string));
table.Columns.Add("ParentId", typeof(int));
table.Rows.Add(1, "TopMenu1", "/foo.html", 0);
table.Rows.Add(2, "SubMenu1.1", "/baz.html", 1);
table.Rows.Add(3, "SubMenu1.2", "/barry.html", 1);
table.Rows.Add(4, "SubMenu1.2.1", "/skeet.html", 3);
table.Rows.Add(5, "TopMenu2", "/bar.html", 0);
table.Rows.Add(6, "TopMenu3", "/bar.html", 0);
table.Rows.Add(7, "SubMenu3.1", "/ack.html", 6);
return table;
}
// See e.g. http://stackoverflow.com/questions/2654627/most-efficient-way-of-creating-tree-from-adjacency-list
// Assuming table is ordered.
static MenuNode ConvertTableToTree(DataTable table)
{
var map = new Dictionary<int, MenuNode>();
map[0] = new MenuNode() { Id = 0 }; // root node
foreach (DataRow row in table.Rows)
{
int nodeId = int.Parse(row["Id"].ToString());
int parentId = int.Parse(row["ParentId"].ToString());
MenuNode newNode = MenuNodeFromDataRow(row);
map[parentId].Children.Add(newNode);
map[nodeId] = newNode;
}
return map[0]; // root node
}
static MenuNode MenuNodeFromDataRow(DataRow row)
{
int nodeId = int.Parse(row["Id"].ToString());
int parentId = int.Parse(row["ParentId"].ToString());
string description = row["Description"].ToString();
string url = row["Url"].ToString();
return new MenuNode() { Id=nodeId, ParentId=parentId, Description=description, Url=url };
}
}
}
MenuNode.cs
using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;
namespace MenuTreeDemo
{
public class MenuNode
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Description { get; set; }
public string Url { get; set; }
public List<MenuNode> Children { get; set; }
public MenuNode ()
{
Children = new List<MenuNode>();
}
// Will visit all descendants and turn them into menu items.
public MenuItem ToMenuItem()
{
MenuItem item = new MenuItem(Description) { NavigateUrl=Url };
foreach (MenuNode child in Children)
{
item.ChildItems.Add(child.ToMenuItem());
}
return item;
}
}
}

Categories