Wednesday, February 15, 2012

Check all ASP CheckBoxes within an ASP Repeater with JavaScript to avoid PostBack

Problem:





I created my own CheckBoxList with an ASP Repeater that contained several ASP CheckBoxes and some labels. I also wanted to have the function to check and uncheck all the checkboxes within the repeater. This was easy to do with an OnCheckedChanged=”SomeCodeBehind” and AutoPostBack=”true” on the design file. This gave a working result but since it made a post back each time, the control flashed for a quick second every time the checkbox was clicked and sometimes even gave me time enough to click on the checkbox multiple times. In other words, it gave an impression of a slow application.

The next problem appeared when this controlwas inserted in to an AJAX Modal Popup window. Whenever the control made a postback the modal popup was closed. A solution would be to make an EventHandler for each time the checkbox is clicked, in that way the event could be caught within the parent control and trigger a function to reopen the modal popup again. But since this event is not needed in any other situation this solution felt a bit to much.

Solution

Javascript! But the first question that arised was "How to get hold of all the elements within a repeater?" Well it is not that complicated actually. First of let me show the design of my repeater:

HTML:




<div id="Div_CheckBoxList">
<asp:repeater id="Repeater_CheckBoxList" runat="server">
<headertemplate>
<table id="Table_CheckBoxList">
<tr>
<th>
<asp:checkbox id="CheckBox_CheckAll" runat="server"></asp:checkbox>
</th>
<th>
<asp:label id="Label_CheckAllText" runat="server" text="Check/Uncheck all"></asp:label>
</th>
</tr>
</headertemplate>
<itemtemplate>
<tr>
<td>
<asp:checkbox id="CheckBox_RepeaterItem"></asp:checkbox>
</td>
<td>
<asp:label id="Label_CheckBoxText" runat="server"></asp:label>
</td>
</tr>
</itemtemplate>
</asp:repeater>
</div>




And now to the magic! Here is the javascript function checkAll that checks and unchecks all the checkboxes within the repeater. It obtains the client ID (since we are working with ASP controls) of the element that contains all the checkboxes. It could be a panel or a div, but in this case we use the repeaters client ID.

All the elements within this "container" are then extracted and iterated through. What we are looking for is an element with an ID that contains the word "checkbox". And we also know that the first checkbox that we will find (since it is furtherest up in the page) is the "check/uncheck all"-checkbox. The interesting thing about that checkbox is to know if it is checked or unchecked to be able to check the rest equally. And that would be all!

NOTE! You maybe ask yourself why we do not get all the elements from the document, document.getElementsByTagName('*') instead? Well that is because sometimes there might be other checkboxes on the page, or in my case I want to be able to introduce two repeaters on the same page, without clicking the "check/uncheck all"-checkbox on one repeater also affects the other one.

JavaScript



function checkAll(containerClientID) {
var repeater = document.getElementById(containerClientID);
var all = repeater.getElementsByTagName('*');
var found = false;
var isChecked = true;
for (var i = 1; i < all.length; i++) {
var e = all[i];
if (e.id.toUpperCase().indexOf("CHECKBOX") != -1) {
if (found) {
e.checked = isChecked;
}
else {
isChecked = e.checked;
found = true;
}}}}


To be able to use this javascript following change must be done on the HTML code:

HTML



<asp:CheckBox id=&quot;CheckBox_CheckAll&quot; ...
onClick='<%# "javascript:checkAll(\"" + this.Div_AlternatingTable.ClientID + "\");" %>'
... />


The following code is for making only one checkbox in the repeater to be able to be selected simultaniously.

JavaScript



function checkOne(containerClientID, checkboxIndex) {
var repeater = document.getElementById(containerClientID);
var all = repeater.getElementsByTagName('*');
var currentCheckboxIndex = 0;
for (var i = 1; i < all.length; i++) {
var e = all[i];
if (e.id.toUpperCase().indexOf("CHECKBOX") != -1) {
if (currentCheckboxIndex != checkboxIndex) {
e.checked = false;
}
currentCheckboxIndex++;
}}}


And the following changes need to be done on the HTML

HTML



<asp:CheckBox id=&quot;CheckBox_RepeaterItem&quot; ...
onClick='<%# "javascript:checkOne(\"" + this.Div_AlternatingTable.ClientID + "\",\"" + Container.ItemIndex + "\");" %>'
... />

Wednesday, February 8, 2012

Quick tip: PopupControlExtender displays popup in wrong position



When using multiple PopupControlExtenders in a page the positioning of the popup panels might get a little bit shifted, as shown in the filter example. (Using Position="Bottom")







One way to make sure this doesn't happen is to always set a css-class with the style "display: none;" on all panels that will be used as popups. This will fix the alignment, and will also remove the flicker of the control when loading the page:

Using System Center Orchestrator 2012 to monitor folders

One big drawback with the existing "Monitor Folder" activity in System Center Orchestrator 2012 RC is that it will only trigger on files dropped while your runbook is actually running. That means that if you temporarily stop your runbook to do an adjustment, all files that some other system drops in your folder while doing this will be ignored when you start your runbook again.

It's pretty easy to avoid this by instead scheduling a powershell script that returns all files in your folder.
Here's a simple example:

Start by adding a Monitor Date/Time activity and set an appropriate polling interval. I'll set mine to 1 minute.


Next add a "Run .Net Script" activity and link that to the monitor acitivity. Select PowerShell as language and paste this into the script window (modify it to your own lab folder). The *copy* file mask is by the way a handy tip when developing these kind of workflows. Using this you will be able to keep a file in your dev directory and just copy-paste it in the same directory to kick your workflow.

$FileName = (Get-ChildItem c:\temp\lab\*copy*.xml) | Select-Object FullName
$Count=@(Get-ChildItem c:\temp\lab\*copy*.xml).Count

The $Count parameter will be used to decide whether or not to continue and the $FileName parameter will be used to perform some kind of operations on that file.



Now set the data that will be published:


Now you're all set to continue building your runbook. In my example i will just copy the file to another location. On the link set the following condition




Now add the copy file activity. 

As you may note I do a little magic on the FileName parameter…Here’s a better view on that




This is what my runbook looks like. I also log when no files are found in this example (set NumberOfFiles equals 0 on the link if you want to do that as well)