Dynamically Add/Remove Menu Items in ASP.NET? - asp.net

I have a menu control (Menu1) and I want to add/remove items from the menu based on certain information I have stored about the authenticated user in the database. I'm not sure though how to access specific menu items from the menu control and remove them at runtime?

ASP.NET menus can be accessed via code behind. A menu declared in the markup, having the id 'Menu1' for example, could be accessed like so:
foreach (MenuItem item in Menu1.Items) {
if (item.NavigateUrl.Contains(pageName)) {
item.Selected = true;
item.Text = "customText";
}
// ...
}
In that example, the currently selected menu item is chosen according to the current page the menu is on. Just as well, the Items collection may be used to add or remove single menu items.
Note, on menu items, the ChildItems collection can be used to alter the submenu items collection.
More infos: http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.menu.items.aspx
#Edit: made it more coherent with the data in the question

I wish you was more specific but I know that the notification for comments kinda sucks here. so if you could tell me exactly
what is the information you'll Add/Remove items based on ?
how are they're stored in the database (which means are they are stored in one of the membership tables ? in the profile's table ? or a custom table where the user is referenced in ?
what's the technique you imagine in your mind in plain English ?
I'm not sure though how to access
specific menu items from the menu
control and remove them at runtime?
A. Remove items:
Menu1.Items.Remove(Menu1.FindItem("Jobs"))
B. Add Items:
Menu1.Items.Add(new MenuItem("News"))
OR use this to specify the required
properties for the new added item:
MenuItem item = new MenuItem()
item.NavigateUrl =""
item.Text = "Child Test"
Menu1.Items.Add(mnuTestChild)

protected void Page_Load(object sender, EventArgs e)
{
con.ConnectionString = ConfigurationManager.ConnectionStrings["MyConString"].ConnectionString;
string selectCmd = "Select * from Pages where Status='"+1+"'";
SqlDataAdapter dap = new SqlDataAdapter(selectCmd,con);
DataSet ds = new DataSet();
dap.Fill(ds);
if (!Page.IsPostBack)
{
int x = 0;
string parent_id = "";
foreach (DataRow dr in ds.Tables[0].Rows)
{
parent_id = dr[0].ToString();
Menu1.Items.Add(new MenuItem(dr["Title"].ToString(), dr["URL"].ToString(), dr["ID"].ToString()));
}
x++;
}
}

Related

Strange listbox behavior. New item won't show unless another item is added or page reloaded by exiting and returning

Here is what a portion of my screen looks like:
The user can pick a choice from the drop-down list and click the add button. Here is the code for the add button:
protected void btnModuleAdd_Click(object sender, EventArgs e)
{
var selectedModule = ddlModsList.SelectedItem.ToString();
var graphicName = this.GraphicName;
var xr = new GraphicModuleXRef();
xr.GraphicName = graphicName;
xr.Module = selectedModule;
// Take drop down list selection and add it to GraphicModuleXRef table.
var context = new XRefDataContext();
context.GraphicModuleXRefs.InsertOnSubmit(xr);
context.SubmitChanges();
}
Basically, it's taking the user's choice and writing it out to a table. This part works fine.
In my Page_Load, I check whether IsPostback and, if it is, I run the code below:
private void LoadOtherModulesUsed()
{
if (this.GraphicName != null)
{
lbModules.Items.Clear();
var context = new XRefDataContext();
var q = context.GraphicModuleXRefs
.Where(a => a.GraphicName.Contains(this.GraphicName));
foreach (GraphicModuleXRef gr in q)
{
lbModules.Items.Add(new ListItem(gr.Module.ToString()));
}
}
}
This code reads from a table, finds all records that match the criteria, and adds them to the listbox.
So, what I'm expecting to happen is for the page to reload and the listbox to be repopulated, including the new entry just added to the table. But, that isn't happening. The screen refreshes like it has reloaded, but the entry doesn't appear in the listbox. However, it IS there, it just can't be seen. If the user adds another entry, by clicking the Add button, the list 'rolls up' one row and the previous entry can be seen. But, not the new one. If the user exits from the screen and re-enters, all the entries in the listbox can be seen. It's almost like the listbox is too short to display all records, but I've tried different heights, with no difference.
I'm wondering if anyone can point me in the right direction?
Put simply, when adding a new item to the listbox, it isn't immediately visible unless another item is added, thereby 'rolling' the list up. Even scrolling the list with the scrollbar doesn't show the new entry until another entry is added. And, if you scroll the list up, you can see the prior entry. So strange!
EDIT: Trying to describe this more simply:
User adds item to listbox by pressing Add button.
New item does not appear in listbox.
User adds another item to listbox by press Add button.
Prior item now shows in listbox if user scrolls listbox up.
The newest item just added, however, does not appear unless step 3 is repeated.
Also, exiting the page and then coming back in loads every item in the list and all is visible.
This is a timing issue. Whats happening is Page_Load runs first in this case and THEN the Click event handler so effectively the control has been bound before the new entry is added. Thats why you're always one refresh behind. Id refactor your code like this so everything runs in the correct order! To understand the timing of event execution I strongly recommend reading this article on MSDN its AWESOME and will really help you get the best from ASP.NET.
Additionally reading this article on MSDN (Also awesome) especially the section on ViewState will explain how the DropDown retains its details even when, in the modified code, you're onlly filling it when the page is NOT a postback and when the click event is fired.
Hope this helps!
public void Page_Load(object sender, EventArgs e)
{
if (IsPostBack) return;
LoadOtherModulesUsed();
}
private void LoadOtherModulesUsed()
{
if (this.GraphicName != null)
{
lbModules.Items.Clear();
var context = new XRefDataContext();
var q = context.GraphicModuleXRefs
.Where(a => a.GraphicName.Contains(this.GraphicName));
foreach (GraphicModuleXRef gr in q)
{
lbModules.Items.Add(new ListItem(gr.Module.ToString()));
}
}
}
protected void btnModuleAdd_Click(object sender, EventArgs e)
{
var selectedModule = ddlModsList.SelectedItem.ToString();
var graphicName = this.GraphicName;
var xr = new GraphicModuleXRef();
xr.GraphicName = graphicName;
xr.Module = selectedModule;
// Take drop down list selection and add it to GraphicModuleXRef table.
var context = new XRefDataContext();
context.GraphicModuleXRefs.InsertOnSubmit(xr);
context.SubmitChanges();
LoadOtherModulesUsed();
}

Global Permissions-Based Navigation Sharepoint Foundation 2010

I am using SharePoint Foundation 2010 and I would like to create a multilevel navigation. (Similar to what the publishing site allows in SharePoint Standard. I realize that this could probably be built using a custom sitemap provider, which I have looked into.
The kicker here, is that I would like for it only show the pages and sites in which the logged in user has permissions to.
Currently, I have began developing a web part. Inside this web part I have written some c# code to loop through and get all the sites and subpages ( and check if the user has permissions) and then add them to a label. Eventually I would like to add them to either an asp:menu or create a and then use some css or jquery to manipulate it to do the drop down features.
Here is some of my code:
using (SPWeb oWebsite = new SPSite(webUrl).OpenWeb())
{
SPWebCollection collWebsite = oWebsite.Webs;
foreach (SPWeb subSite in collWebsite)
{
if (subSite.DoesUserHavePermissions(SPContext.Current.Web.CurrentUser.LoginName, SPBasePermissions.Open))
{
Label1.Text += SPEncode.HtmlEncode(subSite.Title) + "<BR>";
}
SPList pagelist = subSite.Lists["Site Pages"];
foreach (SPListItem item in pagelist.Items)
{
if (item.DoesUserHavePermissions(SPContext.Current.Web.CurrentUser, SPBasePermissions.Open))
{
Label1.Text += item.Name + "<BR>";
}
}
Label1.Text += "<BR><BR>";
subSite.Close();
}
Label1.Text += "<BR><BR><BR><BR>";
}
}
Instead of adding the site or page to the label, here is where I would like to create the list. The ultimate goal would be to place this web part onto the masterpage and allow for it to give the users some navigation based on permissions.
I also found some css to allow the multilevel navigation to work by adding elements simply as a inside of an The only issue here is that once I placed this onto the masterpage, The 2nd level displayed when I hovered over the first item, but would disappear upon moving my mouse there. (This worked great when the webpart was placed onto a page itself)
Am I on the right track here or is there another method that may work just as well or even better?
TIA.
SPSecurity.RunWithElevatedPrivileges(delegate
{
SPSite oSiteCollection = SPContext.Current.Site;
using (SPWeb oWebsite = new SPSite(webUrl).OpenWeb())
{
SPWebCollection collWebsite = oWebsite.Webs;
foreach (SPWeb subSite in collWebsite)
{
var newItem = new MenuItem();
if (subSite.DoesUserHavePermissions(SPContext.Current.Web.CurrentUser.LoginName, SPBasePermissions.Open))
{
newItem.NavigateUrl = subSite.Url;
newItem.Text = SPEncode.HtmlEncode(subSite.Title);
newItem.Value = SPEncode.HtmlEncode(subSite.Title);
}
SPList pagelist = subSite.Lists["Site Pages"];
foreach (SPListItem item in pagelist.Items)
{
if (item.DoesUserHavePermissions(SPContext.Current.Web.CurrentUser, SPBasePermissions.Open))
{
var subItem = new MenuItem();
subItem.Value = SPEncode.HtmlEncode(item.Name);
subItem.Text = SPEncode.HtmlEncode(item.Name);
subItem.NavigateUrl = item.Url;
newItem.ChildItems.Add(subItem);
}
}
mnNAv.Items.Add(newItem);
subSite.Close();
}
}
});

Possible to created a submenu programmatically in c#?

On my page theres buttons on the top. I would like to add sub menus to those buttons:
protected void AddMenuItems()
{
// Check privledges and add links to admin pages accordingly.
// List adds pages in order of user precedence.
if (_cu.IsUser)
{
NavigationMenu.Items.Add(new MenuItem("Generate UserIDs", "Generate UserIDs", null, "~/GenPrefixList.aspx"));
}
}
Is there a way I can add a sub menu to the item?
Yes, each MenuItem has a ChildItems collection to which you can add an indefinite number of items.
protected void AddMenuItems()
{
// Check privledges and add links to admin pages accordingly.
// List adds pages in order of user precedence.
if (_cu.IsUser)
{
var newItem = new MenuItem("Generate UserIDs", "Generate UserIDs", null, "~/GenPrefixList.aspx");
var subItem = new MenuItem("Sub Menu Item", "Submenu Item", null, "page.aspx");
newItem.ChildItems.Add(subItem);
NavigationMenu.ChildItems.Add(newItem);
}
}
The "Sub Menu Item" will be a child of "Geneate UserIDs", which is a child of the navigation menu.
Update: The Items reference in your code should be ChildItems. Items is the collection used for the Windows application menu; you added the ASP.NET tag so I assume this is a web application.

DevExpress XtraGrid FocusedRowChanged event problem when changing datasource

This problem has bugged me for several years and maybe someone here knows a simple solution, since I just ran into it again.
QUESTION: Is there any way to get the XtraGrid to "forget" the current focused row index before a new (different) datasource is assigned to the grid?
BACKGROUND
We use the XtraGrid as a kind of controller for what is displayed in another panel of a multipane Winform.
Now imagine a hypothetical scenario where the datasource of the XtraGrid keeps changing according to menu selections. Menu item 1 populates the grid with a list of today's entrees in the cafeteria: Id, Name. Menu item 2 populates the grid with a list of Customers the user must phone that day: ID, Name. Important thing is that these are separate distinct datasources, and the grid's datasource is being assigned and reassigned.
CRITICAL FACT FOR THIS QUESTION:
We want the grid's FocusedRowChanged event to be the single place where we trap the user's selection in the controller grid. We are a "no spaghetti code" shop. FocusedRowChanged is better than a click event because it handles keyboard navigation too. The row with the focus contains the ID of the detail record we need to fetch from the database for display in Panel #2. This works--most of the time.
Here's how it doesn't work: let's say that on a given day, the list of customers the user must contact contains only one row. So the first (and only) row in the grid is the focused row. Now let's say that the user goes up to the menu and selects the menu item to display the day's cafeteria entrees. When the user clicks on the first item in the Entrees list, the FocusedRowChanged event does NOT fire because the grid has retained a memory of the focused row index from the previous datasource. The focused row index has not changed. And thus the user's selection doesn't trigger anything.
I tried to get DevExpress to offer a second more row-object-oriented mode (as distinct from row-index-oriented approach) whereby each row in the grid would have a GUID, and the FocusedRowChanged event would fire whenever the GUID of the currently focused row differed from the GUID of the previously focused row, regardless of whether the focused row index happened to be the same. This would allow dynamic changes of datasource and enable the desired behavior. But they demurred.
So I'll ask my question again, Is there any way to get the XtraGrid to "forget" the current focused row index before a new datasource is assigned to the grid?
Tim, I had the exact same problem when the grid only had one row of data in it and then changed data sources. I solved it by setting the gridview.FocusedRowHandle = -1 after setting the new datasource.
In a similar situation, I am subscribing to the
FocusedRowObjectChanged
event (using DevExpress 16.1).
I think that the best solution to this problem is to create a new GridView object and override its DoChangeFocusedRowInternal method. Below you will find the default implementation of this method. All you need to do is to change the marked row just as your needs dictate. Also, take a look at the How to create a GridView descendant class and register it for design-time use article, it contains some useful information.
public class MyGridView : GridView {
protected override void DoChangeFocusedRowInternal(int newRowHandle, bool updateCurrentRow) {
if(this.lockFocusedRowChange != 0) return;
if(!IsValidRowHandle(newRowHandle))
newRowHandle = DevExpress.Data.DataController.InvalidRow;
if(FocusedRowHandle == newRowHandle) return; // <<<<<<
int currentRowHandle = FocusedRowHandle;
BeginLockFocusedRowChange();
try {
DoChangeFocusedRow(FocusedRowHandle, newRowHandle, updateCurrentRow);
}
finally {
EndLockFocusedRowChange();
}
RaiseFocusedRowChanged(currentRowHandle, newRowHandle);
}
}
UPDATE
My code:
namespace MyXtraGrid {
public class MyGridControl : GridControl {
protected override BaseView CreateDefaultView() {
return CreateView("MyGridView");
}
protected override void RegisterAvailableViewsCore(InfoCollection collection) {
base.RegisterAvailableViewsCore(collection);
collection.Add(new MyGridViewInfoRegistrator());
}
}
public class MyGridViewInfoRegistrator : GridInfoRegistrator {
public override string ViewName { get { return "MyGridView"; } }
public override BaseView CreateView(GridControl grid) {
return new MyGridView(grid as GridControl);
}
}
public class MyGridView : GridView {
public MyGridView(GridControl ownerGrid) : base(ownerGrid) { }
public MyGridView() { }
protected virtual bool RowEqual(int focusedRowHandle, int newRowHandle) {
if(IsDesignMode)
return focusedRowHandle == newRowHandle;
DataRow row1 = GetDataRow(focusedRowHandle);
DataRow row2 = GetDataRow(newRowHandle);
return row1 == row2;
}
protected override void DoChangeFocusedRowInternal(int newRowHandle, bool updateCurrentRow) {
if(this.lockFocusedRowChange != 0) return;
if(!IsValidRowHandle(newRowHandle))
newRowHandle = DevExpress.Data.DataController.InvalidRow;
if(RowEqual(FocusedRowHandle, newRowHandle))
return;
int currentRowHandle = FocusedRowHandle;
BeginLockFocusedRowChange();
try {
DoChangeFocusedRow(FocusedRowHandle, newRowHandle, updateCurrentRow);
}
finally {
EndLockFocusedRowChange();
}
RaiseFocusedRowChanged(currentRowHandle, newRowHandle);
}
}
}
You can subscribe on the DataSourceChanged event which will fire when Data source changes (you guessed it!) so then you can get using GetFocusedObject() the object and display the relevant items for the other grid...

Set HTML input checkbox name in listview

I am trying to add a checkbox in a listview with value as ids of the records from the database so I can allow the user to check the ones they want to delete and when they click the delete button I can get value collection of checkbox with request.form.
My problem is, because checkbox in a listview ASP.NET renders the listview name into the name property of the checkbox, it prevents me to do request.form["checkboxname"].
I don't want to use Listviews delete commands but simply use request.form to get the collection of checked values.
How can I set name of the htmlinput checkbox so .NET doesn't change it in render time?
I have tried:
ListViewDataItem dataItem = (ListViewDataItem)e.Item;
HtmlInputCheckBox _CheckBoxDelete = (HtmlInputCheckBox)e.Item.FindControl("CheckBoxDelete");
_CheckBoxDelete.Visible = true;
_CheckBoxDelete.Value = DataBinder.Eval(dataItem.DataItem, "id").ToString();
_CheckBoxDelete.Name = "deletechecked";
But still it renders like:
<input name="PmList$ctrl0$CheckBoxDelete" type="checkbox" id="PmList_ctrl0_CheckBoxDelete" value="3" />
This is happening because ListView is a Naming Container. You can get around this in a couple of ways, but they all boil down to the choice of:
Rendering the HTML you want.
Pulling out the checked items in a different way.
The former is doable, but you'll likely loose a lot of ASP.NET's built in functionality. I'd advise against it unless you're deeply familiar with the control life cycle.
You've got everything you need for the pulling the values out in a way ASP is expecting:
HtmlInputCheckBox _CheckBoxDelete = (HtmlInputCheckBox)item.FindControl("CheckBoxDelete");
You just need to wait for the control hierarchy to be populated, and then loop over the ListView.Items looking for the checkboxes. Your "Delete" button's event handler is probably a good place to call this from.
Incidentally, why are you using a HtmlInputCheckbox, rather than a CheckBox?
I have sorted it out with:
string idCollectionTodelete = string.Empty;
foreach (string x in Request.Form)
{
if (x.IndexOf("CheckBoxDelete") > -1)
{
idCollectionTodelete += Request.Form[x] + ",";
}
}
new DB().DeleteUserPm(
ActiveUsername(), subdomain, idCollectionTodelete.TrimEnd(','));
It is not an ideal solution but it does work for me.
I do this
List<HtmlInputCheckBox> chkDeleteContacts = new List<HtmlInputCheckBox>();
foreach (RepeaterItem item in rptrFamilyContacts.Items)
{
chkDeleteContacts.Add((HtmlInputCheckBox)item.FindControl("chkDeleteContact"));
}
foreach(HtmlInputCheckBox chkDeleteContact in chkDeleteContacts)
{
//Delete Contact
if(chkDeleteContact.Checked)
blnStatus = BusinessUtility.DeleteConsumerContact(LoginConsumerID, chkDeleteContact.Value);
}
Slightly easier in my opinion

Resources