How would you guys conditionally disable checkboxes in an asp treeview?
For instance, if an application user does not have a certain permission, disable that permission entry checkbox in a permissions treeview.
Here's what i'm looking for, this is the equivaqlent in a winform app (the checkboxes are disabled where the text is grayed out):
I saw other solutions where the click event on the checkboxes is intercepted and ignored. I would prefer a solution where the checkboxes are simply set to disabled.
I'm looking for a C# solution but will be happy with a C#/Javascript solution.
Thanks!
Ok, found a fairly clean solution to this:
in code-behind:
TreeNode newNode = new TreeNode(permission.ToString());
newNode.SelectAction = TreeNodeSelectAction.None; // no Link
if (shouldDisableCheckbox)
{
// Set a class so disabled nodes can be formatted thru CSS
// and be identifiable as disabled in Javascript.
newNode.Text = "<span class=disabledTreeviewNode>" + newNode.Text +"</span>";
}
nodes.Add (newNode);
in Javascript, scan all treeview nodes for those that have that className and disable the checkboxes associated to them:
// Called via a startup script created in Code Behind.
// Disables all treeview checkboxes that have a text with a class=disabledTreeviewNode.
// treeviewID is the ClientID of the treeView
function DisableCheckBoxes(treeviewID)
{
TREEVIEW_ID = treeviewID;
var treeView = document.getElementById(TREEVIEW_ID);
if (treeView)
{
var childCheckBoxes = treeView.getElementsByTagName("input");
for (var i = 0; i < childCheckBoxes.length; i++)
{
var textSpan = GetCheckBoxTextSpan(childCheckBoxes[i]);
if (textSpan.firstChild)
if (textSpan.firstChild.className == "disabledTreeviewNode")
childCheckBoxes[i].disabled = true;
}
}
}
function GetCheckBoxTextSpan(checkBox)
{
// Set label text to node name
var parentDiv = checkBox.parentNode;
var nodeSpan = parentDiv.getElementsByTagName("span");
return nodeSpan[0];
}
Sadly, I don't have enough reputation to be able to comment directly on zukanta's answer which is a bit of a pain, but I had to make a modification in the javascript to make this work:
if (textSpan.firstChild)
if (textSpan.className == "disabledTreeviewNode")
childCheckBoxes[i].disabled = true;
i.e. replace textSpan.firstChild.ClassName with textSpan.ClassName
Also worth pointing out that the JavaScript will error out unless all of your tree nodes in the treeview that you are addressing have a
<span></span>
in them. You get a null reference at
if (textSpan.firstChild)
and no subsequent nodes are processed.
I got around this point by adding a span with class=enabledTreeviewNode to all tree nodes that I didn't want disabled.
You could also handle the exception in the JavaScript, I guess.
Hope this helps someone who stumbles across this (otherwise excellent) solution later on.
You could use security trimming to not show items that the user doesn't have access to. I don't know of any way to have the items displayed but not active. Disabling checkboxes on the client side only could create a security hole.
Walkthrough: Filtering Site-Map Nodes Based on Security Roles
ASP.NET Site-Map Security Trimming
The OP was looking for conditional disable but I just want to use the TreeView to display historical audit data, logs of when items were switched on.
All the checkboxes on my page should be disabled. It took me some time to find this elegant jQuery solution. I hope it helps anyone with a similar issue.
Add the code below to your script section. As all input boxes will be disabled, there's no need to make any changes at all to your codebehind.
<script type="text/javascript">
$(document).ready(function () {
$("input:checkbox").each(function () {
$(this).prop('disabled', true);
});
});
</script>
Related
Does anyone know if there is a way within Kentico CMS to nest a webpart within another webpart? I did a little research and I don't see many results on the topic so it seems the short answer is no, but maybe there is a workaround? On the project I am working on it is a requirement that the content author is able to place a hamburger menu on the page and then add other content within that hamburger menu via drag and drop. How can this be achieved within Kentico?
Thanks.
Typically a web part consists of user controls and not nested web parts. Take a look at the CMSRepeater for instance. It sets properties of a user control created by Kentico.
For what you're explaining it seems like you want a custom web part with widget zones in it. Those widget zones will allow you to drag and drop content in place (what content I have no idea really because you can't drag and drop content in Kentico, only areas/widgets).
For navigation, you might want to look at creating it based on the content tree but maybe a hamburger menu is not for navigation in your case.
The solution was to add a widget zone within the web-part.
Circling back to this post. Although adding a <cms:CMSEditableRegion /> tag to the page did indeed allow me to nest a Widget within a Webpart zone, it seemed a bit unnecessary for the user to have to switch between the design tab and page tab in order to achieve adding a nested component, not to mention having to register every webpart as a widget; quite unnecessary. I noticed Kentico's built in Layout webpart allowed for nested webparts so I looked at the code and was finally able to figure out how to implemented nesting a webpart within another webpart!
Ensure your webpart's code-behind inherits from CMSAbstractLayoutWebPart
Add the following method to your code-behind:
protected override void PrepareLayout()
{
StartLayout();
Append("<div");
Append(" style=\"width: ", "100%", "\"");
if (IsDesign)
{
Append(" id=\"", ShortClientID, "_env\">");
Append("<table class=\"LayoutTable\" cellspacing=\"0\" style=\"width: 100%;\">");
if (ViewModeIsDesign())
{
Append("<tr><td class=\"LayoutHeader\" colspan=\"2\">");
// Add header container
AddHeaderContainer();
Append("</td></tr>");
}
Append("<tr><td id=\"", ShortClientID, "_info\" style=\"width: 100%;\">");
}
else
{
Append(">");
}
// Add the tabs
var acc = new CMSAccordion();
acc.ID = ID + "acc";
AddControl(acc);
if (IsDesign)
{
Append("</td>");
if (AllowDesignMode)
{
// Width resizer
Append("<td class=\"HorizontalResizer\" onmousedown=\"" + GetHorizontalResizerScript("env", "Width", false, "info") + " return false;\"> </td>");
}
Append("</tr>");
}
// Pane headers
string[] headers = TextHelper.EnsureLineEndings("HEADER", "\n").Split('\n');
// Create new pane
var pane = new CMSAccordionPane();
pane.ID = ID + "pane";
pane.Header = new TextTransformationTemplate(string.Empty);
acc.Panes.Add(pane);
pane.WebPartZone = AddZone(ID + "-ContentArea", ID + "-ContentArea", pane.ContentContainer);
acc.SelectedIndex = 1;
if (IsDesign)
{
if (AllowDesignMode)
{
Append("<tr><td class=\"LayoutFooter cms-bootstrap\" colspan=\"2\"><div class=\"LayoutFooterContent\">");
// Pane actions
Append("<div class=\"LayoutLeftActions\">");
Append("</div></div></td></tr>");
}
Append("</table>");
}
Append("</div>");
FinishLayout();
}
hi i am applying css to one of my list elements which is inside a tag which is in a master page. so when i click this link the style is getting applied but the page reloads and then the applied style is again reset to default this is my code can somebody help me with this? please?
function SelectThis(ctrl) {
debugger;
var list = document.getElementById("myslidemenu").getElementsByTagName('a');
for (i = 0; i < list.length; i++) {
list[i].style.color = "white";
list[i].style.background = "#414141";
}
ctrl.style.background = "black";
ctrl.style.color = 'yellow';
}
but since i am clicking on a hyper link. the master page reloads and the selected styles are lost
Since it is a hyper link is residing inside the <li> use
e.preventDefault(); will stop the default action of the <a> tag
Update:
list[i].addEventListener('click',preventReload);
function preventReload(e){
e.preventDefault();
}
Add this code inside the for loop, which will stop the default functionality of the hyperlink i.e., stops the page from reloading.
If you need the page to reload, because it's a menu to sub pages then the way I do this is to:
Define a class for the style
Compare the current page url to the a href url (various ways to do this and various levels of matching)
If they match apply the class to that element
I would do this server side, not with javascript.
Another option is to use a cookie/session variable, not sure on the C# syntax, I did this recently in PHP and used AJAX to set a session variable after filtering some data to remember the current state when clicking down to the next level.
I have 4 server side ListBox controls. All of them have their Enabled property set to false, yet when rendered they are definitely enabled. They are all multiple select. These have no data binding or any code behind touching them. Below is the markup for all of them (save the ID). I am running v4 of the .NET Framework with IIS6.
<asp:ListBox runat="server" ID="lstProduct" Enabled="false" SelectionMode="Multiple" Rows="6"></asp:ListBox>
Here is the markup that is generated by the runtime:
<select size="6" name="ctl00$ctl00$MainContent$MainContent$lstProduct" multiple="multiple" id="MainContent_MainContent_lstProduct" class="aspNetDisabled">
I found a solution. In the <system.web> section of web.config, you must add <pages controlRenderingCompatibilityVersion="3.5">.
With Asp.net 4.0, any control that does not take specific user input (textbox or password), will not be rendered with a disabled="disabled" attribute when Control.Enabled = false is set.
Try this:
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this.lstProduct.Attributes.Add("disabled", "");
}
}
To remove it you can just remove the disabled tag like this:
this.lstProduct.Attributes.Remove("disabled");
Write the following line in the .cs file
ListBox.Attributes.Add("disabled", "true");
A better solution is to inherit from the ListBox class and then override the SupportsDisabledAttribute property. Detailed information can be found in MSDN library
e.g.
public class MyListBox : ListBox
{
public override bool SupportsDisabledAttribute { get { return true; } }
}
This should be considered a bug in the .Net Framework.
http://www.asp.net/whitepapers/aspnet4/breaking-changes#0.1__Toc256770141 says:
Controls that are not designed for user input (for example, the Label control) no longer render the disabled="disabled" attribute if their Enabled property is set to false (or if they inherit this setting from a container control).
Also see rationale for the change (rendering valid html) at http://msdn.microsoft.com/en-us/library/system.web.ui.control.renderingcompatibility.aspx.
But a list box is designed for user input and the disbled attribute is supported in html, so it ought to render disabled="disabled".
You can use a little jquery as a bandaid until this is properly fixed. If you put this somewhere that's run for all pages it will fix it for all disabled listboxes on all pages:
$(document).ready(function () {
$("select.aspNetDisabled").attr('disabled', 'disabled');
});
You may wish to instead disable the options within the select box, as this will allow scrolling.
//Listbox cannot be disabled directly, instead the inners should be disabled instead.
foreach(ListItem item in lbCategory.Items)
{
item.Attributes.Add("disabled", "disabled");
if (item.Selected)
{
//cannot reliably style with [disabled='disabled'][selected='selected'] or :checked:selected etc, so need a class
item.Attributes.Add("class", "disabledSelected");
}
}
I then use the following CSS, so the user can still see preselected items.
/* Slightly lighter colour than the normal #3399FF because you cannot change the foreground color in IE, so means that it isn't contrasted enough */
select option.disabledSelected { background-color: #97cbff !important}
Unfortunately from my initial investigations it's a bit of a pain to style disabled input elements in a nice cross browser way. I've setteled with using a class for my purposes, however this article regarding styling disabled form elements might help.
You may also notice that in IE, click events will still be triggered, which seemed to deselect the options but only in some combinations of trying to use [disabled='disabled'][selected='selected'] or :checked:selected etc.
I had the same problem but with CheckBoxList.
Setting its Enabled property to false didn't disable it. The panel it was inside of would also not have an effect on it when Enabled = false.
The solution was to use a foreach loop over the items in the CheckBoxList.
foreach (var item in checkBoxList.Items.Cast<ListItem>())
{
item.Enabled = false;
}
Simple one here... is there a clean way of preventing a user from double-clicking a button in a web form and thus causing duplicate events to fire?
If I had a comment form for example and the user types in "this is my comment" and clicks submit, the comment is shown below... however if they double-click, triple-click or just go nuts on the keyboard they can cause multiple versions to be posted.
Client-side I could quite easily disable the button onclick - but I prefer server-side solutions to things like this :)
Is there a postback timeout per viewstate that can be set for example?
Thanks
I dont think that you should be loading the server for trivial tasks like these. You could try some thing jquery UI blocking solution like this one. Microsoft Ajax toolkit should also have some control which does the same. I had used it a long time ago, cant seem to recall the control name though.
With jQuery you can make use of the one event.
Another interesting read is this: Build Your ASP.NET Pages on a Richer Bedrock.
Set a session variable when the user enters the page like Session["FormXYZSubmitted"]=false.
When the form is submitted check that variable like
if((bool) Session["FormXYZSubmitted"] == false) {
// save to db
Session["FormXYZSubmitted"] = true;
}
Client side can be tricky if you are using Asp.Net validation.
If you have a master page, put this in the master page:
void IterateThroughControls(Control parent)
{
foreach (Control SelectedButton in parent.Controls)
{
if (SelectedButton is Button)
{
((Button)SelectedButton).Attributes.Add("onclick", " this.disabled = true; " + Page.ClientScript.GetPostBackEventReference(((Button)SelectedButton), null) + ";");
}
if (SelectedButton.Controls.Count > 0)
{
IterateThroughControls(SelectedButton);
}
}
}
Then add this to the master page Page_Load:
IterateThroughControls(this);
I have had the same scenario. The solution of one of my coworkers was to implement a kind of Timer in Javascript, to avoid considering the second click as a click.
Hope that helps,
Disable the button on click, utilize jquery or microsoft ajax toolkit.
Depending on how important this is to you, could create an array of one time GUID's which you remove from the array once the update has been processed (ie posted back in viewstate/hidden field)
If the guid is not in the array on postback, the request is invalid.
Substitute database table for array in a clustered environment.
In my current asp.net-mvc project one of my pages allows the user to select a value in a dropdown box after wich a post request is made that updates several values.
To make sure the delay from the postback doesn't confuse the user into selecting another value (and thus creating another post, creating another delay etc) I set the select's disabled attribute to true.
Disabled inputs aren't submitted to the post call however.
How can I make it visually clear to the user that work is in progress and make it imposible to select a new value without removing the input from the post?
Yes, this annoys me too.
Basically what you need to do is hide the old button and replace it with a disabled one so it looks the same to the user. That way it's still submitted but can't be doubly submitted.
Actually I've found what seems to be a duplicate of this at Problem with disabling submit buttons on form submit.
From your answer, I gather you are already using jQuery. In that case why don't you get the value of the select box, disable it, then post the value yourself?
Bonus : BlockUI is a nice jQuery plugin to, well, block the UI.
None of the answers I found in Cletus' post was entirely what I was looking for.
Here is what I came up with. It's not 100% reusable, but it does what I need and feel free to improve/edit.
$('#productMixSelectorForm').change(function() { $(this).ChangeSelection() });
jQuery.fn.ChangeSelection = function() {
var html = $('<div class="hidden">');
$(this).find('select, input').each(function() {
if ($(this).hasClass('hidden') == false) {
//Clone the original one into the hidden div
html.append($(this).clone());
//Disable the original (visible) one and make it's name unique again
$(this).attr("disabled", true);
var name = $(this).attr("name");
$(this).attr("name", name + "disabledDummy");
}
});
//Add the collection of clones to the form so they get submitted
$(this).append(html);
$(this).submit();
}