Thursday, November 26, 2009

Read Page.IsValid from javascript (alternate way)

This was my scenario:
In a form there are few fields and 2 buttons :"Save" and "Cancel"
If user presses save button, and if the form validation is true, then immediately disable the "Cancel" button, so that the user cannot press the "Cancel" button. Here is the form:















"Name" field is a required field. So there is a "RequiredFieldValidator" attached with it. I have to disable the cancel button from javascript if:
1. User presses "Save" button.
2. Name field has some value (page validation passed).

To do this, I had to add a customvalidator with the name field, and do the javascript coding inside its client side function. Here is the code for the field and the validators:

<td>
<asp:TextBox ID="uxName" runat="server" MaxLength="50" width="180px"></asp:TextBox>

<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="uxName" SetFocusOnError="true"
ErrorMessage="Department name cannot be empty" ValidationGroup="DepartmentEditValidation">*</asp:RequiredFieldValidator>

<asp:CustomValidator runat="server" ID="uxCustomValidator" ClientValidationFunction="checkNames" ControlToValidate="uxName" ValidationGroup="DepartmentEditValidation" ErrorMessage="" SetFocusOnError="True" Display="Dynamic">
</asp:CustomValidator>

</td>

Here is the code for Save, and Cancel button. Validation Group has to be same for the Save button.

<div class="editPanelButton">
<asp:Button ID="uxSave" runat="server" Text="<%$Resources:Texts,Save %>" ValidationGroup="DepartmentEditValidation"
OnClick="uxSave_Click" />
<asp:Button ID="uxCancel" runat="server" Text="<%$Resources:Texts,Cancel %>" OnClick="uxCancel_Click" />
<</div>

Now we need to do the javascript checking inside the clientside function of the customfield validator. We declared: ClientValidationFunction="checkNames", so here is the function:

<script type="text/javascript" >
function checkNames(source, args){

if(args.IsValid){

if(event.srcElement.id==uxSaveButtonClientId){
document.getElementById(uxCancelButtonClientId).disabled=true;
}
args.isValid=true;
}
}

</script>

args.IsValid is the alternate way of checking whether the page validation is true or not. Then I checked the validation was fired by pressing the "save" button. validation also fires when focus goes out from the "uxname" text field. So, I needed to be specific. If the conditions pass the I disabled the cancel button. This solved my problem.

Tuesday, August 25, 2009

Disable asynchronous postbacks while one asynchronous postback is active.

Asynchronous postbacks are partial postbacks occured by Ajax (or any other 3rd party) update panels. Ajax has 5 stages to handle the life cycle of an asynchronous postback. They are:

1. initializeRequest
2. beginRequest
3. pageLoading
4. pageLoaded
5. endRequest

For our work, we need to handle the initializeRequest only. First make sure that your page contains ScriptManager, UpdatePanel, and UpdateProgress controls.

Now, if you want to keep the existing async postback alive, and kill all new async postbacks that are occuring, then add this script:

<script type="text/javascript">
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_initializeRequest(InitializeRequest);

function
InitializeRequest(sender, args) {
if (prm.get_isInAsyncPostBack()) {
args.set_cancel(true);
}
}
</script>

if you want to kill the existing async postback and start the new one, then replace the above function with this one:

function InitializeRequest(sender, args) {
if(prm.get_isInAsyncPostBack(){
prm.abortPostBack();
}
}



Monday, July 27, 2009

Read from DB while clientside validation using asp:CustomValidator (Bypass)

Ok, by saying bypass, I mean I didnt read data from SQL server table while the client side validation. I read it earlier, stored data in a hidden listbox, and used customvalidator to perform the client side validation . Here's how:

1. Declare a hidden list box that can be readable by javascript:

<asp:ListBox ID="uxHiddenListBoxForPermissionName" runat="server" Style="display:none">
</asp:ListBox>

Style="display:none" keeps the item hidden as well as readable by javascript. For my work I needed to add another hidden item, just to make sure that it is an update or add operation:

<asp:TextBox ID="uxTextBoxLog" runat="server" Style="display:none">
</asp:TextBox>

2. Populate data into the listbox from the code behind file:

Put this method in the code behind file (.cs):
private void GenerateHiddenListBox()
{
uxHiddenListBoxForPermissionName.Items.Clear();
ListItem item;
DataRowCollection rowCollection = Manager.GetManager().RoleManager.GetPermissions().Rows;
foreach (DataRow row in rowCollection)
{
item = new ListItem();
item.Value = row["Id"].ToString();
item.Text = row["Name"].ToString();
uxHiddenListBoxForPermissionName.Items.Add(item);
}
}

Manager.GetManager().RoleManager.GetPermissions() is the method that is used to read the table from DB. Replace it with yours. You should run this method each time there has been any change in the table (ex: new row added/ existing row modified/ deleted)

3. Add the customValidator to the field that you want to validate, along with other validators (if any):

<tr valign="top">
<td style="width: 20%">
<asp:Label ID="Label7" runat="server" Text="<%$Resources:Texts,Name %>"></asp:Label>
</td>
<td style="width: 40%">
<asp:TextBox ID="uxName" runat="server" MaxLength="50" Width="97%"></asp:TextBox>
</td>
<td style="width: 40%">

<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ErrorMessage="*"
ValidationGroup="PermissionValidation" ControlToValidate="uxName" SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>


<asp:CustomValidator runat="server" ID="uxCustomValidator" ClientValidationFunction="checkNames"
ControlToValidate="uxName" ValidationGroup="PermissionValidation" ErrorMessage="
<%$Resources:Texts,PermissionNameAlreadyExists %>" SetFocusOnError="True" Display="Dynamic">
</asp:CustomValidator>

</td>
</tr>

here I am validaing the uxName field. I'm using the asp:RequiredFieldValidator to check the field is nonempty, and using the asp:CustomValidator to check whether the entry already exists or not.

4. Write the client side function to perform the validation (this function is referred from asp:customvalidator):

<script type="text/javascript" >

function checkNames(source, args){
var permissionNameList= document.getElementById(uxPermissionListClientId);
var nameField= document.getElementById(uxNameClientId);
//alert(permissionNameList.length);
// alert(nameField.value);
var logText= document.getElementById(uxTextBoxLogClientId).value;
//alert(logText);
if(logText=='New'){ //I've set the value of textbox to 'New' while performing add operation
for(var i=0;i
<permissionNameList.length;i++){
//alert(permissionNameList.options[i].text);

if(permissionNameList.options[i].text==nameField.value){
// alert('validation is false, brother');
args.IsValid=false;
}
}
}
else{ //else I've set the value of the hidden text field to the current item to exclude it from comparing.
for(var i=0;i
<permissionNameList.length;i++){
//alert(permissionNameList.options[i].text);

if(permissionNameList.options[i].text==nameField.value&&nameField.value!=logText){
// alert('validation is false, brother');
args.IsValid=false;
}
}
}
}
</script>

uxPermissionListClientId is the client id for list box where data is stored reading from table, uxNameClientId is the client id of the textbox for which the validation is being performed, and uxTextBoxLogClientId is the client id of uxTextBoxLog, the hidden text box where I keep track whether an adding or updating event is going on. setting args.IsValid=false means there is an error, validation will not pass, and postback will not occure.

Tuesday, June 30, 2009

Drag and Drop UltraWebTree nodes in Mozilla Firefox using Infragistics Drag and Drop Framework

Infragistics dont support drag and drop tree nodes for UltraWebTree in Firefox. To go around this, you need to use Infragistics drag and drop Framework.

First, in your project/ website, add this reference: "Infragistics2.Web.v9.1" if Infra CLR 2.0 is installed. For CLR 3.5, you should add: "Infragistics35.Web.v9.1". Its a bit tricky to add it in websites. For websites, you may need to copy the dll into the deploy folder first. Then add the reference in the website.

Then, in your usercontrol, or page, or in master page (whichever applicable) add these assembly:

<asp:ScriptManager ID="ScriptManager1" runat="server" EnableScriptGlobalization="true"
AsyncPostBackTimeout="9600">
<Scripts>
<asp:ScriptReference Assembly="Infragistics2.Web.v9.1, Version=9.1.20091.2040, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" Name="Infragistics.Web.UI.SharedScripts.igDragDrop.js" />
<asp:ScriptReference Assembly="Infragistics2.Web.v9.1, Version=9.1.20091.2040, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb" Name="Infragistics.Web.UI.Scripts.5_igObjects.js" />
</Scripts>
</asp:ScriptManager>

You need to be careful about the CLR and version of the infragistics. Here, the version is 2040. But you can check it from your web.config, which version of infragistics is installed in your PC. Use your own version in the string.

In the page/ usercontrol, add this script:

<script type="text/javascript">
Sys.Application.add_load(app_loaded);

function app_loaded()
{
InitiateDragDropFrameWorkToNodes();
}
</script>

This must follow the script manager.

now add scripts in a separate javascript file, and reference it in the page/ usercontrol. I always prefer to use a separate javascript file, because it allows you to debug the script. Add this in the .js file:

var ddR = new $IG.DragDropBehavior();
var dropLoop=true;


function InitiateDragDropFrameWorkToNodes()
{
var treeInstance = igtree_getTreeById(UltraWebTreeClientID); //tree client id passed from pageload.


var firstlevel=treeInstance.getNodes();



//loops through all the first level nodes, usually there wont be more than 1 in our project

for(var i=0;i<firstlevel.length;i++)
{
var treeNode= firstlevel[i];
ddR.addSourceElement(treeNode.getElement());//this line was not needed, but if the root node is not included, then infra's built in drag drop starts, and gives different look.
ddR.addTargetElement(treeNode.getElement());
if(treeNode.hasChildren())
{
AssaignDragSource(treeNode);
}

}
if(ddR._events._handlers.Drop==null||ddR._events._handlers.Drop.length<1){ //this is the most important line, handler should be added only once and not again.
ddR.get_events().addDropHandler(drop);
}

}


function AssaignDragSource( treeNode)
{

var children=treeNode.getChildNodes();

for(var j=0;j<children.length;j++)
{

var element=children[j].getElement();
ddR.addSourceElement(element);
ddR.addTargetElement(element, true);

if(children[j].hasChildren())
{
AssaignDragSource(children[j]); //recursively call all children to add dragdropbehavior


}

}
}

function drop(sender, eventArgs) {
if ( dropLoop==true){

var treeInstance = igtree_getTreeById(UltraWebTreeClientID);
var source = eventArgs.get_manager().get_source().element;
var startNode=treeInstance.getNodeById(source.id);
var startNodes = startNode.getChildNodes();
var target= eventArgs.get_manager().get_target().element;
var endNode=treeInstance.getNodeById(target.id);
if(startNode==endNode){

return;
}

// alert('I am here');

var parentNode = endNode.getParent();
while (parentNode != null) {
if (parentNode == startNode) {
dropLoop=false;//whenever an alert is shown, it reenters the drop event even return is called, its fixed here
alert(msgCannotMoveParentUnderChild);
return;
}
parentNode = parentNode.getParent();
}

CopyNode(endNode, startNode);
//endNode.addChild(startNode.getText());
if (startNode.hasChildren()) {
addDroppedChildren(endNode.getChildNodes()[endNode.getChildNodes().length - 1], startNodes);
}


startNode.remove();


igtree_needPostBack(UltraWebTreeClientID);
var ts = igtree_treeState[UltraWebTreeClientID];
__doPostBack(ts.UniqueId, endNode.element.id + ":Update");
}
dropLoop=true; //allow to enter drop function after the alert message hastle is over.
}

function CopyNode(toNode, fromNode) {

newNode = toNode.addChild(fromNode.getText());
newNode.setTag(fromNode.getTag());
}

function addDroppedChildren(endNode, startNodes) {

for (var i = 0; i < startNodes.length; i++) {
CopyNode(endNode, startNodes[i]);
//endNode.addChild(startNodes[i].getText());
if (startNodes[i].hasChildren()) {
addDroppedChildren(endNode.getChildNodes()[endNode.getChildNodes().length - 1], startNodes[i].getChildNodes());
}
}

}

This will allow you to drag and drop tree nodes (including children nodes) in firefox. These two posts was really helpful with drag and drop framework:
Drop it Like its Windows
Introduction to the Infragistics Web Drag and Drop Framework

Wednesday, June 10, 2009

Use RegularExpressionValidator to validate a URL / web address

This is a nice RegularExpressionValidator to validate URLs:

<asp:RegularExpressionValidator ID="RegularExpressionValidator2" runat="server"
ControlToValidate="uxRssUrl" ErrorMessage="<%$ Resources:Texts, InvalidUrl %>"
ValidationExpression="^(ht|f)tp(s?)\:\/\/([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z]))*\.([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z]))*\.(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_]*)?$"
ValidationGroup="ResellerValidation"></asp:RegularExpressionValidator>">

This RegularExpressionValidator will check whether the content starts with:
(ftp:// ftps:// http:// https://) and before the next '/' comes whether there is at least 2 '.' in it.

This site is helpful for RegularExpressionValidator:
How To: Use Regular Expressions to Constrain Input in ASP.NET

Tuesday, June 2, 2009

How to check whether a variable has been declared from Javascript

Sometimes we declare javascript variables from serverside pageload method and use it in a common javascript file's method. The problem is, when that common javasript method is used by another serverside file, it might find that variable undefined. The best way to check is use typeof method of javascript. Consider the following method:

if(typeof(uxDocumentsOrImagesRadioButton)!= 'undefined'){
if(document.getElementById(uxDocumentsOrImagesRadioButton)!=null){

var thisradioGroup = document.getElementById(uxDocumentsOrImagesRadioButton);
if(thisradioGroup.disabled==true){
thisradioGroup.disabled=false;
}
}
}

uxDocumentsOrImagesRadioButton variable is declared from a page's pageload method. But if another page wants to use this method before that variable has been declared, then it will throw error. So, we need to check whether the variable has been declared previously.

Monday, May 11, 2009

Perform left outer join using LinQ

For my work, I had to perform a left outer join in LinQ. Here is the scenario:
There are 2 tables:










Auto text is the left table. Its customer column holds the Id for Customer table's elements. My task was, if there is a value in the Autotext.Customer field, then I have to show the relevant customer name. And if the value is null then it would show n\a. Only performing a left outer join can solve this. And this is how I solved it:

public IEnumerable GetAutotextsWithCustomerNameLeftJoin(int customerId)
{
try
{
IEnumerable AutotextsWithCustomerName = from AutotextTable in this.Context.AutoTexts
join CustomerTable in this.Context.Customers
on AutotextTable.Customer equals CustomerTable.Id into temporaryTable
from xTable in temporaryTable.DefaultIfEmpty()
select new { AutotextTable.Id, AutotextTable.Key, AutotextTable.Value, CustomerName = xTable.Id==null?"n\a":xTable.Name };
return AutotextsWithCustomerName;
}
catch (Exception ex)
{
return null;
}
}

Here, temporaryTable is the table that is created after the join operation is performed. xTable is the table that is created after left outer join is performed.

This solved the problem. I took help from different online posts. But, this post helped me the most:
LEFT OUTER JOIN in LINQ To SQL